OD高频面试题:手撕代码(一)

题目描述

给你长度相等的两个字符串 s1 和 s2 。一次 字符串交换 操作的步骤如下:选出某个字符串中的两个

下标(不必不同),并交换这两个下标所对应的字符。

如果对 其中一个字符串 执行 最多一次字符串交换 就可以使两个字符串相等,返回 true ;否则,返

回 false 。

示例 1:

输入:s1 = "bank", s2 = "kanb"

输出:true

解释:例如,交换 s2 中的第一个和最后一个字符可以得到 "bank"

示例 2:

输入:s1 = "attack", s2 = "defend"

输出:false

解释:一次字符串交换无法使两个字符串相等

示例 3:

输入:s1 = "kelb", s2 = "kelb"

输出:true

解释:两个字符串已经相等,所以不需要进行字符串交换

示例 4:

输入:s1 = "abcd", s2 = "dcba"

输出:false

提示:

1 <= s1.length, s2.length <= 100

s1.length == s2.length

s1 和 s2 仅由小写英文字母组成

/\*

题目理解:在某一个字符串中去交换两个字符后,是否可以和另外一个字符串相同

思路:遍历s1和s2,用find记录同位置字符不同出现的次数

找到第一处不同也就是find = 1时,记录不同的两个字符s1char和s2char

找到第二处不同也就是find = 2时,比较s1当前字符与s2char,s1char与s2当前字符是否相等

不相等则证明无法通过交换s1当前字符与s1char来满足s1 = s2,返回false

找到第三处不同也就是find = 3时,无法通过一次交换满足s1 = s2,返回false

遍历结束后,如果find = 1,则依然无法通过一次交换满足s1 = s2,返回false

\*/

public static boolean areAlmostEqual(String s1, String s2) {

int find = 0;//记录同位置字符不同出现的次数

char ch1 = ' ';

char ch2 = ' ';

for (int i = 0; i < s1.length(); i++) {

if (s1.charAt(i) != s2.charAt(i)) {

find++;//同位置字符不同出现的次数+1

if (find == 3) {//已经有3处不同,就说明不可能通过交换一次实现连个字

符串相等

173.2. LCR 037. 行星碰撞

PS:面试官对原题做了修改,请注意对比不同。

题目描述

代码实现:

return false;

} else if (find == 2) {//两处不同的话,还要看保存的字符能否通过交换

后得到相同的字符串

if (s1.charAt(i) != ch2 || s2.charAt(i) != ch1) {//无法通

过交换满足s1 = s2

return false;

}

} else {//第一次发现不同

ch1 = s1.charAt(i);

ch2 = s2.charAt(i);

}

}

}

return find != 1;//只有一对不同时无法通过交换满足s1 = s2

}

public static void main(String\[] args) {

String s1 = "bank";

String s2 = "kanb";

System.out.println(areAlmostEqual(s1,s2));

}

行星碰撞

PS:面试官对原题做了修改,请注意对比不同

题目描述

给定一个整数数组planets,表示在同一行的行星。

对于数组中的每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向(正表示向左移动,负表

示向右移动)。每一颗行星以相同的速度移动。

找出碰撞后身下的所有行星。碰撞规则:两个行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相

同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。

示例1:

输入:planets = [-5,-10,5]

输出:[-5,-10]

解释:-10和5进行碰撞后只剩下-10,-5和-10永远不会发生碰撞

示例2:

输入:planets = [-8,8]

输出:[]

解释:-8和8进行碰撞后,全部行星都爆炸了。剩下为空。

