java實現八大排序算法


Arrays.sort() 采用了2種排序算法 -- 基本類型數據使用快速排序法,對象數組使用歸並排序.

java的Collections.sort算法調用的是歸並排序,它是穩定排序

方法一:直接插入

1.基本思路:

在要排序的一組數中,假設前面(n-1) [n>=2] 個數已經是排好順序的,現在要把第n個數插到前面的有序數中,使得這n個數也是排好順序的。如此反復循環,直到全部排好順序。

2.代碼實現:

(1)首先設定插入次數,即循環次數,for(int i=1;i<length;i++),從第二個數字開始插入,排序好。再依次插入第三個。。。

(2)設定插入數和得到已經排好序列的最后一個數的位數。temp和j=i-1。

(3)從最后一個數開始向前循環,如果插入數小於當前數,就將當前數向后移動一位。

(4)將當前數放置到空着的位置,即j+1。

    public static void insertSort(int[] data){
        int temp;
        for(int i=1;i<data.length;i++){//取第i個數,插入前邊的有序的序列
            temp = data[i];
            int j;
            for(j =i-1;j>=0;j--){//從第i-1的位置上開始比較
                if(data[j]>temp){//若前面的數大,則往后挪一位
                    data[j+1] = data[j];
                }else {
                    break;//否則,說明要插入的數比較大
                }
            }
            data[j+1] = temp;//找到這個位置,插入數據
        }
    }

3.時間復雜度和空間復雜度:

直接插入排序的平均復雜度為O(n²),最壞時間復雜度:O(n²),空間復雜度:O(1),沒有分配內存。

方法二:希爾排序

1.定義:

針對直接插入排序的下效率問題,有人對次進行了改進與升級,這就是現在的希爾排序。希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序算法。

2.基本思路:

數的個數為length,i=length/2,將下標差值為i的數分為一組,構成有序序列。

再取i=i/2 ,將下標差值為i的數分為一組,構成有序序列。

重復第二步,直到k=1執行簡單插入排序。 

思路

(1)希爾排序(shell sort)這個排序方法又稱為縮小增量排序,是1959年D·L·Shell提出來的。該方法的基本思想是:設待排序元素序列有n個元素,首先取一個整數increment(小於n)作為間隔將全部元素分為increment個子序列,所有距離為increment的元素放在同一個子序列中,在每一個子序列中分別實行直接插入排序。然后縮小間隔increment,重復上述子序列划分和排序工作。直到最后取increment=1,將所有元素放在同一個子序列中排序為止。
(2)由於開始時,increment的取值較大,每個子序列中的元素較少,排序速度較快,到排序后期increment取值逐漸變小,子序列中元素個數逐漸增多,但由於前面工作的基礎,大多數元素已經基本有序,所以排序速度仍然很快。
(3)希爾排序舉例:

3.代碼實現:

(1)首先確定每一組序列的下標的間隔,循環每次需要的間隔:int i = length/2; i >0 ; i /= 2

(2)然后將每一組序列中元素進行插入排序,第二組第一個插入的數字是第一組第一個插入數字之后的那個數組,從i之后每個數字都要進行插入排序,就是插入的序列是各自不同的序列,不是一個一個子序列循環,而是在一個循環中for (int j=i;j<length;j++)完成所有子序列的插入排序。

(3)直到i=0為止。

    public static void shellSort(int[] array){
        int length = array.length;
        for (int i = length/2; i >0 ; i /= 2) {//序列的間隔,一直到間隔為一,這時候就只有一個子序列
            for (int j=i;j<length;j++){//從i之后每個數字都要進行插入排序,就是插入的序列是各自不同的序列
                int temp = array[j];//里面就是直接插入算法
                int k;
                for(k = j-i; k>=0;k -= i){//實現各個數字插入排序到不同的序列中,直到間隔為1的時候,只有一個序列,就是完全的一個直接插入排序
                    if(temp< array[k]){
                        array[k+i] = array[k];
                    }else{
                        break;
                    }

                }
                array[k+i] = temp;//把數字插入到位置上
            }
        }
        System.out.println(Arrays.toString(array));
    }

