文章目录
二分查找之基础题目
1.二分查找
1.1暴力解法
func search(nums []int, target int) int {
if nums==nil {
return -1
}
for i :=0; i<len(nums);i++{
if nums[i]==target{
return i
}
}
return -1
}
1.2左闭右闭区间二分查找
func search(nums []int, target int) int {
high := len(nums)-1// 定义target在左闭右闭的区间里,[left, right]
low := 0
for low <= high {// 当left==right,区间[left, right]依然有效,所以用 <=
mid := low + (high-low)/2 // 防止溢出 等同于(left + right)/2
if nums[mid] == target { // 数组中找到目标值,直接返回下标
return mid
} else if nums[mid] > target {
high = mid-1// target 在左区间,所以[left, middle - 1]
} else {// target 在右区间,所以[middle + 1, right]
low = mid+1
}
}
return -1 // 未找到目标值
}
//可以debug尝试一下查[]int{1, 2, 3, 4, 7, 9, 10}中的3;正好low==high,mid右往右移动了
//注:不管是奇数个元素,还是偶数个元素,算的都是中间值
//可以debug尝试一下查[]int{1, 2, 3, 4, 7, 9, 10},寻找-1,最终high=-1,low=0,退出循环
可以debug尝试一下查[]int{1, 2, 3, 4, 7, 9, 10},寻找100,最终high=6,low=7,退出循环
1.3左闭右开区间二分查找
func search(nums []int, target int) int {
high := len(nums)// 定义target在左闭右开的区间里,即:[left, right)
low := 0
for low < high {// 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
//假如数组长度有4个元素,则mid=1;则num是第二个元素
//假如数组长度有5个元素,则mid=2;则num是第三个元素
mid := low + (high-low)/2
if nums[mid] == target { // 数组中找到目标值,直接返回下标
return mid
} else if nums[mid] > target {// target 在左区间,在[left, middle)中
high = mid
} else {// target 在右区间,在[middle + 1, right)中
low = mid+1
}
}
return -1// 未找到目标值
}
//可以debug尝试一下查[]int{1, 2, 3, 4, 7, 9, 10}中的3;正好low < high,差一点接近于low===high
//可以debug尝试一下查[]int{1, 2, 3, 4, 7, 9, 10},寻找-1,最终high=0,low=0,退出循环
可以debug尝试一下查[]int{1, 2, 3, 4, 7, 9, 10},寻找100,最终high=7,low=7,退出循环
1.4递归左闭右闭区间二分查找
func search(nums []int, target int) int {
return BinarySearch(nums,target,0,len(nums)-1)
}
func BinarySearch(array []int, target int, l, r int) int {
if l > r {
// 出界了,找不到
return -1
}
// 从中间开始找
mid := (l + r) / 2
middleNum := array[mid]
if middleNum == target {
return mid // 找到了
} else if middleNum > target {
// 中间的数比目标还大,从左边找
return BinarySearch(array, target, 0, mid-1)
} else {
// 中间的数比目标还小,从右边找
return BinarySearch(array, target, mid+1, r)
}
}
1.5递归左闭右开区间二分查找
func search(nums []int, target int) int {
return BinarySearch(nums,target,0,len(nums))
}
func BinarySearch(array []int, target int, l, r int) int {
if l >= r {
return -1
}
mid := (l + r) / 2
middleNum := array[mid]
if middleNum == target {
return mid
} else if middleNum > target {
return BinarySearch(array, target, 0, mid)
} else {
return BinarySearch(array, target, mid+1, r)
}
}
704.二分查找
2.搜索插入位置
2.1暴力解法
func searchInsert(nums []int, target int) int {
for i:=0;i<len(nums);i++{
// 分别处理如下三种情况
// 目标值在数组所有元素之前,返回0
// 目标值等于数组中某一个元素
// 目标值插入数组中的位置
if target <= nums[i]{
return i
}
}
// 目标值在数组所有元素之后的情况
return len(nums)// 如果target是最大的,或者 nums为空,则返回nums的长度
}
//时间复杂度:O(n) 空间复杂度:O(1)
2.2左闭右闭区间二分查找
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)-1// 定义target在左闭右闭的区间里,[left, right]
for left <= right {// 当left==right,区间[left, right]依然有效
mid := left + (right-left)/ 2 //也可以写成mid := left + (right-left) >> 1,防止溢出 等同于(left + right)/2
if nums[mid] < target {
left = mid + 1// target 在右区间,所以[middle + 1, right]
} else if nums[mid] > target {
right = mid -1 // target 在左区间,所以[left, middle - 1]
} else {// nums[middle] == target
return mid
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
return left
}
//时间复杂度:O(log n)
//空间复杂度:O(1)
二分法
左闭右闭二分查找
2.3左闭右开区间二分查找
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)// 定义target在左闭右开的区间里,[left, right) target
for left < right {// 因为left == right的时候,在[left, right)是无效的空间
mid := left + (right-left)/ 2 //也可以写成mid := left + (right-left) >> 1
if nums[mid] < target {// target 在右区间,在 [middle+1, right)中
left = mid + 1
} else if nums[mid] > target {
right = mid // target 在左区间,在[left, middle)中
} else {// nums[middle] == target
return mid// 数组中找到目标值的情况,直接返回下标
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0,0)
// 目标值等于数组中某一个元素 return middle
// 目标值插入数组中的位置 [left, right) ,return right 即可
// 目标值在数组所有元素之后的情况 [left, right),return right 即可
return left
}
//时间复杂度:O(log n)
//空间复杂度:O(1)
3.在排序数组中查找元素的第一个和最后一个位置
3.1左闭右闭区间二分查找
func searchRange(nums []int, target int) []int {
return []int{searchFirst(nums, target), searchLast(nums, target)}
}
//寻找左边界
// 二分查找,寻找target的左边界leftBorder(不包括target)
// 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4),为了处理情况一
func searchFirst(nums []int, target int) int {
left, right := 0, len(nums)-1// 定义target在左闭右闭的区间里,[left, right]
for left <= right {
mid := left + (right-left)>>1
if nums[mid] >= target {// 寻找左边界,就要在nums[middle] == target的时候更新right
right = mid - 1
} else {
left = mid + 1
}
}
if left = len(nums) || nums[left] != target { // 判断一下是否越界,或者不相等
return -1
}
return left
}
//寻找右边界
// 二分查找,寻找target的右边界(不包括target)
// 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2),为了处理情况一
func searchLast(nums []int, target int) int {
left, right := 0, len(nums)-1// 定义target在左闭右闭的区间里,[left, right]
for left <= right {// 当left==right,区间[left, right]依然有效
mid := left + (right-left)>>1
if nums[mid] > target { // 这里是 > 而不是 >=
right = mid - 1 // target 在左区间,所以[left, middle - 1]
} else {// 当nums[middle] == target的时候,更新left,这样才能得到target的右边界
left = mid + 1
}
}
if right == -1 || nums[right] != target { // 判断一下是否越界,或者不相等
return -1
}
return right // 这里返回 right 而不是 left
}
4.二分查找第一个元素
4.1左闭右闭区间二分查找
func main() {
fmt.Println(searchFirst([]int{5, 7, 7, 8, 8, 10}, 1)) //输出-1;最终left=0,right=-1
fmt.Println(searchFirst([]int{5, 7, 7, 8, 8, 10}, 8)) //输出3;最终left=3,right=2
fmt.Println(searchFirst([]int{5, 7, 7, 8, 8, 10}, 19)) //输出-1;最终,left=6,right==5
fmt.Println(searchFirst([]int{5, 7, 7, 8, 8, 10}, 6)) //输出-1;最终:left=1,right=0
fmt.Println(searchFirst([]int{5, 7, 7, 8, 8, 10}, 5)) //输出0;最终:left=0,right=-1
}
func searchFirst(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)>>1
if nums[mid] >= target {
right = mid - 1
} else {
left = mid + 1
}
}
if left = len(nums) || nums[left] != target { // 判断一下是否越界,或者不相等
return -1
}
return left
}
5.二分查找第二个元素
5.1左闭右闭区间二分查找
func main() {
fmt.Println(searchLast([]int{5, 7, 7, 8, 8, 10}, 1)) //输出-1,最终left==0,right=-1
fmt.Println(searchLast([]int{5, 7, 7, 8, 8, 10}, 8)) //输出4,最终left=5,rught=4
fmt.Println(searchLast([]int{5, 7, 7, 8, 8, 10}, 19)) //输出-1,最终left=6,right=5
fmt.Println(searchLast([]int{5, 7, 7, 8, 8, 10}, 10)) //输出-1,最终left=6,right=5
fmt.Println(searchLast([]int{5, 7, 7, 8, 8, 10}, 6)) //输出-1,left=1,right=0
fmt.Println(searchLast([]int{5, 7, 7, 8, 8, 10}, 5)) //输出0,left=1,right=0
}
func searchLast(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)>>1
if nums[mid] > target { // 这里是 > 而不是 >=
right = mid - 1
} else {
left = mid + 1
}
}
if right == -1 || nums[right] != target { // 判断一下是否越界,或者不相等
return -1
}
return right // 这里返回 right 而不是 left
}
6.二维数组中的查找
6.1暴力解法
func findNumberIn2DArray(matrix [][]int, target int) bool {
if matrix == nil || len(matrix) == 0 || len(matrix[0]) == 0 {
return false
}
rows := len(matrix)
colums := len(matrix[0])
for i := 0; i < rows; i++ {
for j := 0; j < colums; j++ {
if matrix[i][j] == target {
return true
}
}
}
return false
}
//时间复杂度:O(nm)
//空间复杂度:O(1)
6.2左闭右闭二分查找
func searchMatrix2(matrix [][]int, target int) bool {
var m = len(matrix)
if m == 0 {
return false
}
var n = len(matrix[0])
var shortDim = min(m, n)
for i := 0; i < shortDim; i++ {
var rowFound = binarySearchRow(matrix, i, target)
var colFount = binarySearchCol(matrix, i, target)
if rowFound || colFount {
return true
}
}
return false
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func binarySearchRow(matrix [][]int, row int, target int) bool {
var left, right = row, len(matrix[0]) - 1
for left <= right {
var mid = left + (right - left) / 2
if matrix[row][mid] == target {
return true
} else if matrix[row][mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return false
}
func binarySearchCol(matrix [][]int, col int, target int) bool {
var left, right = col, len(matrix) - 1
for left <= right {
var mid = left + (right - left) / 2
if matrix[mid][col] == target {
return true
} else if matrix[mid][col] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return false
}
6.3线性查找
func findNumberIn2DArray(matrix [][]int, target int) bool {
i, j := len(matrix)-1, 0
for i >= 0 && j < len(matrix[0]) {
if target > matrix[i][j] {
j++
} else if target < matrix[i][j] {
i--
} else {
return true
}
}
return false
}
7.搜索二维矩阵
7.1暴力解法
func searchMatrix(matrix [][]int, target int) bool {
if matrix == nil || len(matrix) == 0 || len(matrix[0]) == 0 {
return false
}
rows := len(matrix)
colums := len(matrix[0])
for i := 0; i < rows; i++ {
for j := 0; j < colums; j++ {
if matrix[i][j] == target {
return true
}
}
}
return false
}
//时间复杂度是 O(n^2)
//空间复杂度是 O(1)
7.2二分查找
func searchMatrix(matrix [][]int, target int) bool {
var m, n = len(matrix), len(matrix[0])
var left, right = 0, m * n - 1
for left <= right {
// mid 是一维数组的索引
var mid = left + (right - left) / 2
// mid / n 是将一维数组的索引转成二维数组的行坐标
// mid % n 是将一维数组的索引转成二维数组的列坐标
var num = matrix[mid / n][mid % n]
if num == target {
return true
} else if num < target {
left = mid + 1
} else {
right = mid - 1
}
}
return false
}
//时间复杂度:O(logn)
//空间复杂度:O(1)
74.搜索二维矩阵