寻找重复数——算法的体现

127 阅读1分钟

image.png

方法1 (二分查找):

例子: image.png

  1. 我们把(1-12)12个数放在了13个框里,也就是我们最终零号位置填入一个数,也就是重复的那个数,如果我们填入的那个数小于等于6,那么小于等于mid的数由原来的6个数变成了7个数,(如果填入的数大于6,则大于mid的数由原来的6个数变成了7个数),我们可以通过这个特性,来进行二分查找
  2. 在步骤1中我们只讨论了体会零号位置所造成的变化,但其实改变其他位置所造成的效果也是一样的
  3. 至于代码实现的细节,外层for循环进行二分控制,临界条件 l <= r
  4. 内层for循环进行遍历查找小于等于mid的数的个数,记录这个个数(cont),在退出内层for循环后与mid进行比较,如果满足小于等于mid,说明重复的那个数一定,大于mid
  5. 如果大于mid,则重复的数小于等于mid,但是为了可以退出循环,我们需要将 r=(mid-1),为了防止mid刚好就是那个重复的数,那么我们就需要将mid先记录为ans,如果之后的循环无法再进入else,那么也就说明ans所记录的那个数就是答案,如果再次进入else,那么ans也会被覆盖
func findDuplicate(nums []int) int {
    ans := 0
    n := len(nums)
    l , r := 1 , n-1
    for l <= r {
        mid := (l+r) >> 1
        cont := 0
        for i := 0; i < n; i++{
            if nums[i] <= mid {
                cont++
            }
        }
        if cont <= mid {
            l = mid + 1
        }else {
            r = mid - 1
            ans = mid
        }
    }
    return ans
}

方法2(二进制——二分查找):

func findDuplicate(nums []int) int {
    n := len(nums)
    ans := 0
    bit_max := 31
    for ((n-1) >> bit_max) == 0 {
        bit_max--
    }
    for bit := 0; bit <= bit_max; bit++ {
        x, y := 0, 0
        for i := 0; i < n; i++ {
            if (nums[i] & (1 << bit)) > 0 {
                x++
            }
            if i >= 1 && (i & (1 << bit)) > 0 {
                y++
            }
        }
        if x > y {
            ans |= 1 << bit
        }
    }
    return ans
}

方法3(快慢指针):

func findDuplicate(nums []int) int {
    slow,fast := 0,0
    slow,fast = nums[slow],nums[nums[fast]]
    for slow != fast {
        slow, fast = nums[slow],nums[nums[fast]]
    }
    slow = 0
    for slow != fast {
        slow, fast = nums[slow], nums[fast]
    }
    return slow
}