4.時間復雜度和空間復雜度:

希爾排序的平均時間復雜度為O(n²),空間復雜度O(1) 。

方法三:簡單選擇

1.基本思路:

基本原理如下:對於給定的一組記錄,經過第一輪比較后得到最小的記錄,然后將該記錄的位置第一個記錄的位置交換;接着對不包括第一個記錄以外的其他記錄進行第二次比較,得到最小記錄並與第二個位置記錄交換;重復該過程,知道進行比較的記錄只剩下一個為止。

2.代碼實現:

(1)確定要插入最小值的位置,從0開始到最后int i = 0; i <len ; i++

(2)將每次開始位置上的數字暫定為最小值min,從開始數字之后一個個和min比較,再把最小值存放到min

(3)將最小值所在位置上的數字和開始位置上的數字交換

    public static void selectSort(int[] array){
        int len = array.length;
        for (int i = 0; i <len ; i++) {//確定每次開始的位置

            int min = array[i];//設定開始數字為最小的值最小值
            int flag = i;
            for(int j=i+1;j<len;j++){//把最小值存放到min,從開始數字向后一個個和min比較,再把最小值存放到min
                if(min>array[j]){
                    min = array[j];
                    flag = j;
                }
            }
            if(flag != i){
                array[flag] = array[i];
                array[i] = min;
            }


        }
        
    }

3.時間復雜度:

簡單選擇排序時間復雜度:O(n^2)

方法四:堆排序

1.基本思路:

(1)若array[0,...,n-1]表示一顆完全二叉樹的順序存儲模式,則雙親節點指針和孩子結點指針之間的內在關系如下:

  任意一節點指針 i:父節點:i==0 ? null : (i-1)/2

            左孩子:2*i + 1

            右孩子:2*i + 2

(2)堆的定義:n個關鍵字序列array[0,...,n-1],當且僅當滿足下列要求:(0 <= i <= (n-1)/2)

      ① array[i] <= array[2*i + 1] 且 array[i] <= array[2*i + 2]; 稱為小根堆;

      ② array[i] >= array[2*i + 1] 且 array[i] >= array[2*i + 2]; 稱為大根堆;

(3)建立大根堆:

  n個節點的完全二叉樹array[0,...,n-1],最后一個節點n-1是第(n-1-1)/2個節點的孩子。對第(n-1-1)/2個節點為根的子樹調整,使該子樹稱為堆。

  對於大根堆,調整方法為:若【根節點的關鍵字】小於【左右子女中關鍵字較大者】,則交換。

  之后向前依次對各節點((n-2)/2 - 1)~ 0為根的子樹進行調整,看該節點值是否大於其左右子節點的值,若不是,將左右子節點中較大值與之交換,交換后可能會破壞下一級堆,於是繼續采用上述方法構建下一級的堆,直到以該節點為根的子樹構成堆為止。

  反復利用上述調整堆的方法建堆,直到根節點。

(4)堆排序:(大根堆)

  ①將存放在array[0,...,n-1]中的n個元素建成初始堆;

  ②將堆頂元素與堆底元素進行交換,則序列的最大值即已放到正確的位置;

  ③將數組中array[0,...,n-1]前n-1個元素再次形成大根堆,再重復第②③步,直到堆中僅剩下一個元素為止。

