【算法】分治法

二分搜索(二分查找、折半查找)

【要求】線性表爲有序表(排好序的)
【基本思想】先確定待查找記錄所在的範圍,然後逐步縮小範圍直至找到或找不到該記錄位置。
【步驟】
1、先確定中間位置:middle = (left+right)/2;
2、將待查找得key值與data[middle].key值相比較。若相等,則查找成功並返回該位置,否則須確定新得查找區間,繼續二分查找.如果data[middle].key大於key,right=middle-1。反之,data[middle].key小於key,left=middle+1。

//二分搜索算法
public class test{
    public static void main(String[] args){
        int[] a = {1,2,3,4,5,6,7,8,9};
        int position = bSearch(a,0,a.length-1,3);
        System.out.println(position);
    }
    public static int bSearch(int[] data,int left,int right,int key){
        //獲取中間位置
        int middle = (left+right)/2;
        //三種情況
        if(data[middle] == key){
            return middle;
        }else if(data[middle] > key){
            return bSearch(data,left,middle-1,key);
        }else{
            return bSearch(data,middle+1,right,key);
        }
    }
}

漢諾塔

在漢諾塔遊戲中,有三個分別命名爲A、B、C得塔座,幾個大小各不相同,從小到大一次編號得圓盤,每個原盤中間有一個小孔。最初,所有得圓盤都在A塔座上,其中最大得圓盤在最下面,然後是第二大,以此類推.

在這裏插入圖片描述

遊戲的目的是將所有的圓盤從塔座A移動到塔座B;塔座C用來防止臨時圓盤,遊戲的規則如下:

1、一次只能移動一個圓盤

2、任何時候都不能將一個較大的圓盤壓在較小的圓盤上面.

3、除了第二條限制,任何塔座的最上面的圓盤都可以移動到其他塔座上.

【解決思想】

在解決漢諾塔問題時,事實上,我們不是關心圓盤1開始應該挪到哪個塔座上,而是關心最下面的圓盤4.當然,我們不能直接移動圓盤4,但是圓盤4最終將從塔座A移動到塔座B.按照遊戲規則,在移動圓盤4之前的情況一定如下圖

在這裏插入圖片描述

我們仍將分析,如何將前三個圓盤從A移動到C,然後圓盤4從A移動到B,前三個圓盤從C再移動到B.

但是上面的步驟可以重複利用!例如將三個圓盤從A移動到C,那麼應該先將前兩個圓盤從A移動到B,然後將圓盤3從A移動到C,最後將前兩個圓盤從B移動到C.

持續簡化這個問題,最終我們將只需要處理一個圓盤從一個塔座移動到另一個塔座的問題.

//漢諾塔
public class test{
    public static int count = 1;
    public static void main(String[] args){
        moved(4,"第一根柱子","第二根柱子","第三根柱子");

    }
    //i:圓盤數量
    //a:初始位置
    //b:最終位置
    //c:輔助位置
    public static void moved(int i,String a,String b,String c){
        if(i == 1){
            disPlay(1,a,b);
        }else{
            moved(i-1,a,c,b);
            disPlay(i,a,b);
            moved(i-1,c,b,a);
        }
    }
    public static void disPlay(int i,String a,String b){
        System.out.println("第"+count+"步:移動第"+i+"個塔從"+a+"到"+b);
        count++;
    }
}

合併排序(歸併排序)

【歸併排序思路】
將長度爲n的待排序數組看做是由n個有序長度爲1的數組組成
將其兩兩合併,得到長度爲2的有序數組
然後再對這些子表進行合併,得到長度爲4的有序數組
重複上述過程,一直到最後的子表長度爲n也就完成了排序

一、合併兩個有序數組

//合併排序(合併兩個有序數組)
public class test{
    public static void main(String[] args){
        int[] a = {1,3,4,5,9};
        int[] b = {2,6,7,8,10};
        int[] result = merge2Arr(a,b);
        for(int i=0;i<result.length;i++){
            System.out.println(result[i]);
        }

   }
    public static int[] merge2Arr(int[] arr1,int[] arr2){
        int len1 = arr1.length;
        int len2 = arr2.length;
        int[]  result = new int[len1+len2];//新數組
        int i=0,j=0,k=0;
        while(i < len1 && j < len2){
            result[k++] = arr1[i] < arr2[j] ? arr1[i++] : arr2[j++];
        }
        while(i < len1){
            result[k++] = arr1[i++];
        }
        while(j < len2){
            result[k++] = arr2[j++];
        }
        return result;
    }

}

二、合併排序的遞歸實現

在這裏插入圖片描述

//合併排序(遞歸算法)
public class test {

    static int[] a = new int[] { 20, 9, 3, 5, 26, 100, 8, -1, 7, 50, -5 };

    public static void main(String[] args) {
        System.out.println("before sort");
        //ArrayUtils.printArray(a);
        for(int i=0;i<a.length;i++){
            System.out.println(a[i]);
        }
        //進行合併排序mergeSort
        mergeSort(a);
        System.out.println("after sort");
        for(int i=0;i<a.length;i++){
            System.out.println(a[i]);
        }
        //ArrayUtils.printArray(a);

    }

    private static void mergeSort(int[] k) {
        mSort(k, 0, k.length - 1);
    }

    private static void mSort(int[] k, int left, int right) {
        int center;
        // 遞歸退出條件,及left》=right的時候
        if (left < right) {
            // 找出中間索引
            center = (left + right) / 2;
            // 對左邊數組進行遞歸
            mSort(k, 0, center);
            // 對右邊數組進行遞歸
            mSort(k, center + 1, right);
            // 合併
            merging(k, left, center, right);

        }
    }

    private static void merging(int[] k, int left, int center, int right) {
       // 存放數據的數組
        int tempArr[] = new int[k.length];
        // third記錄中間數組的索引
        int mid = center + 1;
        int third = left;//中間數組從左側開始
        int temp = left;//先記錄下初始左側位置
        while (left <= center && mid <= right) {
            // 從左右兩個數組找出最小的數存入tempArr數組
            if (k[left] < k[mid]) {
                tempArr[third++] = k[left++];
            } else {
                tempArr[third++] = k[mid++];
            }
        }

        // 剩餘部分依次放入中間數組
        while (mid <= right) {
            tempArr[third++] = k[mid++];
        }

        while (left <= center) {
            tempArr[third++] = k[left++];
        }
        // 將中間數組中的內容複製回原數組
        while (temp <= right) {
            k[temp] = tempArr[temp];
            temp++;
        }
    }

}

三、合併排序的非遞歸實現(迭代)

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章