目录
1、数组理论基础
2、二分查找
2.1 区间左闭右闭写法
2.2 区间左闭右开写法
3、移除元素
3.1 暴力解法
3.2 双指针(快慢指针)法
1、数组理论基础
参考以前的博客:http://t.csdn.cn/HAVSF
2、二分查找
力扣https://leetcode.cn/problems/binary-search/
【题目】给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
【参考代码】
int search(int* nums, int numsSize, int target){ int left = 0; int right = numsSize - 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; }
首先关于这个二分查找法,主要有两个易错点,一个是while循环条件里面的符号究竟是小于等于还是小于。另一个是if—else if—else里面的判断体究竟需不需要进行加一减一的操作。
要解决这个问题还是从问题本身出发,那就是没有搞清楚区间的定义,而定义过的区间就是固定的,即“不变量”。每一次循环里面的处理都需要根据区间来选择,这也是为什么有时候有些题加一减一似乎对其没什么影响,但有时候又必须加一或者减一。
下面给出二分法写法的两个版本:
2.1 区间左闭右闭写法
int search(int* nums, int numsSize, int target){ int left = 0; int right = numsSize - 1; while(left<=right) { int mid = left+(right-left)/2; //int mid = (left+right)/2; //int mid = left+(right-left)>>1; if(nums[mid]>target) { right = mid-1; } else if(nums[mid]<target) { left = mid+1; } else { return mid; } } return -1; }
在这种情况下,target是定义在[left,right]上的,此时left=right是有意义的,注意这里跟数学里面的区间定义不同,因为有意义,所以此时while(left<=right),如果你少写=,会有值被你漏算。除此之外,你还需要注意一下if判断体里面是否需要加一或减一,如果nums[mid]>target,说明当前的nums[mid]一定不是target,所以更新后的搜索右下标范围为mid-1。
2.2 区间左闭右开写法
int search(int* nums, int numsSize, int target){ int left = 0; int right = numsSize; 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; }
在这种情况下,target是定义在[left,right)上的,此时left=right是没有意义的,因为没有意义,所以此时while(left<right)。除此之外,你还需要注意一下if判断体里面是否需要加一或减一,如果nums[mid]>target,说明当前的nums[mid]一定不是target,而右区间本就是开区间,所以更新后的搜索右下标范围为mid。但nums[mid]<target,而左区间是闭的,所以坐下标需要进行加一。
【相关题目训练】
1.力扣
https://leetcode.cn/problems/search-insert-position/
2.力扣https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/3.力扣
https://leetcode.cn/problems/sqrtx/4.力扣
https://leetcode.cn/problems/valid-perfect-square/
3、移除元素
力扣https://leetcode.cn/problems/remove-element/
【题目】给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
【思路】要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
以下给出两个方法:
3.1 暴力解法
//两个for循环,一个for遍历整个数组元素,另一个更新数组元素 int removeElement(int* nums, int numsSize, int val){ for(int i=0;i<numsSize;i++) { if(nums[i]==val) //出现目标元素,就将数组整体向前移一步。 { for(int j=i+1;j<numsSize;j++) { nums[j-1] = nums[j]; } i--; //因为下标i后面的都向前移了一位,所以i也需要向前移一位。 numsSize--; //此时数组长度-1 } } return numsSize; }
在本代码中:
- 时间复杂度:
- 空间复杂度:
3.2 双指针(快慢指针)法
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针:
- 寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 指向更新后新数组下标的位置
int removeElement(int* nums, int numsSize, int val){ int slow = 0; for(int fast =0;fast<numsSize;fast++) { if(val!=nums[fast]) { nums[slow++]=nums[fast]; } } return slow; }
在本代码中:
- 时间复杂度:
- 空间复杂度:
【本题小结】显然法二明显优于法一,双指针法在数组和链表的操作中是很常见的,很多数组链表OJ题都可以使用双指针法解决。
【相关题目训练】
力扣
https://leetcode.cn/problems/remove-duplicates-from-sorted-array/
力扣
https://leetcode.cn/problems/move-zeroes/
力扣
https://leetcode.cn/problems/backspace-string-compare/
力扣
https://leetcode.cn/problems/squares-of-a-sorted-array/