public static void main(String[] args) {
int[] plants = {-8,8};
int[] res = getRes(plants);
for (int item : res) {
System.out.println(item);
}
}
//思路:用栈的思想去实现:
//负数直接入栈,用正数去和栈顶元素抵消(相同才能抵消,不同的时候留下值大的)
//抵消后还是整数的话,继续和栈顶元素进行抵消,直到不能抵消为止。
//最后栈中剩余的元素就是想要的结果
//举例:-5, -10, 5
// -5 先入栈 -10来了后也直接入栈 5来了以后和-10比较,留下-10,所以最后结果是
[-5,-10]
//1、栈中没有元素的时候,小于0的直接将值入栈
//2、遇到正数且栈中有元素的时候,当前正数与栈顶元素进行比较,如果正数大于负数的相反值
则碰撞未完全抵消
//那么继续循环去抵消 若完全抵消 则循环终止 每抵消一个 出栈一个元素 若flag扔为true
代表当前是负数
// 则当前遍历入栈
public static int[] getRes(int[] planets) {
Stack<Integer> stack = new Stack<>();
for (int planet : planets) {
boolean flag = true;//是否要添加到栈中
while (flag && planet > 0 && !stack.isEmpty() && stack.peek() <
0) {//元素为正数且栈不为空,且栈顶为负数
flag = stack.peek() > -planet; //栈顶与遍历值的相反数是否可以碰撞
if (stack.peek() >= -planet) { // 栈顶小行星爆炸,也就是要弹栈
stack.pop();
}
}
if (flag) {
stack.push(planet);
}
}
int size = stack.size();
int[] ans = new int[size];
for (int i = size - 1; i >= 0; i--) {
ans[i] = stack.pop();
}
return ans;
}

盛最多水的容器

题目描述

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i,

height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]

输出:49

解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色

部分)的最大值为 49。

示例 2:

输入:height = [1,1]

输出:1

//思路:使用双指针算法,初始时左指针指向0位置,右指针指向最后的位置
//不断移动左右指针向中间移动获取左右指针上的最小的高度,以便获得该窗口的面积
//当左指针小于右指针的时候进入循环:
//1、right-left :就是x轴的长度 左右指针上选择一个较小的作为高度
// 计算出面积后 更新最大值maxArea
//2、左指针上的值小于右指针的值,左指针后移(也就是left++)
//3、左指针上的值大于等于右指针的值,右指针前移(也就是right--)
public static int getMaxCapacity(int[] height) {
int maxArea = 0;
int left = 0;
int right = height.length - 1;
while (left < right) {
int area = (right - left) *
Math.min(height[left], height[right]);
maxArea = Math.max(maxArea, area);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxArea;
}
public static void main(String[] args) {
int[] height = {1, 8, 6, 2, 5, 4, 8, 3, 7};
System.out.println(getMaxCapacity(height));
}

下一个更大元素||

题目描述

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums

中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应

该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

示例 1:

输入: nums = [1,2,1]

输出: [2,-1,2]

解释: 第一个 1 的下一个更大的数是 2;

数字 2 找不到下一个更大的数;

第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

示例 2:

输入: nums = [1,2,3,4,3]

输出: [2,3,4,-1,4]

提示:

1 <= nums.length <= 104

-109 <= nums[i] <= 109

//1、暴力解法(候选人年限低的时候可以用这个版本)
public static int[] findNextEle(int[] nums) {//格式化你的代码
int length = nums.length; //获取原数组长度
int[] res = new int[length]; //存储结果数据
for (int i = 0; i < length; i++) { //遍历字符串
boolean flag = false; //设计一个标志位 用来判断是否能找到下一个更大值
for (int j = i + 1; j < i + length; j++) { //从当前数字开始遍历后面
的length - 1个元素
int num = nums[j % length]; //若j越界 则获取j%length位置元素 相
当于从0继续遍历 保证不越界
if (num > nums[i]) { //若内层循环遍历元素大于外层循环遍历元素 则说
明找到了
res[i] = num; //将当前值记录到res数组对应位置
flag = true; //更改标记变量为true
break; //因为只找第一个大于的 所以这里结束循环
}
}
if (!flag) { //若flag标记为仍然为false 则说明没找到下一个更大的
res[i] = -1; //则将当前下标对应元素存为-1
}
}
return res; //返回结果数组
}
public static void main(String[] args) {
int []nums = {4,1,3,5};
int []res = findNextEle(nums);
for (int i = 0; i < res.length; ++i){
System.out.print(res[i] + " ");
}
}
//单调栈
public static int[] findNextEle(int[] nums) {
int[] res = new int[nums.length];
Arrays.fill(res, -1);//默认所有数组为-1
Stack<Integer> st = new Stack<>();//使用单调栈来解决,注意单调栈中存放的是
下标
for (int i = 0; i < nums.length * 2; i++) {//因为是循环数组,所以这里循环
2次
//i % nums.length :取余的目的是为了循环数组2次
//当元素大于栈顶元素时,持续出栈并更新最大值
while (!st.empty() && nums[i % nums.length] > nums[st.peek()]) {
res[st.pop()] = nums[i % nums.length];
}
st.push(i % nums.length);//每次将当前元素入栈
}
return res;
}
public static void main(String[] args) {
int[] nums = {4, 1, 3, 5};
int[] res = findNextEle(nums);
for (int i = 0; i < res.length; ++i) {
System.out.print(res[i] + " ");
}
}

一亿以内的素数

public static void main(String[] args) {
int n = 100000000;
System.out.println(getCount(n));
}
//思路很简单:就是把数字的倍数全部都判断一下是否为false
//就不用再判断倍数是不是素数了,能提升效率
public static int getCount(int n) {
boolean[] flag = new boolean[n + 1];
int count = 0;
for (int i = 2; i <= n; i++) {
if (!flag[i]) {
count++;
} else {
continue;
}
for (int j = 2; i * j <= n; j++) {
flag[i * j] = true;
}
}
return count;
}

小岛问题(翻转矩阵)

题目描述:

给定一个N行M列的二维矩阵,矩阵中每个位置的数字取值为0或1,矩阵示例如:

1 1 0 0

0 0 0 1

0 0 1 1

1 1 1 1

现需要将矩阵中所有的1进行翻转为0,规则如下:

1)当点击一个1时,该1被翻转为0,同时相邻的上、下、左、右,以及左上、左下、右上、右下8个方

