代码随想录算法训练营第一天 | 704, 27, 35, 34

代码随想录算法训练营第一天 | 704. 二分查找,27. 移除元素,35.搜索插入位置, 34. 在排序数组中查找元素的第一个和最后一个位置

  • 704. 二分查找
    • 文章:
    • 视频
    • 思路
    • 代码
  • 27. 移除元素
    • 文章
    • 视频
    • 思路
    • 代码
  • 35.搜索插入位置
    • 文章
    • 视频
    • 思路
    • 代码
  • 34. 在排序数组中查找元素的第一个和最后一个位置
    • 文章
    • 视频
    • 思路
    • 代码
  • 总结

704. 二分查找

文章:

代码随想录0704.二分查找

视频

手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找

思路

是非常朴素的二分查找。需要注意区间的开闭,我个人喜欢用前闭后开的区间 [ l e f t , m i d ) , m i d , [ m i d + 1 , r i g h t ) [left, mid), mid, [mid + 1, right) [left,mid),mid,[mid+1,right),其中 l e f t left left初始为 0 0 0 r i g h t right right初始为 n u m . l e n g t h num.length num.length

代码

class Solution {
    public int search(int[] nums, int target) {
        int left, right, mid;
        left = 0;
        right = nums.length - 1;
        while (left <= right) {
            mid = (left + right) / 2;
            if(nums[mid] == target) {
                return mid;
            }
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
}

27. 移除元素

文章

代码随想录0027.移除元素

视频

数组中移除元素并不容易! | LeetCode:27. 移除元素

思路

移除数组中的元素,实际上是求原数组的一个子集,这个子集中元素的出现顺序与原数组相同,且不含有被指定要删除的元素。这个时候考虑到快慢指针,用 f a s t fast fast指针搜索下一个可以加入子集的元素并将该元素填入 s l o w slow slow指针所指向的位置;每次填入之后同时将 s l o w slow slow f a s t fast fast向后移动,分别表示下一个可填入位置和下一轮查找的起点。最终跳出循环时, s l o w slow slow会指向所求子集最后一个元素的后一个位置,也即能表示所求子集的长度。

代码

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow, fast;
        slow = 0;
        fast = 0;
        while(fast < nums.length) {
            if (nums[fast] == val) {
                ++fast;
                continue;
            }
            nums[slow] = nums[fast];
            ++slow;
            ++fast;
        }
        return slow;
    }
}

35.搜索插入位置

文章

没看

视频

没看

思路

同样是二分查找的题目。当带查找的元素在数组中存在时,与704题解法相同。当数组中不存在这样一个元素时,需要考虑一下最后跳出循环时问题的状态。

  • 如果最后一次检查发现 n u m s [ m i d ] < t a r g e t nums[mid] < target nums[mid]<target 而后更新左边界 l e f t = m i d + 1 left = mid + 1 left=mid+1 不满足循环条件 l e f t < r i g h t left < right left<right 说明此时 m i d + 1 mid + 1 mid+1 r i g h t right right 相等。插入位置不可能在 r i g h t right right 的后面,因为如果 r i g h t right right 及其后面如果有元素就一定比 t a r g e t target target 大;插入位置也不可能在 r i g h t right right 前面,因为 r i g h t right right 前面最大的元素也比 t a r g e t target target 小 。此时插入位置一定是 r i g h t right right
  • 如果最后一次检查发现 n u m s [ m i d ] > t a r g e t nums[mid] > target nums[mid]>target 而后更新右边界 r i g h t = m i d right = mid right=mid 不满足循环条件 l e f t < r i g h t left < right left<right 说明此时 r i g h t right right l e f t left left 相等。这意味着 n u m s [ l e f t ] nums[left] nums[left] t a r g e t target target 大,但这是不可能的。所以最后一次检查不可能是 n u m s [ m i d ] > t a r g e t nums[mid] > target nums[mid]>target 的情况。
    所以对于查不到 t a r g e t target target 转而要找插入位置的情况,最后返回 r i g h t right right 即可。

代码

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left, right, mid;
        left = 0;
        right = nums.length;
        while (left < right) {
            mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return right;
    }
}

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

文章

没看

视频

没看

思路

二分查找,退出循环后检查 n u m s [ m i d ] nums[mid] nums[mid] 是否是 t a r g e t target target

  • 如果不是则说明查不到,答案为 [ − 1 , − 1 ] [-1, -1] [1,1]
  • 如果是则从 m i d mid mid 位置开始想两遍展开搜索直到达到边界。

代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        int left, right, mid;
        left = 0;
        right = nums.length;
        mid = right / 2;
        while(left < right) {
            mid = (left + right) / 2;
            if(nums[mid] == target){
                break;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (nums.length == 0 || nums[mid] != target){
            res[0] = -1;
            res[1] = -1;
        } else {
            left = mid;
            right = mid;
            boolean l = false;
            boolean r = false;
            while(left >= 0 && right < nums.length) {
                if(left == 0){
                    l = true;
                } else if (nums[left - 1] == target) {
                    --left;
                } else {
                    l = true;
                }
                if (right == nums.length - 1){
                    r = true;
                } else if(nums[right + 1] == target) {
                    ++right;
                } else {
                    r = true;
                }
                if(l && r) {
                    res[0] = left;
                    res[1] = right;
                    break;
                }
            }
        }
        return res;
    }
}

总结

第一天打卡,题目本身不难,但是要非常注意细节,二分查找的边界处理非常重要,很久以前因为没有考虑清楚区间的开闭导致死循环超时。今天做题的时候统一使用了前闭后开,没有出错,非常奶思。

你可能感兴趣的:(算法)