1. 程式人生 > 其它 >查詢演算法的Java實現

查詢演算法的Java實現

技術標籤:演算法演算法java

夏天的時候學習網上的課程實現的幾個查詢演算法,放這兒方便檢視

package com.zhangyan.algorithm;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class MySearch {

	/**
	 * 線性查詢,就是遍歷一下
	 */
	/**
	 * 二分查詢演算法實現
	 * 需要時排好序的數列,然後折半的查詢
	 */
	public static List<Integer> dichotomySearch(int[] arr,int element){
		return (element<arr[0]||element>arr[arr.length-1])?null:dichotomySearch_0(arr, 0, arr.length-1, element);
	}
	/**
	 * 二分查詢遞迴實現  
	 * @param arr
	 * @param left
	 * @param right
	 * @param element
	 * @return
	 */
	private static List<Integer> dichotomySearch_0(int[] arr,int left,int right,int element) {
		if(left>right) {
			return null;
		}
		int mid = (left+right)/2;//中間索引值
		if(arr[mid]<element) {
			//以升序算遞迴右邊
			return dichotomySearch_0(arr, mid+1, right, element);
		}else if(arr[mid]>element) {
			//遞迴到左邊
			return dichotomySearch_0(arr, left, mid-1, element);
		}else {
			LinkedList<Integer> list = new LinkedList<Integer>();
			list.addFirst(mid);
			int i = mid+1;//向後的指標
			int j = mid-1;//向前的指標
			while(i<arr.length&&arr[i]==element) {
				list.addLast(i);
				i++;
			}
			while(j>=0&&arr[j]==element) {
				list.addFirst(j);
				j--;
			}
			return list;
		}
	}
	//二分查詢的非遞迴實現
	public static int dichotomySearchNon_recursive(int[] arr,int target) {
		int left = 0;
		int right = arr.length-1;
		while(left<=right) {
			int mid = (left+right)/2;
			if(arr[mid]==target) {
				return mid;
			}else if(arr[mid]>target) {
				right = mid-1;
			}else {
				left = mid+1;
			}
		}
		return -1;
	}
	//插值查詢演算法實現
	/**
	 * 插值查詢演算法也要求數列是有序的
	 * 當在資料分佈較為均勻的數列中,使用死板的二分查詢會有很多浪費的步驟,此時使用插值查詢較為合適
	 * 插值查詢相較於二分查詢,它的中軸值是根據要查詢的數值大小動態調整,可以更快的找到目標值
	 * @param arr 在此陣列中查詢
	 * @param element 要查詢的值
	 */
	public static List<Integer> insertValueSearch(int[] arr,int element) {
		return (element<arr[0]||element>arr[arr.length-1])?null:insertValueSearch_0(arr,0,arr.length-1,element);
	}
	//遞迴實現插值查詢
	/**
	 * 插值查詢求中軸索引的公式:int mid = left+(right-left)*(element-arr[left])/(arr[right]-arr[left])
	 * 盡然這麼快就找到了目標數,很神奇哦
	 * @param arr
	 * @param left
	 * @param right
	 * @param element
	 */
	private static List<Integer> insertValueSearch_0(int[] arr,int left,int right,int element) {
		System.out.println("~~~~~~~~~~~~~~~");
		if(left>right) {//退出條件
			return null;
		}
		//中軸值
		int mid = left+(right-left)*(element-arr[left])/(arr[right]-arr[left]);
		if(element>arr[mid]) {//向右查詢
			return insertValueSearch_0(arr,mid+1,right,element);
		}else if(element<arr[mid]) {//向左查詢
			return insertValueSearch_0(arr,left,mid-1,element);
		}else {//找到,並迴圈找到所有
			LinkedList<Integer> list = new LinkedList<>();
			list.addFirst(mid);
			int i = mid+1;//向左查詢
			int j = mid-1;//向右查詢
			while(element==arr[i]) {
				list.addLast(i);
				i++;
			}
			while(element==arr[j]) {
				list.addFirst(j);
				j--;
			}
			return list;
		}
	}
	
	/*
	 * 斐波那契查詢演算法
	 * 也即黃金分割演算法,是利用斐波那契數列找到黃金分割點,作為mid
	 * 斐波那契數列定義{1,1,2,3,5,8,13,21,34,55},每個數都是前兩個數之和
	 * F[k] = F[k-1] + F[k-2] => (F[k]-1) = (F[k-1]-1)+(F[k-2]-1)+1
	 * 如果陣列長度為F[k]-1,則可以將其分為F[k-1]-1和F[k-2]-1兩段,至於那個1,就是mid佔得索引位置,單純是我的想法
	 * 所以黃金分割的mid = low + F[k-1]-1,如果從陣列頭開始low為0,但如果是中間開始的話必須加low值
	 */
	public static List<Integer> fibonacciSearch(int[] arr,int element) {
		int low = 0;
		int high = arr.length-1;
		int mid = 0;
		int k = 0;//要查詢陣列需滿足的斐波那契數列中的數值索引
		//構建能滿足斐波那契數值的陣列,不滿的用陣列的最後元素補齊
		int[] f = fib();
		while(f[k]<=arr.length) {
			k++;
		}
		//以計算出來的斐波那契數值拷貝一份新陣列
		int[] temp = Arrays.copyOf(arr, f[k]);
		//多出的部分用最後一位數值填充
		for(int j=arr.length;j<temp.length;j++) {
			temp[j] = arr[arr.length-1];
		}
		while(low<=high) {
			System.out.println("~~~~~~~~~~~~");
		mid = low + f[k-1]-1;
		if(temp[mid]>element) {
			high = mid-1;
			k--;
		}else if(temp[mid]<element) {
			low = mid+1;
			k-=2;
		}else {
			LinkedList<Integer> list = new LinkedList<Integer>();
			//之所以要判斷是因為為了滿足斐波那契數列條件尾部有填充的元素
			//判斷返回原本最後一位數值的索引
			if(high>mid) {
				int i = mid+1;//向左
				int j = mid-1;//向右
				while(i<=high&&arr[i]==element) {
					list.addLast(i);
					i++;
				}
				list.addFirst(mid);
				while(j>=low&&arr[j]==element) {
					list.addFirst(j);
					j--;
				}
				return list;
			}else {
				int m = high-1;
				list.add(high);
				while(m>=low&&arr[m]==element) {
					list.addFirst(m);
					m--;
				}
				return list;
			}
		}
		}
		return null;
	}
	//設定斐波那契數列的數列長度
	public static int maxLength = 20;
	/*
	 * 構建斐波那契數列的方法
	 */
	private static int[] fib() {
		int[] f = new int[maxLength];
		f[0] = 1;
		f[1] = 1;
		for(int i=2;i<maxLength;i++) {
			f[i] = f[i-1]+f[i-2];
		}
		return f;
	}
	//-----------------------------------------------------------------------
	public static void main(String[] args) {
		int[] data = {21,11, 3,21, 6, 2,21, 33, 9, 1, 63,63,63,63,63,63,63,21, 45};
		Arrays.parallelSort(data);
		System.out.println(fibonacciSearch(data, 1));
	}
}