Golang語言實作 實作二分查找,二分左側查找,二分右側查找,直接貼代碼,涵蓋詳細注釋,助力徹底搞明白,不在懵逼,解決一看就懂,一寫就廢。
package algorithmProject
import (
"fmt"
"testing"
)
func TestBinarySearch(t *testing.T) {
///下标:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21
arr := []int{0,1,2,3,4,4,4,5,6,7,8,9,9,9,10,12,23,43,54,67,89}
fmt.Println("BinarySearch res: ",BinarySearch(arr, 9))
fmt.Printf("\n")
fmt.Println("BinarySearchLeftBound res: ",BinarySearchLeftBound(arr, 9))
fmt.Printf("\n")
fmt.Println("BinarySearchRightBound res: ",BinarySearchRightBound(arr, 9))
}
//普通二分查找
func BinarySearch(arr []int,lookingfor int) int {
fmt.Printf("arr height %v,lookingfor:%v \n",len(arr),lookingfor)
var low=0
var height = len(arr)-1
//為什麼是<=是因為上述 len -1 的原因
for low <= height {
/*
計算Mid的思路
如果直接用height /2 無法進行後續的計算
故需要low來臨時标記拆分之後的資料的長度,根據下述判斷條件,判斷是降低還是提升高度
*/
var mid= low + (height-low) /2
var midValue=arr[mid]
fmt.Printf("low:%v ; height:%v ; mid :%v \n ",low,height,mid)
if midValue==lookingfor {//當找到目标時則停止尋找
return mid
}else if midValue < lookingfor{
//如果大于則在中位數拆分後的右邊,故要用mid+1提升高度
low = mid + 1
}else if midValue > lookingfor{
//如果小于則在中位數拆分後的左邊,故要用mid-1降低高度
height= mid - 1
}
}
fmt.Printf("end low:%v,height:%v \n",low,height)
return -1
}
//尋找左側邊界的二分查找
func BinarySearchLeftBound(arr []int,lookingfor int) int {
fmt.Printf("arr height %v,lookingfor:%v \n",len(arr),lookingfor)
var low=0
var height = len(arr)-1
for low <= height {
/*
計算Mid的思路
如果直接用height /2 無法進行後續的計算
故需要low來臨時标記拆分之後的資料的長度,根據下述判斷條件,判斷是降低還是提升高度
*/
var mid=low + (height-low) /2
var midValue=arr[mid]
fmt.Printf("low:%v ; height:%v ; mid :%v ;midValue:%v \n ",low,height,mid,midValue)
/*
目标是查找左側邊界的數值,也就是每次拆分之後的左邊數組
假設:拆分之後的左邊數組 【9,9,10,12】
當等于或者查找的值大于目标值時,都需要降低高度 height = mid -1
再次拆分之後,查找的值小于目标值是則說明資料在右側,則進要提升左側的高度 也就是low = mid +1
最後low則是要找最左側的值
*/
if midValue==lookingfor{
height = mid -1
}else if midValue < lookingfor{
low = mid + 1
}else if midValue > lookingfor{
height = mid -1
}
}
//防止越界情況
if low >= len(arr) || arr[low] != lookingfor {
return -1
}
fmt.Printf("end low:%v,height:%v \n",low,height)
return low
}
//尋找右側邊界的二分查找
func BinarySearchRightBound(arr []int,lookingfor int) int {
fmt.Printf("arr height %v,lookingfor:%v \n",len(arr),lookingfor)
var low=0
var height = len(arr)-1
//為什麼是<=是因為上述 len -1 的原因
for low <= height {
/*
計算Mid的思路
如果直接用height /2 無法進行後續的計算
故需要low來臨時标記拆分之後的資料的長度,根據下述判斷條件,判斷是降低還是提升高度
*/
var mid=low + (height-low) /2
var midValue=arr[mid]
fmt.Printf("low:%v ; height:%v ; mid :%v ;midValue:%v \n ",low,height,mid,midValue)
/*
目标是查找右層側邊界的數值,也就是每次拆分之後的右邊數組
假設:拆分之後的右邊數組 【8,9,9,9,10,12】
當等于或者查找的值小于目标值時,都需要提升右側的高度 low = mid + 1
再次拆分之後,查找的值小于目标值是則說明資料在左側,則要降低高低 也就是 height = mid -1
最後height 則是要找最右側的值
*/
if midValue==lookingfor{
low = mid + 1
}else if midValue < lookingfor{
low = mid + 1
}else if midValue > lookingfor{
height= mid - 1
}
}
//防止越界情況
if height < 0 || arr[height]!= lookingfor{
return -1
}
fmt.Printf("end low:%v,height:%v \n",low,height)
return height
}
/*
總結:
隻要二分查找則需要通過:low 臨時變量記錄下次拆分之後的len
var mid=low + (height-low) /2
普通二分查找:
判斷等于則傳回、小于則提升low高度、大于則降低height高度
說明:二分左側、右側查找,等于不直接傳回是因不确認是否是最左或右側
二分左側查找:
等于或大于則降低height高度、小于則提升low的高度
二分右側查找:
等于或小于則提升low高度、大于則降低height的高度
*/
驗證
時間複雜度 O(log2n)
分析: 因為二分查找每次排除掉一半的不适合值,是以對于n個元素的情況:
一次二分剩下:n/2
兩次二分剩下:n/2/2 = n/4
.....
m次二分剩下:n/(2^m)
在最壞情況下是在排除到隻剩下最後一個值之後得到結果,即
n/(2^m)=1
是以由上式可得 : 2^m=n