经典数据结构题目-数组
704. 二分查找
-
解决思路
- 基于数组有序的特性,取其中一个值进行比较,即可淘汰该值左边或右边的元素,缩小搜索的区间
- 使用两指针标记需要遍历的区间,取中间值进行比较,淘汰左边或右边元素,不断移动缩小遍历的区间,即可查到
-
代码
public int search(int[] nums, int target) { int l = 0; int r = nums.length - 1; while(l <= r){ // 注意点一 int mid = l + (r-l)/2; if(nums[mid] > target){ r = mid - 1; // 注意点二 }else if(nums[mid] < target){ l = mid + 1; }else{ return mid; } } return -1; }
-
注意点
- 核心注意点:避免漏检元素
- 注意点一:while条件中选择 l <= r 还是 l < r ,取决于 r 的取值;当 r = num.length时,l 不能 <= r,可能会溢出
- 注意点二:当选择 l < r 的判断时,while中每次搜索的区间为 [l,r) 。当num[mid] > target时,r = mid,不能mid-1。因为r所指向的元素在进入第二次循环时,是不会再与target比较,会导致漏检
- 时间复杂度 O(logN) 。总共需要遍历 log2N次,忽略常数2。
-
扩展
-
当元素可重复时,如何定位到与target相等的最小索引
-
public static int search(int[] nums, int target) { int l = 0; int r = nums.length - 1; while(l <= r){ int mid = l + (r-l)/2; if(nums[mid] >= target){ // 等于target的时候 右指针继续移动,继续寻找最左边的一个 // 如果此时右指针已达最左的一个,再循环左指针会移动,最终会大于r,取到最左的 r = mid - 1; }else if(nums[mid] < target){ l = mid + 1; } } // 会存在找不到与target相等的情况 if(nums.length == l || nums[l] != target){ return -1; } return l; }
-
80. 删除有序数组中的重复项 II
-
解决思路
- 使用快慢指针遍历,快指针用于判断是否与上一个元素重复,慢指针用于记录下最终有效的数字
- 快指针判断为不重复,慢指针记下来,同时向前移动一位
-
代码
public int removeDuplicates(int[] nums) { // 只允许元素出现一次的情况 int k = 1; int fast = k; int slow = k; // 注意点一 while(fast < nums.length){ if(nums[fast] != nums[fast-k]){ // 注意点二 nums[slow] = nums[fast]; slow++; } fast ++; } return slow; }
-
注意点
- 核心注意点:理清快慢指针分别的作用
- 注意点一:快慢指针的起始位置,k <= nums.length时,可以初始化快慢指针在k的位置开始遍历
- 注意点二:判断元素是否超过k个重复,因为数组有序,如果当前元素等于前k个位置的元素,说明超过了
-
同类型题目
977. 有序数组的平方
-
解决思路
- 非递减顺序。即递增但可以重复
- 使用双向指针,比较两指针指向元素的绝对值,绝对值大的计算平方添加进结果数组
-
代码
public int[] sortedSquares(int[] nums) { int[] res = new int[nums.length]; int l = 0; int r = nums.length-1; int index = nums.length - 1; while(l <= r){ if(Math.abs(nums[l]) > Math.abs(nums[r])){ res[index] = (int)Math.pow(nums[l],2); l++; }else{ res[index] = (int)Math.pow(nums[r],2); r--; } index --; } return res; }
-
注意点
- 这里空间复杂度为O(1),不是O(n),因为空间复杂度是计算非答案占用的空间
-
扩展
-
想再节省空间的话,可以比较两个数的平方后,进行交换,右指针一直往前移即可
-
补充,该方法不太行。比较完将较大的交换到右边后,不代表前面数的平方一定小于该数。比如 -7、-6、3、4
-
public int[] SortedSquares(int[] nums) { int left = 0; int right = nums.length-1; int leftR = 0, rightR = 0; while(right >= 0){ leftR = nums[left]*nums[left]; rightR = nums[right]*nums[right]; // 左指针的平方比较大,交换到数组的后面来 if(leftR >= rightR){ nums[left] = nums[right]; nums[right] = leftR; }else{ nums[right] = rightR; } right--; } return nums; }
-
总结
- 四种经典的题型对应以上四道题
- 一、二分法。利用有序性提升遍历的速度
- 二、快慢指针。两个同向移动的指针进行遍历,慢指针用来保存有效的元素,实现数组的原地修改
- 三、基于快慢指针的滑动窗口。快慢之间形成一个滑动窗口,通过移动这个滑动窗口,遍历元素的组合情况
- 四、双向指针。即两个相对方向移动的指针,可以同时比较头尾元素,进行交换
企鹅号:1272420336,一起学习,一起进步~