704. 二分查找
这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件。
思路就不多说了
实现有点小问题,测试样例过了,运行存在索引越界。
难点:理清区间关系
class Solution {
public int search(int[] nums, int target) {
return binarySearch(nums, target, 0, nums.length);
}
public int binarySearch(int[] nums, int target, int left, int right) {
if (left > right) return -1;
int mid = (left+right)/2;
if (nums[mid] == target) return mid;
if (target < nums[mid]) {
return binarySearch(nums, target, left, mid-1);
}
if (target > nums[mid]) {
return binarySearch(nums, target, mid+1, right);
}
return -1;
}
}
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 6
at line 9, Solution.binarySearch
at line 14, Solution.binarySearch
at line 14, Solution.binarySearch
at line 3, Solution.search
at line 54, __DriverSolution__.__helper__
at line 87, __Driver__.main
这题虽然基础,但也要认真对待~~
首先明确区间,是左闭右闭,还是左闭右开?
这很关键!不然容易混淆。
希望挑一种养成习惯,那么另一种仅需稍加修改就能做出。
左闭右开还是左闭右闭?
首先来看,左闭右开的情况。
上面提交的代码存在越界,这是边界条件没处理好
例如:
nums=[1,2,3,4],target=5
当做到left=4,right=4时,mid=4
nums[mid]就会报越界异常
因此要单独加上条件判断,处理这种情况。
class Solution {
public int search(int[] nums, int target) {
return binarySearch(nums, target, 0, nums.length); //区间左闭右开
}
public int binarySearch(int[] nums, int target, int left, int right) {
if (left > right || left== nums.length) return -1;
int mid = (left+right)/2;
if (nums[mid] == target) {
return mid;
}else if (target < nums[mid]) {
return binarySearch(nums, target, left, mid-1);
}else if (target > nums[mid]) {
return binarySearch(nums, target, mid+1, right);
}
return -1;
}
}
//非递归版本
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid;
}
return -1;
}
}
再来看左闭右闭的情况就比较简单了。
class Solution {
public int search(int[] nums, int target) {
return binarySearch(nums, target, 0, nums.length-1); //区间左闭右闭
}
public int binarySearch(int[] nums, int target, int left, int right) {
if (left > right) return -1;
int mid = (left+right)/2;
if (nums[mid] == target) {
return mid;
}else if (target < nums[mid]) {
return binarySearch(nums, target, left, mid-1);
}else if (target > nums[mid]) {
return binarySearch(nums, target, mid+1, right);
}
return -1;
}
}
//非递归版本
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
}
根据上面说的,要明确使用区间的习惯,从现在开始,我默认使用左闭右闭的方式。
27. 移除元素
题目链接
必须仅使用 O(1) 额外空间并原地修改输入数组。
思路:使用一个添加保留数组元素的哨兵,一旦后面有需要保留的元素,就在此进行添加,然后哨兵后移一位。
class Solution {
public int removeElement(int[] nums, int val) {
int addIndex = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) {
nums[addIndex++] = nums[i];
}
}
return addIndex;
}
}