向的1(如果存在1)均会自动反转为0;

2)进一步地,一个位置上的1被反转为0时,与其相邻的8个方向的1(如果存在1)均会自动反转为0;

按照上述规则示例中的矩阵只最少需要点击2次后,所有均值0。请问,给定一个矩阵,最少需要点击几

次后,所有数字均为0

输出

输出一个整数,表示最少需要点击的次数

示例1

输入

3 3

1 0 1

0 1 0

1 0 1

输出

1

说明:上述4个角的1均在中间位置1的相邻8个方向上,因此点击一次即可。

public static void main(String[] args) {
// int[][] nums = {{1,1,0,0},{0,0,0,1},{0,0,1,1},{1,1,1,1}};
// int[][] nums = {{1,0,1},{0,1,0},{1,0,1}};
int[][] nums = {{1,0,1},{0,1,0},{1,0,1}};
int cnt = 0;
for (int i = 0; i < nums.length; ++i){
for (int j = 0; j < nums[0].length; j++) {
if(nums[i][j] == 1){ //遍历矩阵 如果遇到1 就将1相邻的八个方向都改为
0 那么进入该if几次
dfs(nums, i, j); //就相当于有几个小岛
cnt++;
}
}
}
System.out.print(cnt);
}
private static void dfs(int[][] nums, int i, int j) {
if(i < 0 || j < 0 || i >= nums.length || j >= nums[0].length){
return ; //递归可以走八个方向 那么i j下标有可能越界 越界了 就结束当前递归
}
if(nums[i][j] == 0){
return ; //若当前位置值为0 则结束递归 不继续翻转
}
nums[i][j] = 0; //若能走到这里 代表当前值为1 那么将其翻转为0 防止重复走回头路
int [][]run = {{0, 1},{0, -1},{1, 0},{-1, 0},{1, 1},{-1, -1},{-1,
1},{1, -1}};
//用数组存储八个相邻方向的差值 比如 0 1 就是向右 0 -1 就是向左 1, 0 就是向
下 1 1 就是右下方
for (int m = 0; m < run.length; ++m){
for (int n = 0; n < 2; n++) { //循环遍历数组的每一个方向 继续递归调用
传播翻转
dfs(nums, i + run[m][0], j +run[m][1]);
}
}
}

OJ红绿砖

题目描述

在美丽的尧山,有一个大广场,50周年校庆的时候Solo就在大广场上见证了史上最壮观的焰火。

