(Java實現)查找算法---線性、折半、插值、斐波那契

查找算法

線性查找

  • 基本思路:線性查找是最簡單的查找方法,也很好理解,就是對一組有序/無序的序列進行遍歷,逐個比較,直到找到對應的值
  • 代碼以下
public class LinearSearch {
    /**
 * * @param arr 要查找的數組
 * @param findVal 要查找的值
 * @return 返回對應值的下標,若是沒有返回-1
 */ public static int linearSearch(int[] arr, int findVal){
        if (arr == null || arr.length <= 0 ) return -1;
 for (int i = 0; i < arr.length; i ++){
            if (arr[i] == findVal)
                return i;
 }
        return -1;
 }
    public static void main(String[] args) {
        int[] arr = {128,321,23,4,19,23,10,98,100};
 int result = linearSearch(arr, 10);
 System.out.println(result);
 }
}

折半查找

  • 介紹:折半查找也叫二分查找,要實現折半查找必須知足兩個要求,第一是數列要有序的,第二是存放數列的容器是按序存放的
  • 基本思路算法

    • 1)肯定數組的中間下標mid,等於左邊下標left+右邊下標right的一半,mid = (left + right) / 2
    • 2)把要找的值findVal和下標mid對應的值midVal進行比較數組

      • 若是findVal大於midVal,則代表要找的數在右邊,向右查找,此時的left爲mid + 1,right不變,從新計算mid
      • 若是findVal小於midVal,則代表要找的數在左邊,向左查找,此時的right爲mid - 1,left不變,從新計算mid
      • 若是上面兩個條件都不知足,證實findVal等於midVal,則直接返回mid下標
    • 3)若是說left > right,證實咱們要找的數不存在,直接返回-1
  • 舉例說明:好比有序數組{1,8,9,10,29,39,49,69},查找39spa

    • image如上圖所示,第一次並無找到findVall,第二次left,mid從新調整事後,找到了findVal
  • 代碼以下code

    • 這裏使用的是遞歸的方式實現折半查找
/**
 * 使用折半查找的前提是該數組是有序的
 */
public class BinarySearch {
    public static int binarySearch(int[] arr, int findVal){
        if (arr == null || arr.length <= 0) return -1;
 return binarySearch(arr, 0, arr.length - 1, findVal);
 }
    public static int binarySearch(int[] arr, int left, int right, int findVal){
        if (left > right) return -1;
 int mid = (left + right) / 2;
 int midVal = arr[mid];
 if (findVal < midVal){
            return binarySearch(arr, left, mid - 1, findVal);
 }else if (findVal > midVal){
            return binarySearch(arr, mid + 1, right, findVal);
 }else {
            return mid;
 }
    }
    public static void main(String[] args) {
        int[] arr = {1,4,9,29,98,100,989};
 int result = binarySearch(arr, 10);
 System.out.println(result);
 }
}

插值查找

  • 介紹:插值查找是相似於折半查找,不一樣的地方在於mid的計算不一樣,折半查找是每次從取中間值,而插值查找每次從自適應mid處開始查找
  • 基本思路blog

    • 與折半查找相同,mid的計算方式不一樣
    • mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left])
  • 舉個例子:好比有序數組{1,8,9,10,29,39,49,69},查找39遞歸

    • image如上圖所示,經歷了三次查找才找到了findVal
    • 這裏用的例子因爲數組裏面的整數比較沒有規律,因此通過了三次查找才找到,而折半查找只用了兩次就找到了。可是若是是對於一組有規律的有序序列來講,查找的次數每每只須要一次(你們能夠試一下從1-100裏面去查找一個數)
  • 代碼以下ci

    • 這裏也是採用遞歸的方式實現,注意看一下下面的註釋
public class InsertSearch {
    public static int insertSearch(int[] arr, int findVal){
        if (arr == null || arr.length <= 0) return -1;
 return insertSearch(arr, 0, arr.length - 1, findVal);
 }
    /**
 ** @param arr 有序數組
 * @param left 左邊的下標
 * @param right 右邊的下標
 * @param findVal 要找的值
 * @return findVal對應的下標,若是找不到則返回-1
 */ public static int insertSearch(int[] arr, int left, int right, int findVal){
        //如下三個判斷條件必須寫
 //第一個:當找不到的時候left會大於right
 //第二個和第三個:若是說要找的值不在該數組的最大值和最小值的範圍裏(對於升序的數組來講)
 // 那麼若是不加這兩個條件,可能會致使最後算出來的mid不在left和right之間
 if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) return -1;
 int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
 int midVal = arr[mid];
 if (findVal > midVal){
            return insertSearch(arr, mid + 1, right, findVal);
 }else if (findVal < midVal){
            return insertSearch(arr, left, mid - 1, findVal);
 }else {
            return mid;
 }
    }
    public static void main(String[] args) {
        int[] arr = {1,8,9,10,29,39,49,69};
 System.out.println(Arrays.toString(arr));
 int result = insertSearch(arr, 39);
 System.out.println(result);
 }
}

