LeetCode刷题总结 --- 二分查找与随机选择

12 篇文章 0 订阅
8 篇文章 1 订阅

查找

导言

  1. 以下代码都存放于 我的GitHub仓库 ,如果小伙伴觉得有用,请给我颗星星哈。
  2. 以下代码都是提交过的,正确性可以保证。

二分查找

1. 简单二分查找

1. 寻找数组中「任意一个等于目标值的元素」值的索引

从非递减数组 nums 中找出任意一个索引 index,使得 nums[index] == targetindex 不存在时返回 -1

func binarySearch(nums []int, target int) int {
    l, r := 0, len(nums)-1
    for l <= r {
        mid := l + (r-l)/2
        if nums[mid] == target {
            return mid
        } else {
            if nums[mid] > target {
                r = mid - 1
            } else {
                l = mid + 1
            }
        }
    }
    return -1
}

2. 进阶二分查找

1. 寻找数组中「第一个大于目标值的元素」的索引

从非递减数组 nums 中找出一个索引 index,使得 nums[index] > target,要求 index 尽可能的小,不存在时返回 len(nums)

// 例子: firstGreater([]int{1,2,3,4,5}, 4) -> return 4
// 「第一个大于」
func firstGreater(nums []int, target int) int {
    l, r := 0, len(nums)-1
    for l <= r {
        mid := l + (r-l)/2
        if nums[mid] == target {
            l = mid + 1
        } else {
            if nums[mid] > target {
                r = mid - 1
            } else {
                l = mid + 1
            }
        }
    }
    return l
}
2. 寻找数组中「第一个大于等于目标值的元素」的索引

从非递减数组 nums 中找出一个索引 index,使得 nums[index] >= target,要求 index 尽可能的小,不存在时返回 len(nums)

// 例子: firstGreaterOrEqual([]int{1,2,3,4,5}, 4) -> return 3
// 「第一个大于等于」
func firstGreaterOrEqual(nums []int, target int) int {
    l, r := 0, len(nums)-1
    for l <= r {
        mid := l + (r-l)/2
        if nums[mid] == target {
            r = mid - 1
        } else {
            if nums[mid] > target {
                r = mid - 1
            } else {
                l = mid + 1
            }
        }
    }
    return l
}
3. 寻找数组中「最后一个小于等于目标值的元素」的索引

从非递减数组 nums 中找出一个索引 index,使得 nums[index] <= target,要求 index 尽可能的大,不存在时返回 -1

// 例子: lastLessOrEqual([]int{1,2,3,4,5}, 4) -> return 3
// 「最后一个小于等于」
func lastLessOrEqual(nums []int, target int) int {
    l, r := 0, len(nums)-1
    for l <= r {
        mid := l + (r-l)/2
        if nums[mid] == target {
            l = mid + 1
        } else {
            if nums[mid] > target {
                r = mid - 1
            } else {
                l = mid + 1
            }
        }
    }
    return r
}
4. 寻找数组中「最后一个小于目标值的元素」的索引

从非递减数组 nums 中找出一个索引 index,使得 nums[index] < target,要求 index 尽可能的大,不存在时返回 -1

// 例子: lastLess([]int{1,2,3,4,5}, 4) -> return 2
// 「最后一个小于」
func lastLess(nums []int, target int) int {
    l, r := 0, len(nums)-1
    for l <= r {
        mid := l + (r-l)/2
        if nums[mid] == target {
            r = mid - 1
        } else {
            if nums[mid] > target {
                r = mid - 1
            } else {
                l = mid + 1
            }
        }
    }
    return r
}

3. 二分查找框架

func function(nums []int, target int) int {
    l, r := 0, len(nums)-1
    for l <= r {
        mid := l + (r-l)/2
        if nums[mid] == target {
            /* 
                以下是「索引选择」,有3个选项,单选。 
                
                A. 找等于目标值的索引时: return mid
                B. 找尽可能小的索引时: r = mid - 1
                C. 找尽可能大的索引时: l = mid + 1
            */
        } else {
            if nums[mid] > target {
                r = mid - 1
            } else {
                l = mid + 1
            }
        }
    }
    /* 
        以下是「返回值选择」,有3个选项,单选。

        A. 找等于目标值的索引时: return -1
        B. 找小于、小于等于target的索引时: return r
        C. 找大于、大于等于target的索引时: return l
    */
}

4. 注意点

  • 以上查找要求数组非递减,所以不适用于旋转排序数组。
  • 解决旋转排序数组问题,需要用到二分查找的思想。
  • 一般情况下,题目不会这么"裸",所以实际应用中,可能需要修改一些地方、或进行一些转换。

5. 练习题

随机选择

1. 选择数组中的「第K小」与「第K大」。

// 选择第k小的数 (重复的数次序不等)
// 比如 [1 2 2 3],2是第2小、也是第3小。
func selectSmallKth(nums []int, k int) int {
	l, r := 0, len(nums)-1
	for l <= r {
		index := randomPartition(nums, l, r)
		if index+1 == k {
			return nums[index]
		} else {
			if index+1 > k {
				r = index - 1
			} else {
				l = index + 1
			}
		}
	}
	return -100000000 // 表示没找到 (k非法了)
}

// 选择第k大的数 (重复的数次序不等)
func selectBigKth(nums []int, k int) int {
	// 第k大 == 第 len(nums)-k+1 小
	return selectSmallKth(nums, len(nums)-k+1)
}

// 随机划分 (l,r在合法范围内)
// 作用: 经过划分后,使得 元素x左边 <= 元素x <= 元素x右边,返回元素x此时在数组中的索引。
func randomPartition(nums []int, l int, r int) int {
    randomIndex := rand.Intn(r-l+1) + l                     // 选择随机索引
    nums[randomIndex], nums[l] = nums[l], nums[randomIndex] // 打乱数组
    guardIndex := l
    for l <= r {
        for l <= r && nums[l] <= nums[guardIndex] {
            l++
        }
        for l <= r && nums[r] >= nums[guardIndex] {
            r--
        }
        if l <= r {
            nums[l], nums[r] = nums[r], nums[l]
        }
    }
    nums[guardIndex], nums[r] = nums[r], nums[guardIndex]
    return r
}

2. 拓展

  • 如果要求数组中相同的数,它们的次序是一样的,那需要对数组进行怎样的预处理呢?
  • 如果数组元素太多,以致内存无法装下,请问还有什么办法获得第K大吗?

(欢迎在评论区给我留言~)

3. 练习题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值