1. 程式人生 > 實用技巧 >資料結構篇--查詢演算法

資料結構篇--查詢演算法

一 總敘

相比排序,查詢簡單很多。

常用的查詢演算法有四種,分別是

  1)順序(線性)查詢   //也就是一個一個找過去

  2)二分查詢/折半查詢   //前提必須是有序陣列,每次都從待查陣列的中間找起

  3)插值查詢   //與二分查詢類似,前提同樣得是有序陣列,mid值更改

  4)斐波那契(黃金分割法)查詢演算法   //與二分法類似,前提得是有序陣列,mid值選擇黃金分割點

二 順序查詢

  就是通過遍歷陣列,逐個得與數值相比較,當相同時返回下標,若沒有找到則返回-1.

  程式碼如下

  

public class Sequential_Search {
        public static
void main(String[] args) { int[] arr = {15,4,3,2,8,1,6,74,8,1,20,4}; System.out.println(Arrays.toString(arr)); int j = Sequential_Search.SequentialSearch(arr,1); if(j ==-1){ System.out.println("沒有找到該數值"); }
else{ System.out.println("找到該數值,在陣列中的索引位置為"+j); } } public static int SequentialSearch(int[] arr,int value){ for (int i = 0; i < arr.length; i++) { if(arr[i]==value){ return
i; } } return -1; } }

三 二分查詢

  mid = (left+right)/2 ,初始時left=0,right=arr.length-1,將待比較的數值與陣列中間的數值相比較,並以此來判斷區間在左邊還是右邊。迴圈往復。

//二分查詢 前提是有序陣列
public class Binary_Search {
        public static void main(String[] args) {
                int[] arr = {1,2,3,4,6,7,8,9,10,10,10,10,10,101,1011,12,13};
                List<Integer> integers = Binary_Search.binartSearch(arr, 10, arr.length - 1, 0);
                for (Integer integer : integers) {
                        System.out.print(integer);
                        System.out.print(" ");
                }
                /*int j = Binary_Search.binartSearch(arr,10,arr.length-1,0);
                if(j==-1){
                        System.out.println("沒有這個數");
                }
                else{
                        System.out.println("這個數字的下標為"+j);
                }*/
                
        }
/*        public static int binartSearch(int[] arr,int value,int right,int left){
                if(left>right){
                        return -1;
                }
                int mid = (right+left)/2;
                int midValue = arr[mid];
                if(value>midValue){
                        return binartSearch(arr,value,right,mid+1);
                }else if(value<midValue){
                        return binartSearch(arr,value,mid-1,left);
                }
                return mid;
        }*/
        //將有序陣列中所有滿足條件的數值下標全都取出來
        public static List<Integer> binartSearch(int[] arr, int value, int right, int left){
                if(left>right){
                        return new ArrayList<Integer>();
                }
                int mid = (right+left)/2;
                int midValue = arr[mid];
                if(value>midValue){
                        return binartSearch(arr,value,right,mid+1);
                }
                else if(value<midValue){
                        return binartSearch(arr,value,mid-1,left);
                }
                else{
                        List<Integer> list = new ArrayList<Integer>();
                        list.add(mid);
                        int temp = mid-1;
                        while(temp>=0&&arr[temp]==value){
                                list.add(temp);
                                temp-=1;
                        }
                        temp=mid+1;
                        while(temp<=arr.length&&arr[temp]==value){
                                list.add(temp);
                                temp+=1;
                        }
                        return list;
                }
        }
        }

四插值查詢

  與二分查詢類似,不過不是取得中間值進行比較,而是通過公式mid = left+(right-left)*(value-arr[left])/(arr[right]-arr[left])來獲取中間值。

public class Interpolation_Method {
        public static void main(String[] args) {
                int[] arr =new int[100];
                for (int i = 0; i < 100; i++) {
                        arr[i]=i;
                }
                int j = Interpolation_Method.insertSort(arr,99,0,arr.length-1);
                if(j==-1){
                        System.out.println("未找到該數值");
                }
                else{
                        System.out.println("該數值在陣列中的位置是"+j);
                }
        }
        public static int insertSort(int[] arr,int value,int left,int right){
               if(left>right || value>arr[arr.length-1] || value<arr[0]){
                       return -1;
               }
               int mid = left+(right-left)*(value-arr[left])/(arr[right]-arr[left]);
               int midValue = arr[mid];
                if (value>midValue){
                        return insertSort(arr,value,mid+1,right);
                }
                if(value<midValue){
                        return insertSort(arr,value,left,mid-1);
                }
                else return mid;
        }
}

