天天看點

Golang 實作二分查找,二分左側查找,二分右側查找 看完助你徹底搞明白!!

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的高度
*/           

驗證

Golang 實作二分查找,二分左側查找,二分右側查找 看完助你徹底搞明白!!

時間複雜度 O(log2n)

分析: 因為二分查找每次排除掉一半的不适合值,是以對于n個元素的情況:

一次二分剩下:n/2

兩次二分剩下:n/2/2 = n/4

.....

m次二分剩下:n/(2^m)

在最壞情況下是在排除到隻剩下最後一個值之後得到結果,即

n/(2^m)=1

是以由上式可得 : 2^m=n

總結