当前位置:   article > 正文

代码随想录算法训练营第一天|704.二分查找、27.移除元素_代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素

代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素

lc704. 二分查找

  1. class Solution {
  2. public:
  3. int search(vector<int>& nums, int target) {
  4. int left = 0;
  5. int right = nums.size() - 1; //定义target的区间左闭右闭,[left,right]
  6. while (left <= right){
  7. int middle = left + ((right - left) / 2); //防止溢出
  8. if (nums[middle] > target){
  9. right = middle - 1;
  10. }
  11. else if (nums[middle] < target){
  12. left = middle + 1;
  13. }
  14. else{
  15. return middle;
  16. }
  17. }
  18. //未找到目标值
  19. return -1;
  20. }
  21. };

二分查找主要是要注意自己的代码是左闭右闭还是左闭右开,也就是第一点:left = right是否合法,第二点:nums[middle]不是搜索值,这时候再看是右开还是右闭,闭区间的right值能搜索到所以不能等于right值,反之右开区间可以。

还有点小细节就是可以用位运算:>>1 去替换 / 2,这样运算速度会更快。

lc34.在排序数组中查找元素的第一个和最后一个位置

lc35.搜索插入位置

以704为母题,34、35和704也是一类题。特点是先判断target值在数组中的位置情况,其中35要求时间复杂度为O(log n), 其实遇到这类暗示一般都会想到用二分法查找, 具体情况如下:

  1. class Solution {
  2. public:
  3. int searchInsert(vector<int>& nums, int target) {
  4. int n = nums.size();
  5. int left = 0;
  6. int right = n - 1;
  7. while (left <= right) { //当区间为[left, right]时仍然有效
  8. int middle = left + ((right - left) / 2);
  9. if (nums[middle] > target) {
  10. right = middle - 1;
  11. }
  12. else if (nums[middle] < target) {
  13. left = middle + 1;
  14. }
  15. else { //nums[middle] == target
  16. return middle;
  17. }
  18. }
  19. // 处理四种情况
  20. // 目标值在数组所有元素之前 [0 , -1]
  21. // 目标值等于数组中的某个元素, return middle;
  22. // 目标值插入数组中的位置 [left , right], return right + 1;
  23. // 目标值在数组所有元素后, return right + 1;
  24. return right + 1;
  25. }
  26. };

而34题更复杂一点,由于数组中有重复的目标值,需要去找目标值的左边界和右边界,通过给左右边界赋值去判断目标值是否在数组范围中: 

  1. class Solution {
  2. public:
  3. vector<int> searchRange(vector<int>& nums, int target) {
  4. int leftBorder = getLeftBorder(nums, target);
  5. int rightBorder = getRightBorder(nums, target);
  6. // target不在数组范围中
  7. if(leftBorder == -2 || rightBorder == -2) return {-1, -1};
  8. // target在数组范围中且数组存在target
  9. if(rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
  10. // target在数组范围中但是数组不存在target
  11. return {-1, -1};
  12. }
  13. private:
  14. int getRightBorder(vector<int>& nums, int target) {
  15. int left = 0;
  16. int right = nums.size() - 1; // 定义target在左闭右闭的区间内,[left,right]
  17. int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
  18. while(left <= right){
  19. int middle = left + ((right - left) / 2); //防止溢出
  20. if (nums[middle] > target){
  21. right = middle - 1;
  22. }
  23. else{
  24. // 当nums[middle] == target时,才得到target的右边界
  25. left = middle + 1;
  26. rightBorder = left;
  27. }
  28. }
  29. return rightBorder;
  30. }
  31. int getLeftBorder(vector<int>& nums, int target) {
  32. int left = 0;
  33. int right = nums.size() - 1; // 定义target在左闭右闭的区间内,[left,right]
  34. int leftBorder = -2; // 记录一下rightBorder没有被赋值的情况
  35. while(left <= right){
  36. int middle = left + ((right - left) / 2); //防止溢出
  37. if (nums[middle] >= target){// nums[middle] = target时更新right
  38. right = middle - 1;
  39. leftBorder = right;
  40. }
  41. else{
  42. left = middle + 1;
  43. }
  44. }
  45. return leftBorder;
  46. }
  47. };

注:关于二分查找最好画个数轴,直观一些。

lc27.移除元素 给一个数组,一个目标值,在数组中删除等于这个目标值的元素,返回新数组的大小。

这个题最关键的底层逻辑是数组中的元素不能删除,只能覆盖。如果用暴力法就是先遍历数组,再赋下标值。通过双指针的思路,把快指针记录的新数组中所需的元素值赋给慢指针,而慢指针的作用是获取新数组中需要更新的位置,遇到相同值的元素时不更新慢指针的位置进入下个循环。

  1. class Solution {
  2. public:
  3. int removeElement(vector<int>& nums, int val) {
  4. int slowIndex = 0;
  5. for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++){
  6. if (val != nums[fastIndex]){
  7. nums [slowIndex++] = nums [fastIndex];
  8. }
  9. }
  10. return slowIndex;
  11. }
  12. };

与27题相关的题还没做,明天一并更新。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/959265
推荐阅读
相关标签
  

闽ICP备14008679号