2022-7-15 java 資料結構入門

里奧~ 發表於 2022-07-16
Java 資料結構

@

資料結構

1.二分查詢

動圖演示:

Alt

前提: 使用二分查詢的前提是陣列是有序的

具體詳解

int []arr ={1,2,3,4,5,6,7};
Scanner scanner = new Scanner(System.in);

int left = 0;//最左邊元素的下標
int right = arr.length-1;//最右邊元素的下標
int mid = 0;//中間元素的值
System.out.println("請輸入一個數:");
int k = scanner.nextInt();//要查詢的元素k

while (left <= right){
    mid = (left+right)/2;
    if (arr[mid]>k){
        right = mid-1;
    }
    else if (arr[mid]<k){
        left = mid+1;
    }
    else {
        break;
    }

}
if (left>right){
    System.out.println("沒有找到該數");
}else {
    System.out.println("找到了");
}

2.氣泡排序

氣泡排序詳解

1.氣泡排序原理

Alt

2.氣泡排序基礎版

 //按照剛才那個動圖進行對應
//氣泡排序兩兩比較的元素是沒有被排序過的元素--->
public void bubbleSort(int[] array){
    for(int i=0;i<array.length-1;i++){//控制比較輪次,一共 n-1 趟
        for(int j=0;j<array.length-1-i;j++){//控制兩個挨著的元素進行比較
            if(array[j] > array[j+1]){
                int temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
            }
        }
    }
}

3.氣泡排序程式碼優化版

說明:因為在最後幾輪,陣列可能已經是有序的了,沒有必要再進行迴圈排序了

  • 利用布林變數 isSorted作為標記。如果在本輪排序中,元素有交換,則說明數列無序;如果沒有元素交換,說明數列已然有序,直接跳出大迴圈
 public static int[] bubbleSort(int[] arr) {
     if (arr == null || arr.length < 2) {
          return arr;
     }
    for (int i = 0; i < arr.length - 1; i++) {
         boolean isSorted  = true;//有序標記,每一輪的初始是true
         for (int j = 0; j < arr.length -i - 1; j++) {
             if (arr[j + 1] < arr[j]) {
                 isSorted  = false;//有元素交換,所以不是有序,標記變為false
                 int t = arr[j];
                 arr[j] = arr[j+1];
                 arr[j+1] = t;
             }
         }
         //一趟下來是否發生位置交換,如果沒有交換直接跳出大迴圈
         if(isSorted )
              break;
     }
     return arr;
}

4.氣泡排序程式碼升級版

如果數列中前半部分是無序的,後半部分是有序的呢?比如(3,4,2,1,5,6,7,8)這個陣列,其實後面的許多元素已經是有序的了,但是每一輪還是白白比較了許多次

  • 解決方法:在每一輪排序的最後,記錄一下最後一次元素交換的位置,那個位置也就是無序數列的邊界,再往後就是有序區
public static int[] bubbleSort(int[] arr) {
     if (arr == null || arr.length < 2) {
          return arr;
     }
    //記錄最後一次交換的位置
    int lastExchangeIndex = 0;
    //無序數列的邊界,每次比較只需要比到這裡為止
    int sortBorder = arr.length - 1;
    
    for (int i = 0; i < arr.length - 1; i++) {
         boolean isSorted  = true;//有序標記,每一輪的初始是true
         for (int j = 0; j < sortBorder; j++) {
             if (arr[j + 1] < arr[j]) {
                 isSorted  = false;//有元素交換,所以不是有序,標記變為false
                 int t = arr[j];
                 arr[j] = arr[j+1];
                 arr[j+1] = t;
                 lastExchangeIndex = j;
             }
         }
        sortBorder = lastExchangeIndex
         //一趟下來是否發生位置交換,如果沒有交換直接跳出大迴圈
         if(isSorted )
              break;
     }
     return arr;
}

3.選擇排序

1. 演算法步驟

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。

重複第二步,直到所有元素均排序完畢。

2.插入排序圖解:

Alt

3.程式碼

public static void searchSort(int[] arr){
        for (int i = 0; i<arr.length - 1; i++){//①
            int minIndex = i;
            int min = arr[i];//②
            for (int j = i + 1; j<arr.length; j++){//③
                if(min>arr[j]){
                    min = arr[j];
                    minIndex = j;
                }
            }
            arr[minIndex] = arr[i];
            arr[i] = min;//④
        }
        System.out.println(Arrays.toString(arr));
    }
 ①.我們定義一個迴圈,這個迴圈就是用來進行上述的每輪排序行為的,在這個迴圈中,每輪迴圈都會找到當前陣列中的最小值,並讓這個最小值和當前陣列的首元素進行位置互換。與此同時,迴圈頭中的變數i在這裡也有有著豐富的含義,它不僅代表迴圈的次數,它也代表著每次輪迴圈中陣列的首元素位置,當陣列中的首元素位置就是原最大的那個陣列中最後一個元素的位置時,就說明排序已經到頭,演算法可以結束了。

​ ②.我們定義最小值的下標變數,同時定義最小值。這裡主要是找最值的方法,找最值的方法一般都是預設陣列首節點是最值點,然後對陣列進行遍歷,在遍歷過程中會依次檢視整個陣列中的所有元素並和最值進行對比,如果發現了比最值小的元素,那麼就讓當前的陣列元素替換掉之前的最值元素成為新的最值元素,如這裡使用minIndex儲存最值下標,使用min儲存最值,當找到陣列中新的最值時,新的最值下標就會替換掉minIndex中原來的值,而新的最值也會替換掉min中原來的值。

​ ③.這個迴圈就是找最值的迴圈體,具體解釋看上文中②的解釋。

