在java中,咱們常見的查找有四種java
咱們將一一介紹着四種查找方式的思想以及程序的實現。算法
順序查找 的查找過程爲:從數組的第一個元素開始,逐個將要查找的關鍵字和數組中的元素進行比較,若存在相等,則返回對應的下標。反之,若至最後一個元素,其關鍵字和元素都不相等,則代表數組中不存在要查找的數,查找不成功數組
舉例說明:
有一個數列: {1,8, 10, 89, 1000, 1234} ,判斷數列中是否包含數字8;要求: 若是找到了,就提示找到,並給出下標值。優化
public class seqSearch { public static void main(String[] args) { //定義數組arr int[] arr = {1,8, 10, 89, 1000, 1234}; //定義下標 int index = seqSearch(arr, 8); //當下標爲-1時,說明要查找的數不存在 if (index == -1) { System.out.println("不存在"); } else { System.out.println("下標爲:" + index); } } public static int seqSearch(int[] arr, int value) { for (int i = 0; i < arr.length; i++) { if (arr[i] == value ) { return i; } } //由於下標數值老是>= 0,因此,當要查找的數不存在時,返回-1便可 return -1; } }
擴展 : 假如咱們要查的數在數組中存在兩個及兩個以上,咱們怎麼把全部知足條件的元素下標輸出呢?
思考: 這個問題的難點在於咱們並不知道數組有多大,以及要查的數到底有多少個,因此沒法新建一個新的數組來保存知足條件的下標。因此咱們使用ArrayList 來存儲保存知足條件的下標。設計
import java.util.ArrayList; public class seqSearch { public static void main(String[] args) { int[] arr = {1, 9, 11, 11, 52, 65, -4}; ArrayList<Integer> arrayList = seqSearch(arr, 11); //當沒有查找到時,則arrayList中則沒有下標值 System.out.println("arrayList:" + arrayList); } public static ArrayList<Integer> seqSearch(int[] arr, int value) { ArrayList<Integer> arrayList = new ArrayList<Integer>(); for (int i = 0; i < arr.length; i++) { if (arr[i] == value ) { //將知足條件的下標放入arrayList中 arrayList.add(i); } } return arrayList; } }
二分查找的查找過程:先肯定待查找記錄所在的範圍(區間),而後逐步縮小範圍直到找到或找不到該記錄爲止。
接下來咱們將對這個查找過程進行分析和代碼實現:
思路:3d
舉例說明: 有一個數列{1, 2, 3, 4, 5, 6, 7, 8, 9},判斷數列中是否包含數字8;要求: 若是找到了,就提示找到,並給出下標值。code
public class BinarySearch { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; //注意: right 最大值 取不到 arr.length, 最大是arr.length - 1 int resIndex = binarySearch(arr,0, arr.length - 1,8); System.out.println("resIndex: " + resIndex); } //二分查找 /** * @param arr 有序數組 * @param left 序列左邊 * @param right 序列右邊 * @param findVal 要找的數 * @return 若是找到就返回下標,不然返回 -1 */ public static int binarySearch(int[] arr, int left, int right, int findVal) { //當left > right 時, 說明遞歸整個數組, 可是沒有找到 if (left > right) { return -1; } int mid = (left + right) / 2; int midVal = arr[mid]; //向右遞歸 if (findVal > midVal) { return binarySearch(arr, mid + 1, right, findVal); } else if (findVal < midVal) { //向左遞歸 return binarySearch(arr, left,mid - 1, findVal); } else { return mid; } } }
擴展 : 假如咱們要查的數在數組中存在兩個及兩個以上,咱們怎麼把全部知足條件的元素下標輸出呢?
思考: 這個問題的難點在於咱們並不知道數組有多大,以及要查的數到底有多少個,因此沒法新建一個新的數組來保存知足條件的下標。因此咱們使用ArrayList 來存儲保存知足條件的下標。blog
import java.util.ArrayList; /** * 問題: * {1, 2, 3, 4, 5, 6, 7, 8, 8, 9} 當一個有序數組中,有多個相同的數值時, * 如何將全部的數值都查找到,好比這裏的 8. */ public class BinarySearch2 { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 8, 9}; ArrayList<Integer> resIndexList = binarySearch2(arr, 0, arr.length - 1, 8); System.out.println("resIndexList = " + resIndexList); } public static ArrayList binarySearch2(int[] arr, int left, int right, int findval) { //若是left > right , 說明遞歸了整個數組,退出循環 if (left > right) { return new ArrayList<Integer>(); } int mid = (left + right) / 2; int midVal = arr[mid]; //向右循環 if (findval > midVal) { return binarySearch2(arr, mid + 1, right, findval); } else if (findval < midVal) { //向左循環 return binarySearch2(arr, left, mid - 1, findval); } else { // 思路: // 1. 在找到mid 索引值後,不要馬上返回 // 2. 向mid 索引值的左邊掃描,將全部等於 8 的元素的下標,加入到集合ArrayList // 3. 向mid 索引值的右邊掃描,將全部等於 8 的元素的下標,加入到集合ArrayList // 4. 將ArrayList 返回 ArrayList<Integer> resIndexList = new ArrayList<Integer>(); //向mid 索引值的左邊掃描,將全部等於 8 的元素的下標,加入到集合ArrayList int temp = mid - 1; while (true) { if (temp < 0 || arr[temp] != findval) { break; } //不然,就temp 放入resIndexList resIndexList.add(temp); //temp 左移 temp--; } //將中間的mid 下標放入 resIndexList.add(mid); //向mid 索引值的右邊掃描,將全部等於 8 的元素的下標,加入到集合ArrayList temp = mid + 1; while (true) { if (temp > arr.length - 1 || arr[temp] != findval) { break; } //不然,就temp 放入resIndexList resIndexList.add(temp); //temp 右移 temp++; } return resIndexList; } } }
插入查找實際上是從二分查找 優化而來,改變了查找的規則,從而實現快速查找。插值查找算法的mid 是自適應的,而二分查找的 mid 老是 序列的中間值,插入查找的mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]) 。這裏的low對應二分查找的left,right對應於二分查找的right,key就是咱們前面說的findVal
遞歸
思路同二分查找相同
舉例說明: 插值查找算法 查找數組中數值爲8的元素下標索引
import java.util.ArrayList; import java.util.Arrays; public class InsertValueSearch { //count 表示遞歸次數 private static int count = 0; public static void main(String[] args) { //定義一個有100個元素的數組 int arr[] = new int[100]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } //將數組轉成字符串輸出 System.out.println(Arrays.toString(arr)); ArrayList<Integer> resArrayList = insertValueSearch(arr, 0, arr.length - 1, 8); System.out.println("resArrayList = " + resArrayList); System.out.println("遞歸次數:" + count); } //插值查找 public static ArrayList insertValueSearch(int[] arr, int left, int right, int findVal) { count++; //若是left > right,則說明已經遞歸完數組中全部的數字, 退出 //findval < arr[0] || findval > arr[arr.length - 1] 必需要有,由於當findVal很是大時,會致使mid越界 if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) { return new ArrayList<Integer>(); } //找到中間值 int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]); int midVal = arr[mid]; //向左遞歸 if (findVal < midVal) { return insertValueSearch(arr, left, mid - 1, findVal); } else if (findVal > midVal) { //向右遞歸 return insertValueSearch(arr, mid + 1, right, findVal); } else { ArrayList<Integer> resArrayList = new ArrayList<Integer>(); //往mid 左邊掃描,將全部等於 8 的元素的下標,加入到集合ArrayList int temp = mid - 1; while (true) { //temp < 0 則意味着,mid在數組的最左邊 if (temp < 0 || arr[temp] != findVal) { break; } resArrayList.add(temp); //左移一位 temp--; } //將中間的mid 下標放入 resArrayList.add(mid); //往mid 右邊掃描,將全部等於 8 的元素的下標,加入到集合ArrayList temp = mid + 1; while (true) { //temp > arr.length - 1 則意味着,mid在數組的最右邊 if (temp > arr.length - 1 || arr[temp] != findVal) { break; } resArrayList.add(temp); //右移 temp++; } return resArrayList; } } }
插值算法的注意事項:
PS:關於插值查找算法的mid值爲何要這麼求,我也不是很清楚。不過我認爲若是你不是專門作算法的,那我們知道應該在什麼時候何地使用這種算法就能夠了。
import java.util.Arrays; public class FibonacciSearch { //斐波那契數組元素的個數, public static int maxSize = 20; public static void main(String[] args) { int[] arr = {1, 8, 10, 89, 1000, 1234}; System.out.println(fibSearch(arr,89)); } //由於後面咱們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; //獲取到斐波那契數列 int f[] = fib(); //獲取到斐波那契分割數值的下標 while (high > f[k] - 1) { k++; } //由於f[k] 值可能大於咱們數組a 的長度,所以咱們須要使用Array類,構造一個新的數組,並指向a //不足的部分會使用0填充 int[] temp = Arrays.copyOf(a, f[k]); //實際上須要使用a數組最後的數填充temp /** * 舉例說明: * int[] arr = {1, 8, 10, 89, 1000, 1234}; * int[] temp = Arrays.copyOf(a, f[k]); * 當 數組a 的長度 小於 f[k] 的長度, 咱們要將a 的值賦值給數組temp ,可是 * 數組temp 的長度是和f[k] 相同的,因此 數組temp 中沒有被賦值的部分 填充爲0; * 又由於,若是數組temp 中沒有被賦值的部分 * */ for (int i = high + 1; i < temp.length; i++) { temp[i] = a[high]; } //使用while來循環處理,找到咱們的數key while (low <= high) { //只要這個條件知足,就能夠繼續尋找 //獲得mid 中間值 的規則 不同 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-2] = 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; } }
以上全部程序都在IDEA中運行過,沒有任何問題。謝謝你們!共勉!