二分搜索以及其擴展形式

歡迎探討,若有錯誤敬請指正 html

如需轉載,請註明出處 http://www.cnblogs.com/nullzx/java


二分搜索使用的前提是數組必須有序,在本文中,咱們用lo(low)表示查找範圍的起始下標,hi(hight)表示查找範圍的結束下標,mid表示lo和hi的中間位置。數組

 

1. 通常狀況二分搜索

	/*普通二分搜索,若是找到key,返回任意一個和key相等的元素下標,不然返回-1*/
	public static int find(int[] a, int key){
		
		int lo = 0, hi = a.length - 1;
		
		while(lo <= hi){
			
			int mid = (lo + hi) / 2;
			
			if(a[mid] > key){
				hi = mid - 1;
			}else if(a[mid] < key){
				lo = mid + 1;
			}else{
				return mid;
			}
		}
		
		return -1;
	}

 如今咱們來看正常的二分搜索,咱們來討論一下若是沒有找到這這個元素時,lo和hi下標的元素值和key的大小關係。若是沒有找到key,最後一個查找的位置必定是lo == hi的位置,下標lo以前元素的必定比key小,下標hi以後元素的必定比key大。若是當前位置(即lo和hi的下標)比key大,hi減少1;若是當前位置比key小,lo增長1。總之lo會比hi大1,結束循環。若是沒有找到key,lo和hi二者之一有可能越界,hi越界時 hi爲-1,lo越界時lo爲a.length。在沒有越界的狀況下,循環結束之後,a[hi] < key,a[lo] > key。因此, 若是沒有找到這這個元素,a[hi]是小於key中最接近key的值,a[lo]是大於key中最接近key的值。spa

 

2. 最小下標二分搜索

問題:若是不存在key,返回-1, 若是存在key,返回和key相等的元素中的最小的下標。 htm

思路:若是a[mid] == key 則用lastFind記錄下mid,而後在[lo, mid-1]中繼續繼續查找,若是在這個新範圍內還能找到和key相等的元素下標,則替換lastFind,而後更新lo和hi,繼續迭代上述過程,直到lo > hi;若是沒有找到,lastFind就是最小下標 。 blog

	/*若是不存在目標元素,返回-1, 若是存在目標元素,返回和目標元素相等中下標最小的*/
	public static int findWithMinIndex(int[] a, int key){
		
		int lo = 0, hi = a.length - 1;
		
		int lastFind = -1;
		while(lo <= hi){
			
			int mid = (lo + hi)/2;
			
			if(a[mid] > key){
				hi = mid - 1;
			}else if(a[mid] < key){
				lo = mid + 1; 
			}else{
				lastFind = mid;
				hi = mid - 1;
			}
		}
		
		return lastFind;
	}

	/*上述問題的第二種實現方法
	public static int findWithMinIndex(int[] a, int target){
		int lo = 0, hi = a.length - 1;
		while(lo <= hi){
			int mid = (lo + hi)/2;
			if(a[mid] >= target){
				hi = mid - 1;
			}else{
				lo = mid + 1;
			}
		}
		
		if(lo < a.length && a[lo] == target){
			return lo;
		}else{
			return -1;
		}
	}
	*/

 同理,咱們能夠解決大於等於key的元素個數的問題。ip

 

3. 小於key的元素個數get

整個數組中的元素能夠分爲兩種,大於等於key的和小於key的。若是a[mid] >= key下一個查找的範圍是[lo, mid-1],若是a[mid] < key下一個查找的範圍是[mid+1, hi], 直到lo > hi 才退出循環。最後一個查找的位置必定是lo == hi的位置,lo下標以前的必定小於key,hi下標以後的必定大於等於key。若是當前位置(即lo和hi的下標)的元素值大於等於key,hi減少1;若是當前位置小於key小,lo增長1。因此當循環結束時,lo以前下標的元素都是小於key的,而這些元素的個數等於lo。 it

	/*返回數組元素 <key 的元素個數*/
	public static int findLessCnt(int[] a, int key){
		
		int lo = 0, hi = a.length - 1;
		
		while(lo <= hi){
			
			int mid = (lo + hi)/2;
			
			if(a[mid] >= key){
				hi = mid - 1;
			}else{
				lo = mid + 1; 
			}
		}
		
		return lo;
	}

同理,咱們能夠解決大於key的元素個數的問題。ast

 

4. 小於等於key的元素個數

整個數組中的元素能夠分爲兩種,大於key的和小於等於key的。若是a[mid] > key下一個查找的範圍是[lo, mid-1],若是a[mid] <= key下一個查找的範圍是[mid+1, hi],直到lo > hi 才退出循環。最後一個查找的位置必定是lo == hi的位置,lo下標以前的必定小於等於key,hi下標以後的必定大於key。若是當前位置(即lo和hi的下標)的元素值大於key,hi減少1;若是當前位置小於等於key小,lo增長1。因此當循環結束時,lo以前下標的元素都是小於等於key的,而這些元素的個數等於lo。

	/*返回 數組元素中 <=key 的元素個數*/
	public static int findLessEqualCnt(int[] a, int key){
		
		int lo = 0, hi = a.length - 1;
		
		while(lo <= hi){
			
			int mid = (lo + hi) / 2;
			
			if(a[mid] > key){
				hi = mid - 1;
			}else{
				lo = mid + 1;
			}
		}
		
		return  lo;
	}

同理,咱們能夠解決大於等於key的元素個數的問題。

相關文章
相關標籤/搜索