【Java】 大話數據結構(10) 查找算法(1)(順序、二分、插值、斐波那契查找)

  

本文根據《大話數據結構》一書,實現了Java版的順序查找、折半查找、插值查找、斐波那契查找html

注:爲與書一致,記錄均從下標爲1開始。java

順序表查找

順序查找

       順序查找(Sequential Search):從第一個到最後一個記錄依次與給定值比較,若相等則查找成功。算法

  順序查找優化:設置哨兵,能夠避免每次循環都判斷是否越界。在數據量不少時能提升效率。編程

  時間複雜度:O(n),n爲記錄的數。數組

如下爲順序查找算法及其優化的Java代碼:數據結構

package Sequential_Search;
/**
 * 順序表查找
 * 數組下標爲0的位置不用來儲存實際內容
 * @author Yongh
 *
 */
public class Sequential_Search {
	/*
	 * 順序查找
	 */
	public int seqSearch(int[] arr,int key) {
		int n=arr.length;
		for(int i=1;i<n;i++) {  //i從1開始
			if(key==arr[i])
				return i;
		}
		return 0;
	}
	/*
	 * 順序查找優化,帶哨兵
	 */
	public int seqSearch2(int[] arr,int key) {
		int i=arr.length-1;
		arr[0]=key;  //將arr[0]設爲哨兵
		while(arr[i]!=key)
			i--;
		return i;  //返回0說明查找失敗
	}
	
	public static void main(String[] args) {
		int[] arr = {0,45,68,32,15};
		Sequential_Search aSearch = new Sequential_Search();
		System.out.println(aSearch.seqSearch(arr, 15));
		System.out.println(aSearch.seqSearch(arr, 45));
	}	
}

 

4
1
Sequential_Search

 

有序表查找

折半查找(二分查找)

折半查找,又稱做二分查找。必須知足兩個前提:ide

1.存儲結構必須是順序存儲 
2.關鍵碼必須有序排列優化

假設數據按升序排列。從中間項與關鍵值(key)開始對比,若關鍵值(key)>中間值,則在右半區間繼續查找,反之則左半區間繼續查找。以此類推,直至找到匹配值,或者查找內無記錄,查找失敗。spa

時間複雜度:O(logn),可從二叉樹的性質4推得。code

折半查找的Java實現代碼:

package OrderedTable_Search;
/**
 * 折半查找
 * @author Yongh
 *
 */
public class BinarySearch {
	public int binarySearch(int[] arr,int n,int key) {
		int low=1;
		int high=n;
		while(low<=high) {
			int mid = (low+high)/2;
			if(arr[mid]<key) 
				low=mid+1;   //要+1
			else if(arr[mid]>key)
				high=mid-1;  //要-1
			else
				return mid;			
		}						
		return 0;
	}
	
	public static void main(String[] args) {
		int[] arr = {0,1,16,24,35,47,59,62,73,88,99};
		int n=arr.length-1;
		int key=62;
		BinarySearch aSearch = new BinarySearch();
		System.out.println(aSearch.binarySearch(arr, n, key));
	}
}

  

4
BinarySearch

 

插值查找

對於表長較大,關鍵字分佈比較均勻的查找表來講,能夠採用插值查找:

  將折半查找中代碼的第12行

  改進爲:

  改進後的第12行代碼以下:

int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]);/*插值*/

  注意:關鍵字分佈不均勻的狀況下,該方法不必定比折半查找要好。

 

斐波那契查找

  斐波那契數列以下所示:

  斐波那契查找原理與前兩種類似,僅僅改變了中間結點(mid)的位置,mid再也不是中間或插值獲得,而是位於黃金分割點附近,即mid=low+F(k-1)-1(F表明斐波那契數列),以下圖所示:

 

  對F(k-1)-1的理解:

  由斐波那契數列 F[k]=F[k-1]+F[k-2] 的性質,能夠獲得 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+。該式說明:只要順序表的長度爲F[k]-1,則能夠將該表分紅長度爲F[k-1]-1F[k-2]-1的兩段,即如上圖所示。從而中間位置爲mid=low+F(k-1)-1

  相似的,每一子段也能夠用相同的方式分割,從而方便編程。

  但順序表長度n不必定恰好等於F[k]-1,因此須要將原來的順序表長度n增長至F[k]-1。這裏的k值只要能使得F[k]-1剛好大於或等於n便可,由如下代碼獲得:

while(n>fib(k)-1)
	k++;

  順序表長度增長後,新增的位置(從n+1到F[k]-1位置),都賦爲n位置的值便可。

  時間複雜度:O(logn)

 如下爲具體的Java代碼,還有不理解的地方可看對應處的註釋:

package OrderedTable_Search;
/**
 * 斐波那契查找
 * 下標爲0位置不存儲記錄
 * 順便編寫了斐波那契數列的代碼
 * @author Yongh
 *
 */
public class FibonacciSearch {
	/*
	 * 斐波那契數列
	 * 採用遞歸
	 */
	public static int fib(int n) {
		if(n==0)
			return 0;
		if(n==1)
			return 1;
		return fib(n-1)+fib(n-2);
	}
	
	/*
	 * 斐波那契數列
	 * 不採用遞歸
	 */
	public static int fib2(int n) {
		int a=0;
		int b=1;		
		if(n==0)
			return a;
		if(n==1)
			return b;
		int c=0;
		for(int i=2;i<=n;i++) {
			c=a+b;
			a=b;
			b=c;							
		}	
		return c;
	}
	
	/*
	 * 斐波那契查找
	 */
	public static int fibSearch(int[] arr,int n,int key) {
		int low=1;	//記錄從1開始
		int high=n;     //high不用等於fib(k)-1,效果相同
		int mid;
		
		int k=0;
		while(n>fib(k)-1)	//獲取k值
			k++;
		int[] temp = new int[fib(k)];	//由於沒法直接對原數組arr[]增長長度,因此定義一個新的數組
		System.arraycopy(arr, 0, temp, 0, arr.length); //採用System.arraycopy()進行數組間的賦值
		for(int i=n+1;i<=fib(k)-1;i++)    //對數組中新增的位置進行賦值
			temp[i]=temp[n];  
		
		while(low<=high) {
			mid=low+fib(k-1)-1;
			if(temp[mid]>key) {
				high=mid-1;
				k=k-1;  //對應上圖中的左段,長度F[k-1]-1
			}else if(temp[mid]<key) {
				low=mid+1;
				k=k-2;  //對應上圖中的右端,長度F[k-2]-1
			}else {
				if(mid<=n)
					return mid;
				else
					return n;		//當mid位於新增的數組中時,返回n		
			}							
		}
		return 0;
	}
	public static void main(String[] args) {
		int[] arr = {0,1,16,24,35,47,59,62,73,88,99};
		int n=10;
		int key=59;
		System.out.println(fibSearch(arr, n, key));  //輸出結果爲:6
	}
}
相關文章
相關標籤/搜索