折半查詢,也叫二分查詢,當在一個陣列或集合中查詢某個元素時,先定位出中間位置元素,如果要查詢的元素正好和該中間位置元素相等,通過一次查詢,就能找到匹配元素;如果要查詢的元素小於該中間位置元素,就拋棄後面一半的元素,在前面一半的元素中再定位出中間位置元素,如此反覆,直到找到匹配元素;如果要查詢的元素大於該中間位置元素,就拋棄前面一半的元素,在後面一半的元素中定位出中間位置元素,如此反覆。
面臨的第一個問題是:中間位置元素如何定位?在折半查詢中規定:當元素個數是奇數,比如有3個元素,中間位置元素是索引為1的元素;當元素個數是偶數,比如有4個元素,索引為1和2的元素理論都是中間位置元素,但在折半查詢中,把後面這個,即索引為2的元素視為中間位置元素。
面臨的第二個問題是:由於,要查詢的元素和中間位置元素之間需要比較,我們在比較之前,勢必要讓陣列按升序或降序來排列。
自定義一個類,該類維護著一個int[]型別陣列,通過建構函式確定陣列長度和對陣列進行排序,並提供列印陣列元素的方法,以及折半演算法。
public class MyArray{private int[] arr;//內部維護著一個陣列private static Random r = new Random();//用它來生成陣列的隨機元素//通過建構函式來確定內部陣列的長度,並生成陣列的隨機元素,並對陣列元素排序
public MyArray(int size){arr = new int[size];for (int i = 0; i < size; i++){arr[i] = r.Next(1, 100);}Array.Sort(arr);}//折半查詢演算法 返回查詢元素的索引位置
public int HalfSearch(int key){int low = 0;//低點,初始值設定成最低點,即索引0int high = arr.Length - 1;//高點,初始值設定成最高點,即索引陣列最後一個位置//如果陣列元素個數是偶數,比如4個,索引2和3都是中間位置,用以下演算法中間位置指向索引3
//如果陣列元素個數是奇數,比如3個,索引1是中間位置,用以下演算法中間位置指向索引1
int middle = (low + high + 1)/2;
int location = -1;//找不到就返回-1//迴圈,找不到就一直查詢
do
{//每次迴圈,把低點和高點位置的元素列印出來
Console.WriteLine(PrintSectionElements(low, high));Console.WriteLine();//如果要查詢元素是中間位置的元素,就返回中間位置這個索引
if (key == arr[middle])
{location = middle;}else if (key < arr[middle]) //如果要查詢元素小於中間位置元素,把中間位置前面的索引設為高點{high = middle - 1;}else//如果要查詢元素大於中間位置元素,把中間位置後面的索引設為低點{low = middle + 1;}//如果程式碼執行到此處,說明還沒有找到匹配元素,由於以上重新設定了低點或高點,所以這裡也要重新設定中間位置
middle = (low + high + 1)/2;} while ((low <= high) && (location == -1));
return location;
}//列印2個位置間的陣列元素
public string PrintSectionElements(int low, int high){string temp = "";
//對於2個位置間的元素列印出元素並跟上一個空格位置
for (int i = low; i <= high; i++){temp += arr[i] + " ";
}return temp;
}//重寫ToString方法,把陣列所有的元素都列印出來
public override string ToString(){return PrintSectionElements(0, arr.Length - 1);
}}
以上,折半查詢的目的是找到匹配元素在陣列中的索引位置,為此,通過需查詢元素和中間位置元素大小的比較,不斷地調整低點、高點和中間位置。另外,在C#中,1/2的結果是0。
客戶端。
class Program
{private static int key; //需查詢元素private static int position;//匹配元素所在的位置static void Main(string[] args){MyArray myArray = new MyArray(7);
//把所有元素列印出來
Console.WriteLine(myArray);Console.WriteLine("請輸入需要查詢的元素或輸入-1退出 ");
key = Convert.ToInt32(Console.ReadLine());Console.WriteLine();while (key != -1)
{//呼叫折半演算法得出所需查詢元素的位置
position = myArray.HalfSearch(key);if (position == -1) //說明沒有找到需要匹配的元素{Console.WriteLine("沒有找到元素{0}", key);
}else
{Console.WriteLine("在{0}號位置查到元素{1}", position, key);
}Console.WriteLine("請輸入需要查詢的元素或輸入-1退出 ");
key = Convert.ToInt32(Console.ReadLine());Console.WriteLine();}}}
用時間複雜度來描述折半查詢的效率
假設一個陣列有1023個元素,由於可以表示成"1023=2ⁿ-1"等式,所有n=10。對該陣列每進行一次折半,相當於除以2,也就是說,在該陣列中查詢某個元素,最多需要10次,就可以查到匹配元素。
對於"2ⁿ-1"這個表示式,當n=30,就表示10億,使用折半查詢,10億個元素最多需要30次就能找到匹配元素。而使用線性查詢需要平均5億次的查詢。2種演算法的效率由此可見一斑。
把折半查詢、二分查詢的時間複雜度寫成O(log n),稱為"對數執行時間",讀為"對數階"。
“查詢與排序”系列包括: