704.二分查找
题目链接:https://leetcode.cn/problems/binary-search/
题目要求:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入:nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入:nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
总结:1.首先二分查找的条件是满足数组是有序且不重复的。2.区间是全闭还是左闭右开,定好后要保持不变,left < right,还是left<=right,包括后面缩小空间时,是right = mid 还是 right = mid - 1,都要根据区间的选择来看。3.同时要注意给的target值小于有序数组最左边,大于有序数组最右边的情况。4.当算mid 位置时,正常是(left+right)/2,但是可能会存在溢出,故用left+((right-left)/2)代替 ,同时可进一步优化为位运算,右移1位,代表/2操作。变为left+((right-left) >> 1)。
区间全闭:
class Solution {
public int search(int[] nums, int target) {
if(target < nums[0] || target > nums[nums.length - 1]){
return -1;
}
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = left + ((right - left) >> 1);
if(nums[mid] == target){
return mid;
}
if(nums[mid] > target){
right = mid - 1;
}
if(nums[mid] < target){
left = mid + 1;
}
}
return -1;
}
}
此种情况,left = right时存在意义,故 left <=right ,当mid值大于target值时,已经知道mid值肯定不是target,故right = mid -1
区间为左闭右开:
class Solution {
//左闭右开区间
public int search(int[] nums,int target){
//考虑target值不在有序数组的区间内的情况
if(nums[0] > target || nums[nums.length-1] < target){
return -1;
}
int left = 0;
int right = nums.length;//左闭右开
//left=right此时区间没有意义
while(left < right){
int mid = left + ((right - left)/2);
if(target < nums[mid]){
right = mid;
}
else if(target > nums[mid]){
left = mid + 1;
}
else if(target == nums[mid] ){
return mid;
}
}
return -1;
}
}
注意此时right指针初始定义为nums.length,该指针指向数组最右元素的右边一个位置,故右为开区间,当left = right时已经无意义,故为left < right,当nums[mid] > target时,mid已经确定不是了,故此时right = mid,因为右为开区间,正好把mid排除在外。
27. 移除元素
题目链接:https://leetcode.cn/problems/remove-element/
题目要求:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
总结:本题目可以使用暴力的两层for循环得出结果,一层循环遍历数组,再一层循环负责删除元素后移动后面其余元素。注意i--是因为当删除一个元素后,所有元素的索引都向前提了一位,故需要让遍历整个数组的i向前移一位,防止漏掉最左边的那一个元素。
双指针法,快指针用来遍历数组,慢指针用来确定不含目标值的“新数组”的最后一位。当遇到不为目标值时,两个指针一起向后走,当遇到为目标值时,慢指针不走,快指针继续向后遍历,然后用快指针指向的值覆盖慢指针此时指向的目标值元素。
暴力法:
class Solution {
//暴力法解决,两层for循环
public int removeElement(int[] nums,int val){
int len = nums.length;
for(int i = 0;i < len;i++){
if(nums[i] == val){
for(int j = i + 1;j < len;j++){
nums[j-1] = nums[j];
}
i--;
len--;
}
}
return len;
}
}
双指针法:
class Solution {
//双指针法,快慢指针
public int removeElement(int[] nums,int val){
int slowindex = 0;
for(int fastindex = 0;fastindex < nums.length;fastindex++){
if(nums[fastindex] != val){
nums[slowindex] = nums[fastindex];
slowindex++;
}
}
return slowindex;
}
}