Day1 Leetcode 704 27

175 阅读3分钟

Leetcode 704. 二分查找

心得

  • 虽然2个月前刚刷过,没想到这么快就忘了,看到第一反应还是会想到递归,但是仔细考虑到递归的下一个数组如何拷贝又是个问题,逐渐想起来通过左右索引也能缩短查找空间,细节是魔鬼,区间开闭问题(跟mid的奇偶问题是一个问题,均不均分无所谓,核心是以其为界后,两边怎么分,能不能取到的问题),可以先写出来再用验证边界
  • 耗时20m
  • 代码间接性欠缺,多余变量不必要

题解

  • 有序数组,尤其不重复,基本考虑二分,如果重复元素结果不唯一
  • 边界条件要定好,确定一个原则不能变,左闭右闭或左闭右开两种写法
  • 分别对应[] 和[)同时对应取到的索引是size -1 还是取不到的size,也决定了while判断中的=的取舍
  • 同时分别对应右边界下次遍历的时能否取到从而判断需不需要-1,而左边界一直闭合不用考虑该问题
// 时间复杂度:logN

// 左闭右闭
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int mid;
        
        while (left <= right){
            mid  = (right - left) / 2  + left;
            if (nums[mid] == target){
                return mid;
            } else if (nums[mid] > target){
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }

        return -1;

    }
};

// 左闭右开
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();
        int mid;
        
        while (left < right){ // 此时取不到
            mid  = (right - left) / 2  + left;
            if (nums[mid] == target){
                return mid;
            } else if (nums[mid] > target){
                right = mid; // 开区间,最大mid-1,不用减
            } else {
                left = mid + 1;
            }
        }

        return -1;

    }
};

LeetCode 27. 移除元素

心得

  • 第一时间想到双指针,收尾相互对调,然后从前往后保留的是正确的值,而逆序则都是命中的去除元素,但是如果首尾值一样都是移除的话,右侧收缩,左侧标位,相当于移动右侧,遇到非命中值,再考虑左侧是否需要替换,想起来很繁琐,最后考虑快慢指针,同向移动相对思考简单
  • 相向考虑中始终想的是一侧满足要求另一侧如何互换,增加了难度,应该分别独立考虑两端
  • 耗时20m

题解

  • 快慢指针,同向,一个表示最终结果位置,以终为始,另一个负责遍历,遇到需要放到最后位置的直接赋值过来即可,自己也写的这种,但是代码简洁上有优化空间,cout可以用slow代替,同时返回值可以表示0值情况,fast指针兼并遍历index和相等情况时合并
  • 相向双指针,也可以考虑自己第一反应的相向双指针,移动次数最少,但是改变了相对位置(此题中可以满足题意条件),核心左边是正确位置,始终找左边命中的位置和右侧不命中的,回到他该去的位置,需要考虑循环终止条件,内侧需要大循环的外部条件,因其内部遍历值发生变化需要外部条件确认是否满足大循环
// 时间复杂度:O(n)
// 空间复杂度:O(1)
// 没有改变相对位置
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

/**
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
* 时间复杂度:O(n)
* 空间复杂度:O(1)
*/
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int leftIndex = 0;
        int rightIndex = nums.size() - 1;
        while (leftIndex <= rightIndex) {
            // 找左边等于val的元素
            while (leftIndex <= rightIndex && nums[leftIndex] != val){
                ++leftIndex;
            }
            // 找右边不等于val的元素
            while (leftIndex <= rightIndex && nums[rightIndex] == val) {
                -- rightIndex;
            }
            // 将右边不等于val的元素覆盖左边等于val的元素
            if (leftIndex < rightIndex) {  // 这里为什么去掉=,前面两个循环导致左右不可能等
                nums[leftIndex++] = nums[rightIndex--]; 
            }
        }
        return leftIndex;   // leftIndex一定指向了最终数组末尾的下一个元素
    }
};