斐波那契查找

  • 介紹:利用斐波那契數列找到數組中的黃金分割點it

    • 黃金分割點:是指把一條線段分割爲兩部分,使其中一部分與全長之比等於另外一部分與這部分之比。取其前三位數字的近似值是0.618。
    • 斐波那契數列中,兩個相鄰數的比例是無限接近與黃金分割值0.618
    • 斐波那契數列計算公式:F(k) = F(k-1) + F(k-2),F(1)=1,F(2)=2
  • 基本思路:class

    • 該查找算法與折半查找相似,區別也是在於mid的計算,mid是取位於黃金分割點附近,即mid=low+F(k-1)-1
  • F(k-1)是什麼?容器

    • 由F(k)計算公式的性質,能夠獲得F(k)-1 = (F(k-1) - 1) + (F(k-2) - 1) + 1
    • 也就是說,只要數組的長度爲F(k)-1,就能夠將該表分紅長度爲F(k-1) - 1 和F(k-2) - 1兩段
    • 而中間位置mid = low + F(k - 1) - 1
  • 是否須要擴容數組:數組的長度n不必定等於F(k),因此須要將原來的數組長度n增長至F(k),不必定要徹底相等,只要比F(k)大就行,因此當n增大後就須要進行擴容
  • K如何肯定? 代碼以下:
while(n > F(k))
    k++
  • 舉個例子:好比有序數組{1,8,9,10,29,39,49,69},查找39

    • 1)首先判斷是否須要擴容,n爲8,k爲5,F(5)=8,n恰好等於5,不須要擴容
    • 2)第一輪,根據公式計算出mid=4,arr[mid]=29,小於39,向右查找

      • left = mid + 1 = 5
      • right = 7
      • k = k - 2 = 3(爲何須要k-2,看下面的解釋)
      • mid = 6image
    • 3)第二輪,arr[mid] = arr[6] = 49,大於39,向左查找

      • left = 5
      • right = mid - 1 =6
      • k = k - 1 =2(爲何須要k-1,看下面的解釋)
      • mid = 5image
    • 4)第三輪,arr[mid] = arr[5] = 39,等於39,找到了image
  • k -= 2和 k --的含義

    • image如上圖所示,f(k)-1能夠分紅兩段,分別是f(k-1)-1和f(k-2)-1
    • 當要找的數比arr[mid]小,咱們就把f(k)-1的長度縮減成f(k-1)-1,因此此時k--
    • 當要找的數比arr[mid]大,咱們就把f(k)-1的長度縮減成f(k-2)-1,因此此時k -= 2
  • 代碼以下
public class FibonacciSearch {
    private static int maxSize = 100;
 private static int count = 0;
 private static int[] fib(){
        int[] f = new int[maxSize];
 f[0] = 1;
 f[1] = 1;
 for (int i = 2; i <= maxSize - 1; i ++){
            f[i] = f[i - 1] + f[i - 2];
 }
        return f;
 }
    //斐波那契查找算法
 public static int fibSearch(int[] arr, int findVal){
        return fibSearch(arr, 0, arr.length - 1, findVal);
 }
    /**
 * * @param arr 有序數組
 * @param low 數組左邊的下標
 * @param high 數組右邊的下標
 * @param findVal 要查找的值
 * @return 找到的值對應的下標,若是沒有返回-1
 */ public static int fibSearch(int[] arr, int low, int high, int findVal){
        if (arr == null || arr.length <= 0 ) return - 1;
 int k = 0; //表示斐波那契數列的下標
 int[] f = fib(); //獲取斐波那契數列
 int mid = 0; //mid值
 //獲取斐波那契分割數值
 while (high > f[k] - 1){
            k ++;
 }
        //把k與n進行比較,若是n小就須要進行擴容
 int[] temp = Arrays.copyOf(arr, f[k]);
 //把擴容多出來的數賦予原來arr的最後一個數據
 for (int i = high + 1; i < temp.length; i ++){
            temp[i] = arr[high];
 }
        System.out.println("查找以前的工做:");
 System.out.println("t擴容後的數組arr:" + Arrays.toString(temp));
 System.out.println("tleft:" + low);
 System.out.println("tright:" + high);
 System.out.println("tk:" + k);
 //使用while循環,找到咱們的數Key
 while (low <= high){
            //設置中間點
 mid = low + f[k-1] - 1;
 System.out.println("這是第" + ++count +"輪:");
 System.out.println("tleft:" + low);
 System.out.println("tright:" + high);
 System.out.println("tk:" + k);
 System.out.println("tf[k-1]-1:" + (f[k-1] -1));
 System.out.println("tmid:" + mid);
 if (findVal < temp[mid]){
                high = mid - 1;
 //這裏爲何是k --
 //一、所有元素 = 前面的元素 + 後邊的元素
 //二、f(k) = f(k-1) + f(k-2)
 //三、前面有f(k-1)個元素,因此能夠繼續拆分,f(k-1) = f(k-2)+f(k-3)
 k --;
 }else if (findVal > temp[mid]){
                low = mid + 1;
 //爲何是k -= 2
 //一、所有元素 = 前面的元素 + 後面的元素
 //二、f(k) = f(k-1) + f(k-2)
 //三、由於咱們有f[k-2],因此能夠繼續拆分f[k-2]=f[k-3]+f[k-4]
 //四、即在f[k-2]的前面進行查找
 //五、下次循環mid = f[k - 1 - 2] - 1
 k -= 2;
 }else {
                if (mid <= high){
                    return mid;
 }else {
                    return high;
 }
            }
        }
        return -1;
 }
    public static void main(String[] args) {
        int[] arr = {1,8,9,10,29,39,49,69};
 int resultIndex = fibSearch(arr, 39);
 System.out.println(resultIndex);
 }
}
相關文章
相關標籤/搜索