• 水题: 旋转数组系列


    189. 轮转数组

    思路1: 引用辅助数组 + 拷贝

    func rotate(nums []int, k int)  {
       res := make([]int, len(nums))
    
       for i, v := range nums {
           res[(i+k) % len(nums)] = v
       }
    
       copy(nums, res)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    思路2: 字符串逆转

    func rotate(nums []int, k int) {
    	reverse := func(nums[]int, beg int, end int) {
            for beg < end {
                nums[beg], nums[end-1] = nums[end-1], nums[beg]
                beg++;
                end--;
            }
        }
    
        k = k % len(nums)
        reverse(nums, 0, len(nums))
        reverse(nums, 0, k)
        reverse(nums, k, len(nums))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    153. 寻找旋转排序数组中的最小值

    • 有序数组旋转
    • 寻找最小值
    • 不允许重复
    func findMin(nums []int) int {
        l, r := 0, len(nums) - 1
    
        for l < r {
            m := l + (r - l)/2
    
            if nums[m] < nums[r] {
                r = m
            }else {
                l = m + 1
            }
        }
    
        return nums[r]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    此处,为什么只看右边界nums[r]呢? 因为数组可能没有旋转过,如果我们通过左边界来判断,会误以为最小值在右子数组中。

    154. 寻找旋转排序数组中的最小值 II

    在153的前提下,允许数字重复。因此相等nums[r] == nums[m] 时我们需要将r挪动一个位置。直接将r = m可能会错过最小值, 因为可能出现这种状况[2, 2, 2, 1, 2]

    • 允许重复
    • 寻找最小值
    • 有序数组旋转
    class Solution {
    public:
        int findMin(vector<int>& nums) {
            int l = 0, r = nums.size() - 1;
    
            int m ;
            while(l < r) {
                m = l + (r - l)/2;
                if (nums[r] > nums[m]) {
                    r = m;
                }else if(nums[r] == nums[m]) {
                    r--;
                }else{
                    l = m + 1;
                }
            }
    
            return nums[r];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这个二分的写法与我们之前提及的二分查找的模板不一致,用之前的模板反而无法实现这样的情形。

    33. 搜索旋转排序数组

    其实,与153的旋转的概念依旧是一样的,只不过问题从找最小值,变成了找target

    由于是有序数组的旋转,中点会将数组划分成两个子数组。可能两个子数组都有序,也可能一个有序而另一个无序,不可能出现两个都无序的情况。

    对于有序的子数组,我们可以直接找到target在不在里面
    对于无序的子数组,我们可以继续用中点划分

    • 不允许重复
    • 判断是否包含target值
    • 有序数组旋转
    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int l = 0, r = nums.size() - 1;
    
            while (l <= r) {
                int m = l + (r - l)/2;
                if(nums[m] == target) return m;
    
                if(nums[m] < nums[r]) {
                    // 右边有序
                    if(nums[m] < target && nums[r] >= target){
                        // target在右子数组内部
                        l = m + 1;
                    }else{
                        r = m;
                    }
                }else{
                    // 左边有序
                    if(nums[m] > target && nums[l] <= target){
                        // target在左子数组内部
                        r = m;
                    }else{
                        l = m + 1;
                    }
                }
            }
    
            return -1;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    81. 搜索旋转排序数组 II

    此处其实是33的进阶版本,多加了一个条件,即允许重复值。
    同样,如果相等我们就挪动一步呗。

    • 允许重复
    • 判断是否包含target值
    • 有序数组旋转
    class Solution {
    public:
        bool search(vector<int>& nums, int target) {
            int l = 0, r = nums.size() - 1;
    
            while (l <= r) {
                int m = l + (r - l)/2;
                if(nums[m] == target) return true;
    
                if(nums[m] < nums[r]) {
                    // 右边有序
                    if(nums[m] < target && nums[r] >= target){
                        // target在右子数组内部
                        l = m + 1;
                    }else{
                        r = m;
                    }
                }else if(nums[m] > nums[r]){
                    // 左边有序
                    if(nums[m] > target && nums[l] <= target){
                        // target在左子数组内部
                        r = m;
                    }else{
                        l = m + 1;
                    }
                }else{
                    r--;
                }
            }
    
            return false;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    这里的挪动一步,可以是l也可以是r

    面试题 10.03. 搜索旋转数组

    • 有序数组的旋转
    • 允许重复
    • 寻找target值的最小下标

    因为允许重复, 所以我们找到相同的只能一步一步挪

    对于81略作修改,对于输入[5,5, 1, 2,3,4,5] 时target为5的情况,我们需要先排除掉。因此,只需要用常规作法即可。

    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int l = 0, r = nums.size() - 1;
    
            while (l <= r) {
                int m = l + (r - l)/2;
                // 排除target恰好同时在最前和最后
                if(nums[l] == target) return l;
    
                if(nums[m] == target) {
                    for(;m >= 0 && nums[m] == target; m--);
                    return m + 1;
                }
    
                if(nums[m] < nums[r]) {
                    // 右边有序
                    if(nums[m] < target && nums[r] >= target){
                        // target在右子数组内部
                        l = m + 1;
                    }else{
                        r = m;
                    }
                }else if(nums[m] > nums[r]){
                    // 左边有序
                    if(nums[m] > target && nums[l] <= target){
                        // target在左子数组内部
                        r = m;
                    }else{
                        l = m + 1;
                    }
                }else{
                    r--;
                }
            }
    
            return -1;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    对于中间那部分,如果担心复杂度超过O(logn), 可以也可以写成这样

    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int l = 0, r = nums.size() - 1;
    
            while (l <= r) {
                int m = l + (r - l)/2;
                // 排除target恰好同时在最前和最后
                if(nums[l] == target) return l;
    
                if(nums[m] == target) {
                    // for(;m >= 0 && nums[m] == target; m--);
                    // return m + 1;
                    r = m;
                    continue;
                }
    
                if(nums[m] < nums[r]) {
                    // 右边有序
                    if(nums[m] < target && nums[r] >= target){
                        // target在右子数组内部
                        l = m + 1;
                    }else{
                        r = m;
                    }
                }else if(nums[m] > nums[r]){
                    // 左边有序
                    if(nums[m] > target && nums[l] <= target){
                        // target在左子数组内部
                        r = m;
                    }else{
                        l = m + 1;
                    }
                }else{
                    r--;
                }
            }
    
            return -1;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 相关阅读:
    2022寒假字节跳动前端训练营笔记
    Python中pupl的基础操作
    深度学习之 10 卷积神经网络2
    Axios+Elementui+Vue+原生JS+后端原生java的JAVA web小组项目踩坑总结
    sentinel实现流控规则nacos持久化
    Spring Boot 日志文件和单元测试
    架构:应用架构的演进以及微服务架构的落地实践
    verdi fsdb转vcd波形:用于后端功耗分析
    cpp学习笔记:STL queue容器
    最新出炉的Java面试题(2022亲身经历)
  • 原文地址:https://blog.csdn.net/u012342808/article/details/126232699