1. 程式人生 > >查詢操作------折半查詢(二分查詢)

查詢操作------折半查詢(二分查詢)

對於無序的序列,可以使用排序來對序列中的元素排列從而使序列呈升序或降序列.而對這樣一個有序的序列來講,進行順序查詢就顯得不合時宜了.那麼在序列元素有序排列的情況下,應該如何實現排序呢?

以在圖書館查詢需要的圖書為例,當獲得需要的圖書編號後,就可以在書架上查詢這本書了,此時,是將書架上的書一本本地翻看查詢,還是放到一本書的編號後,跳過一部分書在查詢呢?顯然,大部分人都會選擇後者.

這種查詢方法就是所謂的"折半查詢"(Binary Search).

折半查詢的核心是查詢一次就將查詢的範圍減掉一半.實現方式則是將目標與序列正中間的記錄進行比較,如果相等,則查詢成功;如果目標小於中間點,則將查詢範圍縮小到中間點前面;如果目標大於中間點,則將查詢範圍縮小到中間點後面半個序列.

下面為折半查詢的程式碼實現.

int list::BinarySearch_1(const int target,int &position)//二分查詢
{
	int top = count;//把上限和下限設定好
	int bottom = 0;
	while(bottom < top){
		int mid = (top + bottom) / 2;//設定中間點
		if(data[mid] < target)
			bottom = mid + 1;
		else
			top = mid;
	}
	if(top < bottom)
		return overflow;
	else{
		position = bottom;
		if(data[position] == target)
			return success;
		else
			return overflow;
	}
}

仔細分析過後,發現上述查詢方法每次查詢後將範圍縮小一半,以此類推,直到最後將查詢範圍縮小到一個元素.此時如果剩下的元素與待查元素相等,則查詢成功,否則失敗.因此,其成本為logn.這裡n為表中的個數.

但上面的查詢方法沒有考慮到也許某一次跟中間元素進行比較時出現相等的情況,也就是說中間點元素就是要查詢的元素,此時查詢已經成功,無需再進行折半.一個"改進"的查詢方法如下.

int list::BinarySearch_2(const int target.int &position)
{
	int top = count;//把上限和下限設定好
	int bottom = 0;
	while(bottom < top){
		int mid = (top + bottom) / 2;//設定中間點
		if(data[mid] == target)		//中間點元素等於目標元素,查詢成功
		{
			position = mid + 1;
			return success;
		}
		else if(data[mid] < target)//中間點元素小於目標元素,將查詢範圍縮小到右邊半個表
			bottom = mid + 1;
		else						//中間點元素大於目標元素,將查詢範圍縮小到左邊半個表
			top = mid - 1;
	}
	return overflow;
}

改進後的查詢方法成本在最壞情況下,仍需要縮小到最後一個元素,而達到最後一個元素的過程中,每次需要進行兩次比較。因此,在最壞情況下,這個方法的成本更高。在最好的情況下,則第一次比較久命中,成本為常數,好於改進前的方法。在平均情況下,即被查詢的元素處於任意位置概率相等,則改進hoist的方法有(logn/2n+1/n)的概率好於改進前的方法,而不如改進前方法的概率為(1-logn/2n-1/n)。顯然,隨著n的增大,(logn/2n+1/n)將不斷減小,而(1-logn/2n-1/n)則不斷增大。也就是說,改進後的方法隨著n的增大,將越來越不如改進前的方法。