vlambda博客
学习文章列表

数据结构和算法(7)三个“二分查找”练习

关于二分查找算法的总结和三个LeetCode练习题,简单或中等难度,编号就是LeetCode的题号,Swift语言编码解答。如有错误或需优化处,欢迎交流指正

/*
* 二分查找要点介绍
* 69. x 的平方根
* 33. 搜索旋转排序数组
* 167. 两数之和 II - 输入有序数组
*/
二分查找介绍

二分查找主要用于有序数据集合元素查找,其时间复杂度为O(logN)

写二分查找代码的要点

  • 退循环条件为low <= high
  • mid 最佳取值公式为 mid = low+((high-low)>>1
  • 两端更新方式为 low = mid+1 或 high = mid-1

二分查找适用场景

  • 适合顺序表(数组),不适合链表
  • 需要连续的内存空间
  • 数据量不宜太大,GB量级的就不适合了

二分查找变题

  • 查找第一个值等于给定值的元素
  • 查找最后一个值等于给定值的元素
  • 查找第一个大于等于给定值的元素
  • 查找最后一个小于等于给定值的元素
69. x 的平方根

实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

适合用二分查找法,如求8的平方根可转换为找数组[1,2,3,4,5,6,7,8]数组中平方最接近8的数字, 二分查找过程如下

[1,2,3,4,5,6,7,8]
[1,2,3,4]
[2,4]
[2,3]
[2]

实现代码如下(swift)

class Solution {
func mySqrt(_ x: Int) -> Int {
if x == 0 {
return 0
}
var low = 1
var high = x/2 + 1
while high - low > 1 {
let mid = (low + high)/2
if mid * mid > x {
high = mid
} else {
low = mid
}
}
return low
}
}
33. 搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。

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

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

提出一种解法,先找出翻转点,再判断target所在区间分区间查找。

class Solution {
func search(_ nums: [Int], _ target: Int) -> Int
{
// 1.0
guard nums.count > 0 else {
return -1
}

var low = 0, high = nums.count-1
var mid = low + (high-low)>>1

// 先找分割点
var minFlag = 0

// 先判断是否翻转点是否在数组中间(最后一个元素翻转,对数组顺序无影响)
// 若翻转则nums[low] > nums[high]
if nums[low] > nums[high] {
while low < high {
if nums[mid] > nums[low] {
low = mid
} else {
high = mid
}
mid = low + (high-low)>>1
}
minFlag = mid+1
}

// 二分查找
if target <= (nums.last ?? 0) {
low = minFlag
high = nums.count - 1
} else {
low = 0
high = minFlag - 1
}
mid = low + (high-low)>>1
while low <= high {
if nums[mid] > target{
high = mid - 1
} else if nums[mid] < target {
low = mid + 1
} else {
return mid
}
mid = low + (high-low)>>1
}

return -1
}
}
167. 两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

这题并没有用折半查找,但用到有序的特性来进行遍历。思路相对简单:双指针分别从头尾遍历。swift代码实现如下

class Solution {
func twoSum(_ numbers: [Int], _ target: Int) -> [Int] {

var low = 0
var high = numbers.count - 1

while low < high
{
if (numbers[low] + numbers[high]) == target{
return [low+1, high+1]
} else if (numbers[low] + numbers[high]) < target {
low += 1
} else {
high -= 1
}
}

return []
}
}

最后欢迎同学们交流关注~~