五斐波那契(黃金分割法)查詢演算法

  這部分的程式碼來自https://blog.csdn.net/interesting_code/article/details/104322118

package com.cl.Search_Slgorithm;

import java.util.Arrays;

public class Fibonacci_Searches {
        
        public static int maxSize = 20;
        public static void main(String[] args) {
                int [] arr = {1,8, 10, 89,189,1000, 1234};
                
                System.out.println("index=" + fibSearch(arr, 189));// 0
                
        }
        
        //因為後面我們mid=low+F(k-1)-1,需要使用到斐波那契數列,因此我們需要先獲取到一個斐波那契數列
        //非遞迴方法得到一個斐波那契數列
        public static int[] fib() {
                int[] f = new int[maxSize];
                f[0] = 1;
                f[1] = 1;
                for (int i = 2; i < maxSize; i++) {
                        f[i] = f[i - 1] + f[i - 2];
                }
                return f;
        }
        
        //編寫斐波那契查詢演算法
        //使用非遞迴的方式編寫演算法
        /**
         *
         * @param a  陣列
         * @param key 我們需要查詢的關鍵碼(值)
         * @return 返回對應的下標,如果沒有-1
         */
        public static int fibSearch(int[] a, int key) {
                int low = 0;
                int high = a.length - 1;
                int k = 0; //表示斐波那契分割數值的下標
                int mid = 0; //存放mid值
                int f[] = fib(); //獲取到斐波那契數列
                //獲取到斐波那契分割數值的下標
                while(high > f[k] - 1) {
                        k++;
                }
                //因為 f[k] 值 可能大於 a 的 長度,因此我們需要使用Arrays類,構造一個新的陣列,並指向temp[]
                //不足的部分會使用0填充
                int[] temp = Arrays.copyOf(a, f[k]);
                //實際上需求使用a陣列最後的數填充 temp
                //舉例:
                //temp = {1,8, 10, 89, 1000, 1234, 0, 0}  => {1,8, 10, 89, 1000, 1234, 1234, 1234,}
                for(int i = high + 1; i < temp.length; i++) {
                        temp[i] = a[high];
                }
                
                // 使用while來迴圈處理,找到我們的數 key
                while (low <= high) { // 只要這個條件滿足,就可以找
                        mid = low + f[k - 1] - 1;
                        if(key < temp[mid]) { //我們應該繼續向陣列的前面查詢(左邊)
                                high = mid - 1;
                                //為甚是 k--
                                //說明
                                //1. 全部元素 = 前面的元素 + 後邊元素
                                //2. f[k] = f[k-1] + f[k-2]
                                //因為 前面有 f[k-1]個元素,所以可以繼續拆分 f[k-1] = f[k-2] + f[k-3]
                                //即 在 f[k-1] 的前面繼續查詢 k--
                                //即下次迴圈 mid = f[k-1-1]-1
                                k--;
                        } else if ( key > temp[mid]) { // 我們應該繼續向陣列的後面查詢(右邊)
                                low = mid + 1;
                                //為什麼是k -=2
                                //說明
                                //1. 全部元素 = 前面的元素 + 後邊元素
                                //2. f[k] = f[k-1] + f[k-2]
                                //3. 因為後面我們有f[k-2] 所以可以繼續拆分 f[k-1] = f[k-3] + f[k-4]
                                //4. 即在f[k-2] 的前面進行查詢 k -=2
                                //5. 即下次迴圈 mid = f[k - 1 - 2] - 1
                                k -= 2;
                        } else { //找到
                                //需要確定,返回的是哪個下標
                                if(mid <= high) {
                                        return mid;
                                } else {
                                        return high;
                                }
                        }
                }
                return -1;
        }
}