​ ④.這裡的兩句程式碼就是將找到的最值與陣列首元素的值進行對換的行為。之後將開啟新的一輪迴圈,新陣列將從i開始,而i這時加了1,這就代表著新陣列往後“縮”了一格,規模變小了一些。重複進行外迴圈,直到外迴圈終止,這個陣列最終會被排好序。

4.插入排序

詳解

1.插入排序圖解

Alt

2.演算法總結

​ 根據圖解,我們現在可以理解插入排序的過程了(以從小到大排序為例):我們將原陣列空間看成兩個部分,前邊是有序部分,後邊是無序部分,有序部分我們預設為它就已經是排好序的,它內部已經是從小到大有序的狀態了,即使當前它是空的,它也具備這個特徵。然後我們不斷的取無序陣列的首元素,向有序陣列的尾部新增,我們在這裡並沒有真正的將它取出,並且再新增回來,因為根據我們的定義,有序陣列的尾部正好就和無序陣列的頭部相鄰,這只是一個更改標記的事情,我們只要把有序陣列的長度加1,無序陣列的長度減一,並且將有序陣列的頭部元素指標指向它的下一個,就可以完全準確的代表這個行為。因此這時我們已經將無序陣列的首元素取出,並插入在了有序陣列的尾部,然而這時,在尾部新加入的元素有可能會導致整個有序陣列變得無序,因此我們需要進行調整。而調整方式就是將新加入的元素進行對比並根據對比結果往前移動,這個移動過程有點像氣泡排序的過程:新加入元素和它前邊的元素進行對比,如果它比它前邊的元素小,則二者互換位置,重複這個行為,直到它前邊的元素小於它才會停止,這樣一來,有序陣列就仍然是有序陣列了。我們重複這個向有序陣列中插入不斷插入資料,並在插入之後通過操作使其變得有序的行為,在這個過程中我們會一個一個的拿走無序陣列中的首節點並一個一個單位的減少無序陣列的規模,並一個一個的向有序陣列中新增並擴大有序陣列的規模,直到最後整個無需陣列被掏空,有序陣列佔據了原陣列空間的所有空間時,整個陣列排序宣告完成。

3.程式碼

public static void insertSort(int[] arr){
        for (int i = 1; i<arr.length; i++){//①
            for (int j = i-1; j>=0; j--){//②
                if(arr[j]>arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }else
                    break;
            }
        }
        System.out.println(Arrays.toString(arr));
    }

4.程式碼詳解

​ ①標記了外部的大迴圈,這個迴圈實際上就是驅動有序陣列一個一個增加,無序陣列一個一個減少的動力,在此,i就代表了無序陣列的首節點位置,因為當陣列中有一個元素時它必定有序,所以我們在這裡可以忽略掉往插入第一個元素的行為,直接從第二個開始插入。

​ ②標記了外部迴圈中的一個內迴圈,外迴圈的每次執行,實際上都代表了我們將無序陣列中的首元素拿下來放到有序陣列的尾部,而這裡這個內迴圈,實際上就是移動這個新來的元素,使有序陣列重新變得有序的機制,我們可以看到,j指向的是i-1,這個位置就是當前有序陣列沒有插入新元素之前的尾部位置,在這個迴圈中,j要一直和j+1進行對比,j+1指向的就是新加入的元素,如果j指向的元素大於j+1指向的元素的話,二者就要進行一個類似氣泡排序的互換,將j+1指向的元素和j指向的元素進行互換,在互換之後,j指向了新加入的元素,而j+1則指向了之前新元素前邊的元素,也就是之前的陣列尾部,這時j自減一次,這就導致j+1又重新指向了新加入的元素,而j也又指向了新加入的元素的前一個元素,新元素得以繼續同它前邊的元素進行對比,當經過對比發現,新加入的元素已經大於前邊的元素之後,說明新加入的元素已經移動到了自己正確的位置,這個移動行為就可以終止了。

​ 插入排序的精髓在於設定一個有序的空陣列,從它為空的時候就認定其為有序陣列,並在不斷的插入新元素的過程中維護其有序性,這樣一來,我們實際上一直在乾的事情就是向一個有序陣列中不斷地插入新元素,這比進行直接排序的操作手法會簡單的多。以上就是插入排序。

5.陣列反轉

// 建立一個字串的陣列
        String[] str = new String[]{"AA","BB","CC","DD","EE","FF","GG"};

方式一

臨時變數

for(int i = 0; i < str.length / 2; i++){
			String temp = str[i];
			str[i] = str[str.length - i - 1];
			str[str.length - i - 1] = temp;
		}

方式二

建立一個新的陣列,倒序遍歷舊陣列中的元素,然後按照順序複製給新陣列,然後再把新陣列的值複製到舊陣列當中

for(int i = 0,j = str.length - 1; i < j; i++,j--){
    // 交換
    String temp = str[i];
    str[i] = str[j];
    str[j] = temp;
}

方式三

建立一個新陣列,長度是舊陣列的長度。將舊陣列倒序遍歷獲取陣列中的元素,拿到元素之後從頭開始賦值給新陣列當中,最後將新陣列的地址值賦值給舊陣列

 // 方式1.3
        String[] newArr = new String[str.length];       // 建立新陣列,陣列的長度是舊陣列的長度
        for (int i = str.length - 1, j = 0; i >= 0; i--, j++) {
            newArr[j] = str[i];
        }

        // 迴圈完畢之後,切記要將newArr陣列的地址值賦值給str陣列
        str = newArr;

        // 遍歷str陣列,檢視是否反轉了
        for(int i = 0; i < str.length; i++){
            System.out.print(str[i] + " ");
        }