常規有序數組二分查找和旋轉有序數組二分查找—Java實現

常規有序數組二分查找

二分查找又稱折半查找(Binary Search),是一種效率較高的查找方法。二分查找要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列。

查找過程:首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查找關鍵字比較,如果兩者相等,則查找成功;否則利用中間位置記錄將表分成前、後兩個子表,如果中間位置記錄的關鍵字大於查找關鍵字,則進一步查找前一子表,否則進一步查找後一子表。重複上述過程,直到找到滿足條件的記錄,使查找成功,或直到子表不存在爲止,此時查找不成功。 

舉例說明:現在我有1到50的數字(這一組數字不一定是連續整數,但一定是有序排列),隨機選取其中一數(假設爲35),你來猜到我所選擇的數字,每次猜測後,我會告訴你大了,小了,或者對了。常規思路是從1一直猜到35,這種簡單查找最大的問題是假設我隨機選中的數字爲50,那麼你可能需要從1猜到50了!假設隨機選取的數爲n,則簡單查找需要n步,顯然時間計算成本太高,那麼有沒有更好的查找方式呢?

答案當然是有的,接下來我們就來聊一聊二分查找,還是剛剛的1到50的數,同樣需要你查找35,首先將表{1,2,3,⋯⋯50}分成前後兩個子表{1,2,3,⋯⋯25}和{26,27,28,⋯⋯50},25即爲關鍵字,你猜25,我告訴你“小了”,這樣就排除了一半,你再將{26,27,28,⋯⋯50}分成兩個字表{26,27,28,⋯⋯37}和{38,39,40⋯⋯50},此時37及爲關鍵字,你猜37,我告訴你“大了”,這樣就又排除了一半,再將{26,27,28,⋯⋯37}分成兩個字表{26,27,28,⋯⋯31}和{32,33,34⋯⋯37},,此時31及爲關鍵字,你猜31,我告訴你“小了”,如此重複,直至查找到35爲止。用二分查找最多需要logn步。

Java實現(使用遞歸):

public static int BinarySearch(int[] arr,int target,int start,int end) {
	if(target<arr[start]||target>arr[end]||start>end) {
		return -1;
	}
	//middle=(start+end)/2有可能溢出
	int middle=start+(end-start)/2;
	if(arr[middle]>target) {
		return BinarySearch(arr,target,start,middle-1);
	}
	else if(arr[middle]<target) {
		return BinarySearch(arr,target,middle+1,end);
	}
	else {
		return middle;
	}
}

測試代碼:

public static void main(String[] args) {
	int[] arr= {2,4,6,8,12,16,19,22,25,26,29,30,36,39,47,58,66,71,77,88};
	int target=39;
	System.out.print(BinarySearch(arr,target,0,arr.length-1));
}

Java實現(不使用遞歸):

public static int BinarySearch(int[] arr,int target) {
	int start=0;
	int end=arr.length-1;
	int middle=0;
	if(target<arr[start]||target>arr[end]||start>end) {
		return -1;
	}
	while(start<=end) {
		//middle=(start+end)/2有可能溢出
		middle=start+(end-start)/2;
		if(target<arr[middle]) {
			end=middle-1;
		}
		else if(target>arr[middle]) {
			start=middle+1;
		}
		else {
			return middle;	
		}
	}
	return -1;
}

測試代碼:

public static void main(String[] args) {
	int[] arr= {2,4,6,8,12,16,19,22,25,26,29,30,36,39,47,58,66,71,77,88};
	int target=39;
	System.out.print(BinarySearch(arr,target));
}

到這裏是不是感覺問題迎刃而解了,但可別高興的太早,二分查找還有許多變化可以挖掘,例如我們剛剛研究的只是常規有序數組,但如果變成了旋轉有序數組呢?所謂旋轉有序數組,舉個栗子你就明白了,原數組:{2,4,6,8,12,16,19,22,25,26},旋轉數組:{12,16,19,22,25,26,2,4,6,8},這樣的旋轉數組還有很多,下面我們就來聊聊如何對旋轉有序數組進行二分查找吧!

旋轉有序數組二分查找

數組:int[] arr={12,16,19,22,25,26,2,4,6,8}

查找:int target=22

查找過程:每次根據middle=start+(end-start)/2求出middle後,middle的左邊數組爲[start, middle], middle的右邊數組爲[middle+1, end],這兩個數組中至少有一個是有序數組。將arr[middle]與target進行比較:

(1) arr[middle] ==target,此時要查找的數正好是arr[middle],因此返回middle;
(2) arr[middle] < arr[end],此時至少可說明右邊數組爲有序數組,當arr[middle] < target 時,則start = middle + 1,否則 end= middle - 1;
(3) arr[middle] > arr[start],此時至少可說明左邊數組爲有序數組,當arr[middle] > target 時,則end= middle - 1,否則 start = mid + 1;

Java實現:

public static int BinarySearch(int[] arr,int target) {
	int start=0;
	int end=arr.length-1;
	while(start<end) {
		//middle=(start+end)/2有可能溢出
		int middle=start+(end-start)/2;
		if(target==arr[middle]) {
			return middle;
		}
		//如果左邊數組爲有序數組
		if(arr[middle]>arr[start]) {
			//判斷target是否在左邊數組,如果在,則繼續遍歷左邊數組,如果不在,則遍歷右邊數組;
			if(target<arr[middle]) {
				end=middle-1;
			}else {
				start=middle+1;
			}
		}
		//如果右邊數組爲有序數組
		if(arr[middle]<arr[end]) {
			//判斷target是否在右邊數組,如果在,則繼續遍歷右邊數組,如果不在,則遍歷左邊數組;
			if(target>arr[middle]) {
				start=middle+1;
			}else {
				end=middle-1;
			}
		}
	}
	return -1;
}

測試代碼:

public static void main(String[] args) {
	int[] arr= {12,16,19,22,25,26,2,4,6,8};
	int target=26;
	System.out.print(BinarySearch(arr,target));
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章