查找算法——二分查找

查找算法——二分查找

  1. 簡單介紹:對有序數組,通過遞歸,不斷進行折中,直到其中間值等於所需要查找的數字
  2. 思路分析:
    • 確定該數組的中間下標,mid = (left + right) / 2
    • 然後將需要查找的數與中間值mid進行比對
      - 比中間值大,那就再以中間值mid爲left,right爲right進行同樣的折半查找
      - 比中間值小,那就再以中間值mid爲right,left爲left進行同樣的折半查找
    • 如果arr[mid] == findVal,說明找到了
    • 判定遞歸截至的條件
      - 找到對應的值,返回後進行推出
      - 沒有找到對應的數值,會滿足left>right,然後進行退出
  3. 代碼實現:
    我的代碼:
package binarysearch;

public class Binary {
    public static void main(String[] args) {
        int[] arr = {1,3,45,56,789,888,999};
        System.out.println(binarySearch(arr,0,arr.length - 1,4));
    }
    static int valIndex = - 1;
    public static int binarySearch(int[] arr,int left,int right,int searchVal){

        //設置中間值的索引

        int mid = (left + right) / 2;
        if(left < right){
          if(arr[mid] == searchVal){
            //如果找到對應的值,那就退出
            valIndex =  mid;
          }else if(arr[mid] > searchVal){
            //如果你要找的值小於中間值,那就說明在左半邊
            binarySearch(arr,left,mid - 1,searchVal);
          }else if (arr[mid] < searchVal){
            //如果你要找的值大於中間值,那就說明再右半邊
            binarySearch(arr,mid + 1,right,searchVal);
          }else{
            valIndex =  - 1;
          }
          return valIndex;
        }
    }
}

問題分析:

  1. 每一次都折中是否能夠查遍每一個元素?
  • 通過實驗發現,不能夠查找隨後一個元素。這取決去整除的特性,自動捨棄餘數。如果是相鄰的兩個數,他會下意識的取左邊那個數,而右邊的數取不到。如{1,3},取中間值,會默認是索引0,就是對應的1.永遠取不到三
  1. 如果在運算中沒有對中值索引進行更改,爲什麼找不到元素時會陷入死循環?
    *死循環
  • 如{1,3},要尋找的是2。因爲不專門對mid進行改變,mid在僅僅剩兩個值的時候始終不變,都是0,right右邊界始終不變,都是1,所以陷如【0,1】的死循環
  • 問題:不修改前,能夠對除了最後一個元素的所有數組元素進行遍歷,但是對於不在數組內的元素,會現如相鄰區間的死循環。
  • 修改:既然每一次比較都是和中間值進行比較,那既然不等與中間值,那麼在下一次比較中就可以不把中間值算在內。比中間值小,那麼在下一次比較中,right = mid - 1,比中間值大,下一次比較 left = mid + 1。既可以便利最後一個元素,又可以避免陷入死循環,最後一定是left == right。
  • 問題:在查找不存在的數時,會陷入死循環。最後肯定是圍繞着某一個值來回反覆旋轉。因爲無論是加一,還是減一,最終都是left和mid以及mid等於同一個數。
    棧溢出
  • 修改:添加判斷是否執行的條件,一但left和right並於0處,就會出現right = -1,left = 0,然後陷入死循環。所以前提就必須是,left <right纔可以

教程代碼:

public static int binarySearch(int[] arr,int left,int right,int searchVal){
        //設置中間值的索引
        int mid = (left + right) / 2;
        if (right >= left){
            if(arr[mid] == searchVal){
                //如果找到對應的值,那就退出
                return mid;
            }else if(arr[mid] > searchVal){
                //如果你要找的值小於中間值,那就說明在左半邊
                return binarySearch(arr,left,mid - 1,searchVal);
            }else if (arr[mid] < searchVal){
                //如果你要找的值大於中間值,那就說明再右半邊
                return binarySearch(arr,mid + 1,right,searchVal);
            }
        }
        return -1;
    }

分析總結:

  1. 沒有必要設置static index專門承裝索引值,直接設置返回的是遞歸函數返回的值就可以了,層層遞歸,返回的一定是迭代到最終的那個數。返回最終值
  2. 思考不夠有邏輯,兩數相比較,除了大於,等於,小於,還剩什麼?第四種使你變得嗎?大哥!!!

改良版——有序數組中多個相同的值

  1. 思路分析:找到mid的索引之後並不要立即放回,而是以mid爲中心分別向兩邊進行掃描,將所有於mid相同的值的索引加入到集合ArrayList中,然後將集合ArrayList返回
  2. 代碼實現:
 public static List<Integer> binarySearch2(int[] arr,int left,int right,int searchVal){
        //區別一下重寫和重載:重載:兩同一不同,同類同名,不同的形參.重寫:原方法的覆蓋,是子類對父類的繼承.
        //這裏都不是,形參列表不同,只有返回值類型不同,不是重載,沒有繼承,不是重寫

        int mid = (left + right) / 2;
        if (left <= right){
            if (searchVal < arr[mid]){
                return  binarySearch2(arr,left,mid - 1,searchVal);
            }else if(searchVal > arr[mid]){
                return binarySearch2(arr,mid + 1,right,searchVal);
            }else{
                List<Integer> list = new ArrayList<>();
                int temp = mid;
                while (temp >= 0 && arr[temp] == arr[mid]){
                    list.add(temp);
                    temp --;
                }
                temp = mid + 1;
                while (temp <= arr.length - 1 && arr[temp] == arr[mid]){
                    list.add(temp);
                    temp ++;
                }
                return list;
            }
        }
        return new ArrayList<>();

    }

分析總結:

  1. 多重索引關鍵在於遍歷,因爲是有序數組,所以相同的一定在一起。對其左右進行遍歷就行了。
  2. 出現兩個數組複製值的情況,賦值加上索引移位兩步走
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章