1. 程式人生 > >今日分資料結構作業:氣泡排序、堆排、希爾排序、快排……

今日分資料結構作業:氣泡排序、堆排、希爾排序、快排……

先看實驗報告:

 好吧,這次實驗報告沒有什麼好吐槽的,安安分分的寫了好久。

bug無數,雖然之前寫過,但是沒這麼詳細。

程式碼+註釋:

import java.io.*;
import java.util.Arrays;
public class Main3 {
    /*直接排序,希爾排序,氣泡排序,快速排序,直接選擇排序,堆排序,歸併排序演算法
     * 直接排序和直接選擇排序不是同一個嗎?
     * 太虛假了吧
     * 既然如此我就寫一個好了
     */
    static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) {
        int arr[]=new int[10];                //測試一個1萬資料的陣列
        randomArrays(arr);
        long startTime,endTime;
        startTime = System.currentTimeMillis();
        chooseSort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("選擇排序的時間是:"+(endTime-startTime)+"ms");
        upsetArrays(arr);
        startTime = System.currentTimeMillis();
        bubbleSort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("氣泡排序的時間是:"+(endTime-startTime)+"ms");
        upsetArrays(arr);
        startTime = System.currentTimeMillis();
        shellSort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("希爾排序的時間是:"+(endTime-startTime)+"ms");
        upsetArrays(arr);
        startTime = System.currentTimeMillis();
        memerySort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("歸併排序的時間是:"+(endTime-startTime)+"ms");
        upsetArrays(arr);
        startTime = System.currentTimeMillis();
        heapSort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("堆排序的時間是:"+(endTime-startTime)+"ms");
        upsetArrays(arr);
        startTime = System.currentTimeMillis();
        quickSort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("快排的時間是:"+(endTime-startTime)+"ms");
        upsetArrays(arr);
        startTime = System.currentTimeMillis();
        Arrays.sort(arr);
        endTime = System.currentTimeMillis(); 
        System.out.println("JAVA類庫自帶快速的時間是:"+(endTime-startTime)+"ms");
    }
//選擇排序=====================================================================================================
    /*
     * 柿子先挑軟的捏,當然要先寫選擇排序
     */
    static void chooseSort(int arr[]) {
        for(int i=0;i<arr.length;i++) {
            for(int j=i+1;j<arr.length;j++) {
                if(arr[i]>arr[j]) {
                    swap(arr,i,j);
                }
            }
        }
    }
//============================================================================================================

//氣泡排序=====================================================================================================
    /*
     * 氣泡排序
     * 加個flag優化一下
     */
    static void bubbleSort(int arr[]) {
        boolean flag=true;
        for(int i=0;i<arr.length&&flag;i++) {
            flag=false;
            for(int j=arr.length-1;j>i;j--) {
                if(arr[j]<arr[j-1]) {
                    flag=true;
                    swap(arr,j,j-1);
                }
            }
        }
    }
//=============================================================================================================
    
//希爾排序=====================================================================================================
    /*
     * 希爾排序
     * 這個排序挺有趣的,剛才看了一個視訊理解了:
     * https://www.bilibili.com/video/av17062242?from=search&seid=2961680739883013473
     * 不過下一個視訊也挺有趣的
     * https://www.bilibili.com/video/av17004970/?spm_id_from=trigger_reload
     * 程式碼我自己實現一下,不行可以除錯,或者看別人的。
     * 這個排序不太穩定
     * 不過我很奇怪這個原理
     */
    static void shellSort(int arr[]) {
        int len=arr.length;
        int gap=len;                                //過程增量
        do {
            gap=gap/3+1;
            for(int i=0;i<len-gap;i++) {
                if(arr[i]>arr[i+gap]) {
                    int j=i;
                    while(j>=0&&arr[j]>arr[j+gap]) {
                        swap(arr,j,j+gap);
                        j-=gap;
                    }
                }
            }
        }
        while(gap>1);
        // 寫下了感覺還是很簡單的,而且測試發現也沒什麼問題。         
    }
//=============================================================================================================

//歸併排序=====================================================================================================
    /*
     * 歸併排序還是很好用的,很穩定的。
     * 記得剛加入ACM的時候,學長就是通過選擇排序和歸併排序給我們講複雜度
     * 這個在我大一上學期自己實現了一下,覺得也沒有什麼難度。
     * 很經典的分治遞迴
     */
    static void memerySort(int arr[]) {
        int[] temp=new int[arr.length];
        memerySort(arr,0,arr.length-1,temp);
    }
    static void memerySort(int arr[],int l,int r,int temp[]) {
        int t=(l+r)>>1;                    //位運算,(x>>1)==x/2。計算比較快,堆排應該會用到比較多的位運算
        if(l==r)
            return;
        memerySort(arr,l,t,temp);
        memerySort(arr,t+1,r,temp);
        memeryArrays(arr,l,r,t,temp);
    }
    static void memeryArrays(int arr[],int l,int r,int t,int temp[]) {
        int i=l,j=t+1;
        int now=0;
        while(i<=t&&j<=r) {
            if(arr[i]<arr[j]) 
                temp[now++]=arr[i++];
            else
                temp[now++]=arr[j++];
        }
        for(;i<=t;i++) {
            temp[now++]=arr[i];
        }
        for(;j<=r;j++) {
            temp[now++]=arr[j];
        }
        for(int k=l;k<=r;k++) {
            arr[k]=temp[k-l];
        }
    }
//=============================================================================================================

//堆排序=======================================================================================================
    /*
     * 堆排這東西,每次都是寫bug10分鐘。debug兩小時。東西太多了,思路太嚴謹了。
     * 雖然寫過好幾次吧
     * 我本來以為堆排一定要額外開一個堆,沒想到看了網上的思路,不一定是這樣的
     * 很節省空間啊
     * 大概就是,需要一個adjust函式調整堆(如果要從小排序,需要挑成最大堆)
     * 把這個無序陣列調整成堆
     * 然後把堆頂和陣列最後一個元素交換,堆容量-1
     * 這樣每次都把堆裡最大一個元素放到了最後面,就排好序了
     * 其實我感覺堆排嚴謹的複雜度應該是n*logn!
     */
    static void heapSort(int[] arr) {
        buildHeap(arr);                //建堆
        getSort(arr);                //排序
    }
    //需要從下向上調整
    static void buildHeap(int[] arr) {
        for(int i=(arr.length-2)/2;i>=0;i--) {
            adjustHeap(arr,i,arr.length);
        }
    }
    static void getSort(int[] arr) {
        for(int i=arr.length-1;i>=1;i--) {
            swap(arr,0,i);
            adjustHeap(arr,0,i);
        }
    }
    //i是調整節點為i的點,len是堆容量
    static void adjustHeap(int arr[],int i,int len) {
        while((i<<1|1)<len) {
            int left=i<<1|1,right=(i<<1)+2;
            if(right<len&&arr[right]>arr[i]&&arr[right]>arr[left]) {
                swap(arr,i,right);
                i=right;
            }
            else if(arr[left]>arr[i]) {
                swap(arr,i,left);
                i=left;
            }
            else
                break;
        }
    }
//=============================================================================================================
    
//快排=========================================================================================================
    /*
     * 安利一篇文章,挺不錯的
https://mp.weixin.qq.com/s?__biz=MzA5MzY4NTQwMA==&mid=2651005737&idx=1&sn=924250b9065f44f5f
8c7b026ff914fcc&chksm=8bad90debcda19c8fc016c5968d4c817b16c736c0a2c9a5de378ddac1c82b5c4df446
dbeb8e1&mpshare=1&scene=23&srcid=0910GbfDP3XhEWpCqgfK2Ffc#rd
     */
    static void quickSort(int[] arr) {
        quickSort(arr,0,arr.length-1);
    }
    static void quickSort(int[] arr,int l,int r) {
        if(l>=r)
            return;
        int privot=partition(arr,l,r);
        quickSort(arr,l,privot-1);
        quickSort(arr,privot+1,r);
    }
    static int partition(int[] arr,int start,int end) {
        int l=start,r=end;
        int pivot=arr[start];
        while(l!=r) {
            while(l<r&&arr[r]>pivot) {
                r--;
            }
            while(l<r&&arr[l]<=pivot) {
                l++;
            }
            if(l<r) {
                swap(arr,l,r);
            }
        }
        if(l!=start)
            swap(arr,l,start);
        return l;
    }
//=============================================================================================================
    
    //列印一個數組,方便測試
    static void print(int arr[]) {
        for(int i=0;i<arr.length;i++) {
            out.write(arr[i]+" ");
        }
        out.write("\n");
        out.flush();
    }
     //隨機生成一個數組,方便測試
    static void randomArrays(int arr[]) {
        for(int i=0;i<arr.length;i++)
            arr[i]=(int) (Math.random()*1000);
    }
    //隨機打亂一個數組,方便測試
    static void upsetArrays(int[] arr) {
        for(int i=0;i<arr.length;i++) {
            int t=(int) (Math.random()*arr.length);
            if(i!=t)
                swap(arr,i,t);
        }
    }
    //異或運算交換兩個變數,比較節省時間。
    static void swap(int arr[],int a,int b) {
        arr[a]^=arr[b];
        arr[b]^=arr[a];
        arr[a]^=arr[b];                    
    }
}
/*
 *1000的資料量
 *選擇排序的時間是:4ms
 *氣泡排序的時間是:5ms
 *希爾排序的時間是:1ms
 *歸併排序的時間是:1ms
 *堆排序的時間是:0ms
 *快排的時間是:1ms
 *JAVA類庫自帶快速的時間是:1ms

 *1w的資料量
 *選擇排序的時間是:59ms
 *氣泡排序的時間是:150ms
 *希爾排序的時間是:3ms
 *歸併排序的時間是:2ms
 *堆排序的時間是:3ms
 *快排的時間是:3ms
 *JAVA類庫自帶快速的時間是:5ms

 *5W的資料量
 *選擇排序的時間是:1391ms
 *氣泡排序的時間是:3770ms
 *希爾排序的時間是:10ms
 *歸併排序的時間是:11ms
 *堆排序的時間是:12ms
 *快排的時間是:20ms
 *JAVA類庫自帶快速的時間是:9ms
 
 *10W的資料量
 *選擇排序的時間是:4915ms
 *氣泡排序的時間是:15191ms
 *希爾排序的時間是:17ms
 *歸併排序的時間是:20ms
 *堆排序的時間是:17ms
 *快排的時間是:18ms
 *JAVA類庫自帶快速的時間是:10ms

 *1千萬的資料,選擇排序和氣泡排序不敢玩了,就搞下這些。
 *希爾排序的時間是:1835ms
 *歸併排序的時間是:1181ms
 *堆排序的時間是:2030ms
 *快排的時間是:15282ms
 *JAVA類庫自帶快速的時間是:500ms

 *由此可見,陣列的容量越大,差距越大,還是系統自帶的好用,哈哈哈
 */