二分查找的应用条件:数组有序且无重复元素。
其中最关键的就是二分区间的定义,一般是左闭右开和左闭右闭这两种情况。不同定义区间还会影响到右边界的定义、循环条件以及边界条件。
整个循环中需坚持循环不变量的原则,也就是二分区间的定义不变,这样就能轻松解题了!
思路如下:
1.定义左右边界,此时二分区间定义左闭右闭
2.循环条件为left <= right,因为此时区间可取到left = right
3.更新区间时考虑到区间定义,如targe < nums[mid],此时更新左区间的右边界,注意此时target是小于中间值的,因此一定无法取到nums[mid],且该二分区间为左闭右闭。因此更新右边界需 right = mid - 1;其他部分如上
class Solution1 {
//左闭右闭版本 二分区间
public int search01(int[] nums, int target) {
int left = 0;//左边界
int right = nums.length - 1;//右边界,此时可以取到最右
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] > target) {
//更新左区间的右边界
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}else {
//等于
return mid;
}
}
//循环完毕都没有找到
return -1;
}
左闭右开版本
注意:逻辑与左闭右闭基本一致,只需考虑到二分区间右边为开区间所带来的影响即可
//左闭右开 区间
public int search02(int[] nums, int target) {
int left = 0;//左边界
int right = nums.length;//右边界,此时小于数组长度,正好取到右边界
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > target) {
//更新左区间的右边界
right = mid ;
}else if (nums[mid] < target) {
left = mid + 1;
}else {
//等于
return mid;
}
}
//循环完毕都没有找到
return -1;
}
}
该题目考虑两个解法:暴力解法和双指针解法
暴力解法:
1.外层for循环遍历整个数组,寻找目标值
2.内层for循环将目标值后面的元素向前移动一位
注意:每次找到目标值需将外层for循环定义变量向前移动一位,以遍历所更新元素
class Solution4 {
public int removeElement(int[] nums, int val) {
int n = nums.length;
for (int i = 0; i < n; i++) {
if (nums[i] == val) {
for (int j = i; j < n - 1; j++) {
nums[j] = nums[j + 1];
n--;//表示
i--;
}
}
}
return n;
}
}
双指针解法
思路:
1.定义快慢指针;快指针负责寻找所需更新元素,慢指针定位更新索引
2.如果找到所需更新元素,通过快指针赋值给慢指针即可。
3.最后返回慢指针(慢指针表示所更新元素数量,也就是新数组的长度)
class Solution5 {
public int removeElement(int[] nums, int val) {
int n = nums.length;
int slow = 0;
//遍历整个数组
for (int fast = 0; fast < n; fast++) {
//找到需更新元素
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
}