2.代碼實現:

    public static int[] buildMaxHeap(int[] array,int length){//截取數組從0到length,構建大根堆

        for (int i = (length-2)/2; i >=0 ; i--) {//從最后一個有子結點的結點開始調整,(減一)一直調節到根節點

            adjustDownToUp(array,i,length);

        }
        return array;
    }

    private static void adjustDownToUp(int[] array, int i, int length) {//i要調整的結點
        int temp = array[i];
        //int j = i*2+1;//取得左孩子
        for (int j=i*2+1;j<length;j=j*2+1){//從該節點一直調節到葉子結點,因為上層調節改變之后,會影響下層結構,所以一直循環到葉子結點。
            if(j+1<length && array[j]<array[j+1]){//有又孩子,且右孩子數值更大
                j++;//取更大結點的索引
            }
            if(temp<array[j]){//根節點比最大的值小
                array[i] = array[j];//調整根節點的值為最大值
                i=j;//把空余的位置給i
                //array[j] = temp;
            }
        }
        array[i] = temp;//找到最后空余的位置,填上向下調整的值

    }
    public static void heapSort(int[] array){
        int len = array.length;
        int temp;
        for (int i = len; i >0; i--) {
            array=buildMaxHeap(array,i);
            temp = array[0];
            array[0] = array[i-1];
            array[i-1]=temp;
        }
        System.out.println(Arrays.toString(array));

    }

3.時間復雜度:

時間復雜度:建堆:o(n),每次調整o(log n),故最好、最壞、平均情況下:o(n*logn);

方法五:冒泡排序

1.基本思路:

一次冒泡將序列中從頭到尾所有元素兩兩比較,將最大的放在最后面。

將剩余序列中所有元素再次兩兩比較,將最大的放在最后面。

重復第二步,直到只剩下一個數。

2.代碼實現:

	public static void bubbleSort(int[] array){
		for (int i = 0; i <array.length ; i++) {//第i冒泡,一次冒泡,會確定一個最大值
			for (int j = 0; j <array.length-i-1 ; j++) {//從頭一直到已經確定的位置前,兩兩比較
				int temp = array[j];
				if(array[j]>array[j+1]){
					array[j]=array[j+1];
					array[j+1]=temp;
				}
			}
		}
	}

3.時間復雜度:

冒泡排序的時間復雜度為O(n^2),空間復雜度為O(1),它是一種穩定的排序算法。

方法六:快排

1.基本思路:

快速排序使用分治策略來把一個序列(list)分為兩個子序列(sub-lists)。步驟為:

  1. 從數列中挑出一個元素,稱為"基准"(pivot)。
  2. 重新排序數列,所有比基准值小的元素擺放在基准前面,所有比基准值大的元素擺在基准后面(相同的數可以到任一邊)。在這個分區結束之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作。
  3. 遞歸地(recursively)把小於基准值元素的子數列和大於基准值元素的子數列排序。

遞歸到最底部時,數列的大小是零或一,也就是已經排序好了。這個算法一定會結束,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最后的位置去。

2.代碼實現:

public static void quickSprt(int[] array,int low,int high){
        if(low>=high) return;
        int left = low;
        int right = high;
        int pivot = array[left];//設立基准點
        while (left<right){
            while (left<right && array[right]>pivot)//從右向左,大數位置不變
                right--;
            array[left] = array[right];//把小數移到左邊

            while (left < right && array[left]<pivot) //從左向右,小數位置不變
                left++;
            array[right] = array[left];//把大數移到右邊

        }

        array[left]=pivot;
        quickSprt(array,low,left-1);
        quickSprt(array,left+1,high);

    }

3.時間復雜度:

雖然 快排的時間復雜度達到了 O(n²),但是在大多數情況下都比平均時間復雜度為 O(n logn) 的排序算法表現要更好。

方法七:歸並排序

1.基本思路:

對於給定的一組記錄,利用遞歸與分治技術將數據序列划分成為越來越小的半子表(直到剩余一個數字),在對半子表排序,最后再用遞歸方法將排好序的半子表合並成為越來越大的有序序列。 

(1)申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並后的序列 

(2)設定兩個指針,最初位置分別為兩個已經排序序列的起始位置 

(3)比較兩個指針所指向的元素,選擇相對小的元素放入到合並空間,並移動指針到下一位置 

(4)重復步驟3直到某一指針達到序列尾 

(5)將另一序列剩下的所有元素直接復制到合並序列尾

