swift算法:搜索旋转排序数组

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lin1109221208/article/details/90902919

1、描述:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。例如 :数组[0, 1, 2, 4, 5, 6, 7] 可能变为 [4, 5, 6, 7, 0, 1, 2]。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1。

你可以假设数组中不存在重复元素,你的算法时间复杂度必须是O(logn)级别。

例1:输入:nums = [4, 5, 6, 7, 0, 1, 2],target = 0

         输出:4

例2:输入:nums = [4, 5, 6, 7, 0, 1, 2],target = 3

          输出:-1

2、算法

由于题目中明确了时间复杂度,所以只能采用二分查找法

1)直接用二分法,判断二分点的位置

func search3(_ nums: [Int], _ target: Int) -> Int {
        //直接用二分法,判断二分点-----超出时间限制
        let n = nums.count
        if n==0 {
            return -1
        }
        
        var left = 0
        var right = n-1
        while left <= right {
            let mid = left+(right-left)/2
            //如果中间值小于第一个数,目标值也小于第一个数,取中间值
            let num = (nums[mid]<nums[0]) == (target<nums[0]) ? nums[mid] : target<nums[0] ? Int.min : Int.max
            if num < target {
                //中间值小于目标值,在中间值的右边查找
                left = mid+1
            }else if num > target {
                //中间值大于目标值,在中间值的左边查找
                right = mid
            }else {
                //中间值等于目标值,返回中间值下标
                return mid
            }
        }
//        print(left, right)
        return -1
    }

2)直接用二分法的方法2

func search2(_ nums: [Int], _ target: Int) -> Int {
        //直接用二分法,判断二分点
        /*
         1、直接等于target
         2、在左半边递增区域
            1)target 在 left 和 mid 之间
            2)不在之间
         3、在右边的递增区域、
            1)target在mid 和 right 之间
            2)不在之间
         */
        let n = nums.count
        if n==0 {
            return -1
        }
        
        var left = 0
        var right = n-1
        while left < right {
            let mid = left+(right-left)/2
            if nums[mid] == target {
                return mid
            }else if nums[mid] >= nums[left]{//二分点在左半边递增区域
                if nums[left] <= target && target < nums[mid] {
                    //target位于 left 和 mid 之间
                    right = mid-1
                }else{
                    left = mid+1
                }
            }else if nums[mid] < nums[right]{
                //二分点在右边的递增区域
                if nums[mid] < target && target <= nums[right] {
                    left = mid+1
                }else{
                    right = mid-1
                }
            }
        }
//        print(left, right)
        return nums[left] == target ? left : -1
    }

3)二分查找法,先找到分割点,再查找目标值

func search1(_ nums: [Int], _ target: Int) -> Int {
        //二分查找法:先找到数组中最小的数 即分割点
        let n = nums.count
        if n==0 {
            return -1
        }
        var left = 0
        var right = n-1
        while left < right {
            let mid = left + (right-left)/2
            if nums[mid] > nums[right] {
                //如果中间的数大于右边的数,从mid的右边查找
                left = mid+1
            }else{
                right = mid
            }
        }
        //分割点下标
        let split_t = left
        left = 0
        right = n-1
        
        //判断分割点在target的左边还是右边
        if nums[split_t] <= target && target <= nums[right] {
            //分割点在target的右边
            left = split_t
        }else{
            //分割点在target的左边
            right = split_t
        }
        while left <= right {
            let mid = left + (right-left)/2
            if nums[mid] == target {
                //中间值等于目标值
                return mid
            }else if nums[mid] > target {
                //中间值大于目标值,右边下标左移一位
                right = mid - 1
            }else{
                //反之,左边下标右移一位
                left = mid+1
            }
        }
        return -1
    }

4)二分查找法:先找到数组中最小的数 即发生旋转的下标,分3个函数

func search(_ nums: [Int], _ target: Int) -> Int {
        //二分查找法:先找到数组中最小的数 即发生旋转的下标
        let n = nums.count
        if n==0 {
            return -1
        }
        if n==1 {
            return nums[0]==target ? 0 : -1
        }
        
        let rotate_index = find_rotate_index(nums, 0, n-1)
        
        //如果target是最小的值
        if nums[rotate_index] == target {
            return rotate_index
        }
        //如果数组并没有旋转,需要查找整个数组
        if rotate_index == 0 {
            return search(nums, target, 0, n-1)
        }
        if target < nums[0] {
            //如果目标值小于第一个数,在旋转下标的右边查找
            return search(nums, target, rotate_index, n-1)
        }else{
            return search(nums, target, 0, rotate_index)
        }
    }
    //找到旋转的下标
    private func find_rotate_index(_ nums : [Int], _ left: Int, _ right : Int)->Int{
        var left = left
        var right = right
        if nums[left] < nums[right] {
            //数组未发生旋转
            return 0
        }
        
        //二分查找
        while left <= right {
            var pivot = (left+right)/2
            if nums[pivot] > nums[pivot+1] {
                //基准数>基准后一位,往右边查找
                return pivot+1
            }else{
                //如果基准数小于左边左边的数,则右下标左移一位,反之,左下标右移一位
                if nums[pivot]<nums[left]{
                    right = pivot-1
                }else{
                    left = pivot+1
                }
            }
        }
        return 0
    }
    //根据下标二分查找
    private func search(_ nums : [Int], _ target : Int, _ left: Int, _ right : Int)->Int{
        var left = left
        var right = right
        
        while left <= right {
            var pivot = (left+right)/2
            if nums[pivot]==target {
                return pivot
            }else{
                if target < nums[pivot] {
                    right = pivot-1
                }else{
                    left = pivot+1
                }
            }
        }
        return -1
    }

5)调测

调用:
        var num3 = [4, 5, 6, 7, 0, 1, 2]
        print(search(num3, 0), search(num3, 3))
        print(search1(num3, 0), search1(num3, 3))
        print(search2(num3, 0), search2(num3, 3))
        print(search3(num3, 0), search3(num3, 3))


运行结果:

4 -1
4 -1
4 -1
4 -1

猜你喜欢

转载自blog.csdn.net/lin1109221208/article/details/90902919