在广场上有一排方砖是有颜色的,被涂上红色或者绿色,从左到右排列。

现在校方要求重新喷涂颜色,但不一定要每一块方砖都重新喷涂,

因为校方的目的是:每一块红色的方砖都至少在绿色方砖的左边(也就是每一个红的左边不能有绿的),

并且尽量喷涂最少的次数。

解答要求

时间限制:1000ms, 内存限制:64MB

输入

输入只有一行,包含一个字符串S,且只包含’R’(代表红色)或者’G’(代表绿色)。

我们保证字符串S的长度L的范围是(0 < L < 50 )。

输出

输出需要重新喷涂的方砖的最少数量。

样例

输入样例 RGRGR

输出 2

/*根据题目描述,我们需要计算重新喷涂的方砖的最少数量。为了满足校方的要求,即每一块红色的方
砖都至少在绿色方砖的左边,我们可以采用贪心算法的思路来解决。具体的解题思路如下:
初始化一个变量 count 用于记录重新喷涂的方砖的次数,初始值为0。
遍历字符串 S 中的每一个字符。
如果当前字符是红色方砖('R'),则判断该红色方砖是否满足要求,即其左边没有绿色方砖('G')。
如果满足要求,则继续遍历下一个字符;如果不满足要求,则将该红色方砖重新喷涂为绿色方砖,并将
count 值加1。
如果当前字符是绿色方砖('G'),则无需操作,继续遍历下一个字符。
完成遍历后,输出 count 的值,即重新喷涂的方砖的最少数量。
以下是相应的 Java 代码实现:*/
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String S = scanner.nextLine();
int count = 0;
for (int i = 0; i < S.length(); i++) {
if (S.charAt(i) == 'R') {
if (i == 0 || S.charAt(i - 1) != 'G') {
count++;
}
}
}
System.out.println(count);
}
}
这段代码根据输入的字符串 S 进行遍历,根据当前字符和前一个字符的关系来判断是否需要重新喷涂方
砖,并计算重新喷涂的次数。最后输出结果即为重新喷涂的方砖的最少数量。
void LRedRGreen()
{
string colors;
cin >> colors;
int size = colors.size();
int* sprayNum = new int[size + 1]();
int left = 0;
int right = size - 1;
while (left <= right) {
if (colors[left] == 'R') {
for (int i = 0; i <= left; i++) {
sprayNum[i]++;
}
} else {
for (int i = left + 1; i <= size; i++) {
sprayNum[i]++;
}
}
if (left != right) {
if (colors[right] == 'R') {
for (int i = 0; i <= right; i++) {
sprayNum[i]++;}
} else {
for (int i = right + 1; i <= size; i++) {
sprayNum[i]++;
}
}
}
left++;
right--;
}
int min = sprayNum[0];
for (int i = 0; i < size + 1; i++) {
min = min < sprayNum[i] ? min : sprayNum[i];
}
cout << min;
delete[] sprayNum;
}

用户分组

题目描述

有 n 个人被分成数量未知的组。每个人都被标记为一个从 0 到 n - 1 的唯一ID 。

给定一个整数数组 groupSizes ,其中 groupSizes[i] 是第 i 个人所在的组的大小。例如,如

果 groupSizes[1] = 3 ,则第 1 个人必须位于大小为 3 的组中。

返回一个组列表,使每个人 i 都在一个大小为 groupSizes[i] 的组中。

每个人应该 恰好只 出现在 一个组 中,并且每个人必须在一个组中。如果有多个答案,返回其中 任何

一个。可以 保证 给定输入 至少有一个 有效的解。

示例 1:

输入:groupSizes = [3,3,3,3,3,1,3]

输出:[[5],[0,1,2],[3,4,6]]

解释:

第一组是 [5],大小为 1,groupSizes[5] = 1。

第二组是 [0,1,2],大小为 3,groupSizes[0] = groupSizes[1] = groupSizes[2] = 3。

第三组是 [3,4,6],大小为 3,groupSizes[3] = groupSizes[4] = groupSizes[6] = 3。

其他可能的解决方案有 [[2,1,6],[5],[0,4,3]] 和 [[5],[0,6,2],[4,3,1]]。