2.代碼實現:

    //歸並排序
    public static int[] mergeSort(int[] array,int low,int high){
        if(low<high){
            int mid = (low+high)/2;
            mergeSort(array,low,mid);
            mergeSort(array,mid+1,high);
            merge(array,low,mid,high);//歸並
        }
        return array;
    }

    private static void merge(int[] array, int low, int mid, int high) {
        int i = low;//指針,前一個序列的頭指針
        int j = mid+1;//指針,后一個序列的頭指針
        int[] temp = new int[high-low+1];
        int k=0;
        while (i<=mid && j<= high){
            if (array[i]<array[j]){//從頭比較兩個序列,小的放入臨時數組temp
                temp[k++] = array[i++];//前一個序列指針后移一位
            }else {
                temp[k++] = array[j++];//后一個序列指針后移一位
            }

        }
        //最后只會剩下一組序列
        while (i<=mid){
            temp[k++] = array[i++];//把前一個指針剩余的數字放入臨時數組
        }
        while (j<=high){
            temp[k++] = array[j++];//把后一個指針剩余的數字放入臨時數組
        }

        for (int m = 0; m <high-low+1 ; m++) {
            array[low+m] = temp[m];
        }

    }

 

3.時間復雜度:

時間復雜度:O(nlog2n)

方法八:基數排序

1.基本思路:

(1)按照所有的數的個位數,在0---9 的基數序列中,把各個數字排入,每一個數字基數上也是一個序列。

(2)將新序列取出按基數順序取出,形成新數組。

(3)再按照十位數字進行排序,插入到基數序列,循環(1)(2)

2.代碼實現:

public static void baseSort(int[] array){
        ArrayList<ArrayList<Integer>> queue = new ArrayList<ArrayList<Integer>>();
        for (int i = 0; i <10 ; i++) {
            ArrayList<Integer> queue2 = new ArrayList<Integer>();
            queue.add(queue2);//創建一個基數從0---9 每個數字上都是一個list
        }
        //找到最大值,並判斷最大值是幾位數
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max<array[i]){
                max = array[i];
            }
        }
        int time = 0;
        while(max>0){
            max /= 10;
            time++;
        }
        for (int i = 0; i < time; i++) {//循環每一個位數(個位、十位、百位)
            for (int j = 0; j <array.length ; j++) {//循環數組,取每一個值
                int x = array[j] % (int)Math.pow(10,i+1) / (int)Math.pow(10,i);
                ArrayList<Integer> queue3 = queue.get(x);;
                queue3.add(array[j]);
                queue.set(x,queue3);
            }

            int count = 0;
            for (int k = 0; k < 10; k++) {
                while (queue.get(k).size() > 0) {
                    ArrayList<Integer> queue4 = queue.get(k);
                    array[count] = queue4.get(0);
                    queue4.remove(0);
                    count++;
                }
            }

        }

    }

3.時間復雜度:

時間復雜度:O(nlog2n)

總結:

排序算法 思路 排序算法 最好時間復雜度 平均時間復雜度 最壞時間復雜度

空間復雜度

(輔助存儲)

是否穩定
插入排序 將一個數字插入已經排列好的序列中 直接插入法 O(N) O(N2) O(N2) O(1) 穩定
 希爾排序 O(N)  O(N1.3) O(N2  O(1)  不穩定
選擇排序 每一次循環都是選擇出最小(最大)的數字放到數組最前面(最后面)的位置  簡單選擇  O(N)  O(N2) O(N2) O(1) 不穩定
堆排序   O(N*log2N) O(N*log2N)   O(N*log2N) O(1) 不穩定
交換排序

(冒泡)兩兩交換,大的后移,再次兩兩交換  

(快速)和基准交換,比基准大排右邊,比基准小排左邊

冒泡排序 O(N)   O(N2) O(N2)  O(1) 穩定
快速排序  O(N*log2N) O(N*log2N)    O(N2 O(log2n)~O(n)  不穩定
歸並排序  O(N*log2N)    O(N*log2N) O(N*log2N)   O(n)  穩定
基數排序  O(d(r+n))   O(d(r+n)) O(d(r+n))  O(rd+n)  穩定


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM