山脉数组中查找目标值
给一个数组,元素大小先单调增再单调减,找一个目标数,如[1,4,9,32,50,100,21,7],找数字21,如果存在返回所在数组下标,不存在返回-1。
对应题目Leetcode:https://leetcode.cn/problems/find-in-mountain-array/description/
整体思路
- 根据二分找到最大节点
- 二分中间节点mid和mid+1比大小,可以确定mid右边处于增区间还是减区间
- 如果是增区间,最大值位置一定大于等于mid+1,二分的左指针移动到mid+1
- 如果是减区间,最大值位置一定小于等于mid,二分的右指针移动到mid
- 先在左侧二分找,找到直接返回
- 在右侧找然后返回
思考总结
类似在完全有序或分段区间内有序,理论上都可以借力二分解决。
如本题:山脉数组中查找目标值,另经典题:搜索旋转排序数组
代码实现
public int findInMountainArray(int target, MountainArray mountainArr) {
int l = 0, r = mountainArr.length() - 1;
// 找峰值点
while (l < r) {
int mid = (l + r) / 2;
if (mountainArr.get(mid) < mountainArr.get(mid + 1)) {
l = mid + 1;
} else {
r = mid;
}
}
// 在左侧找target
int res = bs(0, l, mountainArr, target, true);
// 如果没找到,在右侧找target
return res != -1 ? res : bs(l + 1, mountainArr.length() - 1, mountainArr, target, false);
}
// 二分查找
public int bs(int l, int r, MountainArray mountainArr, int target, boolean flag) {
while (l <= r) {
int mid = (l + r) / 2;
if (mountainArr.get(mid) == target) {
return mid;
}
if (flag) {
if (mountainArr.get(mid) > target) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (mountainArr.get(mid) > target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
数组处理
2、给一个数组,其中数字0移动到数组末尾,非0数字顺序不变,要求时间复杂度O(n),空间复杂度O(1),如[1,0,3,5,0,0,17,0,8,0] 处理为:[1,3,5,17,8,0,0,0,0,0]。
整体思路
双指针法,左指针指向第一个为0数字,右指针指向左指针后边第一个非0数字,交换,之后呢左右指针指向的和最初含义就反了,左右指针进行交换,右指针继续找下个非0数字。
代码实现
public static void fun(int[] arr) {
int l = 0, r = 0;
while (r < arr.length) {
while (l < arr.length && arr[l] != 0) {
l++;
}
if (l == arr.length) {
break;
}
r = Math.max(r, l + 1);
while (r < arr.length && arr[r] == 0) {
r++;
}
if (r == arr.length) {
break;
}
// 交换左右指针指向的元素
int t = arr[r];
arr[r++] = arr[l];
arr[l++] = t;
}
}