示例 2:

输入:groupSizes = [2,1,3,3,3,2]

输出:[[1],[0,5],[2,3,4]]

提示:

groupSizes.length == n

1 <= n <= 500

1 <= groupSizes[i] <= n

//这个题的意思就是:把数组中的每个元素都按照元素的值分到不同的组中
//然后把下表放入数组中返回
//例如: 3,3,3,3,3,1,3
// 第一个3:map 存 3 1
// 第二个3:map 存 3 2
// 第三个3:map 存 3 3 因为到这里分组满了 所以分组有一个[0,1,2]
// 第四个3:map 存 3 1
// 第五个3:map 存 3 2
// 第六个1:map 再存一个 1 1
// 第七个3:map 中的3 2 变成了 3 3 分组满了,所以有一个分组[3,4,6]
//最后将 1 1这个map也放入是一个分组 [5]
//所以最终结果是 [0,1,2] [3,4,6] [5]
public static List<List<Integer>> group(int[] groupSizes) {
//创建一个hash表,键为 组的人数groupSizes[i],值为 这一组的人的编号i的集合。
Map<Integer, List<Integer>> hash = new HashMap<>();
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < groupSizes.length; i++) {
int x = groupSizes[i];
//看看这个索引的数组存在不
if (hash.get(x) == null) {
hash.put(x, new ArrayList<>());
}
//将这个人的编号 i 放在 groupSizes[i] 这个索引的数组上
hash.get(x).add(i);
//链子满了,上传结果,清空这个数组
if (hash.get(x).size() == x) {
//满一组,就放进返回值容器
res.add(hash.get(x));
//将 groupSizes[i] 这个索引的数组清空,方便来存下一组
hash.put(x, null);
}
}
return res;
}
public static void main(String[] args) {
int []groupSizes = {2,1,3,3,3,2};
List<List<Integer>> groups = group(groupSizes);
System.out.println(groups);
}

产生由1,2,3这3个数字符号所构成、长度为n的字符串

编写程序,产生由1,2,3这3个数字符号所构成、长度为n的字符串,并且在字符串中对于任何一个子

串而言,都不会有相邻的、完全相同的子串;

解答要求

时间限制:1000ms, 内存限制:64MB

输入

字符串长度n(1=<n<=10);

输出

无相邻重复子串的所有字符串,每个字符串换行输出。(由小到大输出)

样例

输入样例 1

5

输出样例 1

12131

12132

12312

12313

12321

13121

13123

13212

13213

13231

21231

21232

21312

21321

21323

23121

23123

23132

23212

23213

31213

31231

31232

31321

31323

32123

32131

32132

32312

32313

//结果不是很对,需要确认代码是否正确
public static List<Character> ans;
public static void main(String[] args) {
int n = 5;
ans = new ArrayList<>();
dfs(n, 0);
}
//n:几位数字 curCount:当前已经存入的元素个数
private static void dfs(int n, int curCount) {
if (n == curCount) {
StringBuilder sb = new StringBuilder();
for (Character an : ans) {
sb.append(an);
}
if (sb.indexOf("1") != -1 && sb.indexOf("2") != -1 &&
sb.indexOf("3") != -1) {
System.out.println(sb);
}
return;
}
for (int i = 0; i < 3; i++) {
char ch = (char) ('0' + i + 1);
if (ans.size() != 0 && ans.get(ans.size() - 1) == ch) {
continue;
}
ans.add(ch);
dfs(n, curCount + 1);
ans.remove(ans.size() - 1);
}
}

删除有序数组中的重复项||

题目描述

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次

,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

public static int removeDuplicates(int[] nums) {
int count = 1;
int cur = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[cur] == nums[i]) {
if (count < 2) {
nums[++cur] = nums[i];
count++;
}
}else {
nums[++cur] = nums[i];
count = 1;
}
}
return cur + 1;
}
public static void main(String[] args) {
int[] nums = {0, 0, 1, 1, 1, 1, 2, 3, 3};
System.out.println(removeDuplicates(nums));
}

全部评论

相关推荐

1 3 评论
分享
牛客网
牛客企业服务