算法(排序)

一、內(nèi)部排序

1、插入排序—直接插入排序(Straight Insertion Sort)
/**
 * 基本思想:
 * 將一個記錄插入到已排序好的有序表中,從而得到一個新,記錄數(shù)增1的有序表。即:先將序列的第1個記錄看成是一個
 * 有序的子序列,然后從第2個記錄逐個進(jìn)行插入,直至整個序列有序?yàn)橹埂? */
public class InsertSort {  
  
    public static void main(String[] args) {  
        int a[] = {3,1,5,7,2,4,9,6,10,8};    
        InsertSort  obj=new InsertSort();  
        System.out.println("初始值:");  
        obj.print(a);  
        obj.insertSort(a);  
        System.out.println("\n排序后:");  
        obj.print(a);  
  
    }  
  
    public void print(int a[]){  
        for(int i=0;i<a.length;i++){  
            System.out.print(a[i]+" ");  
        }  
    }  
    public void insertSort(int[] a) {  
        for(int i=1;i<a.length;i++){//從頭部第一個當(dāng)做已經(jīng)排好序的,把后面的一個一個的插到已經(jīng)排好的列表中去。  
            if(a[i]<a[i-1]){  
                int j;  
                int x=a[i];//x為待插入元素  
                a[i]=a[i-1];  
                for(j=i-1;  j>=0 && x<a[j];j--){//通過循環(huán),逐個后移一位找到要插入的位置。  
                    a[j+1]=a[j];  
                }  
                a[j+1]=x;//插入  
            }  
                  
        }  
          
    }  
}  
效率:時間復(fù)雜度:O(n^2).
2、插入排序—希爾排序(Shell`s Sort)
基本思想:
先將整個待排序的記錄序列分割成為若干子序列分別進(jìn)行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進(jìn)行依次直接插入排序
操作方法:

選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列個數(shù)k,對序列進(jìn)行k 趟排序;
每趟排序,根據(jù)對應(yīng)的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進(jìn)行直接插入排序。僅增量因子為
1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

package com;  
/* 
 * Java實(shí)現(xiàn)希爾排序(縮小增量排序) 
 * author:wyr 
 * 2016-7-14 
 *兩個步驟:1,建堆  2,對頂與堆的最后一個元素交換位置 
 */  
public class ShellSort {  
  
    public static void main(String[] args) {  
        int a[] = {3,1,5,7,2,4,9,6,10,8};    
        ShellSort  obj=new ShellSort();  
        System.out.println("初始值:");  
        obj.print(a);  
        obj.shellSort(a);  
        System.out.println("\n排序后:");  
        obj.print(a);  
  
    }  
    private void shellSort(int[] a) {  
         int dk = a.length/2;   
         while( dk >= 1  ){    
            ShellInsertSort(a, dk);    
            dk = dk/2;  
         }  
    }  
    private void ShellInsertSort(int[] a, int dk) {//類似插入排序,只是插入排序增量是1,這里增量是dk,把1換成dk就可以了  
        for(int i=dk;i<a.length;i++){  
            if(a[i]<a[i-dk]){  
                int j;  
                int x=a[i];//x為待插入元素  
                a[i]=a[i-dk];  
                for(j=i-dk;  j>=0 && x<a[j];j=j-dk){//通過循環(huán),逐個后移一位找到要插入的位置。  
                    a[j+dk]=a[j];  
                }  
                a[j+dk]=x;//插入  
            }  
                  
        }  
          
    }  
    public void print(int a[]){  
        for(int i=0;i<a.length;i++){  
            System.out.print(a[i]+" ");  
        }  
    }  
}  
希爾排序時效分析很難,關(guān)鍵碼的比較次數(shù)與記錄移動次數(shù)依賴于增量因子序列d的選取,特定情況下可以準(zhǔn)確估算出關(guān)鍵
碼的比較次數(shù)和記錄的移動次數(shù)。目前還沒有人給出選取最好的增量因子序列的方法。增量因子序列可以有各種取法,有
取奇數(shù)的,也有取質(zhì)數(shù)的,但需要注意:增量因子中除1 外沒有公因子,且最后一個增量因子必須為1。希爾排序方法是一
個不穩(wěn)定的排序方法。
3. 選擇排序—簡單選擇排序(Simple Selection Sort)
基本思想:

在要排序的一組數(shù)中,選出最小(或者最大)的一個數(shù)與第1個位置的數(shù)交換;然后在剩下的數(shù)當(dāng)中再找最小(或者最大)
的與第2個位置的數(shù)交換,依次類推,直到第n-1個元素(倒數(shù)第二個數(shù))和第n個元素(最后一個數(shù))比較為止。

操作方法:

第一趟,從n 個記錄中找出關(guān)鍵碼最小的記錄與第一個記錄交換;

第二趟,從第二個記錄開始的n-1 個記錄中再選出關(guān)鍵碼最小的記錄與第二個記錄交換;

以此類推.....

第i 趟,則從第i 個記錄開始的n-i+1 個記錄中選出關(guān)鍵碼最小的記錄與第i 個記錄交換,

直到整個序列按關(guān)鍵碼有序。
package com;  
/* 
 * Java實(shí)現(xiàn)希爾排序(縮小增量排序) 
 * author:wyr 
 * 2016-7-14 
 *兩個步驟:1,建堆  2,對頂與堆的最后一個元素交換位置 
 */  
public class SimpleSelectSort {  
  
    public static void main(String[] args) {  
        int a[] = {3,1,5,7,2,4,9,6,10,8};    
        SimpleSelectSort  obj=new SimpleSelectSort();  
        System.out.println("初始值:");  
        obj.print(a);  
        obj.selectSort(a);  
        System.out.println("\n排序后:");  
        obj.print(a);  
  
    }  
    private void selectSort(int[] a) {  
        for(int i=0;i<a.length;i++){  
            int k=i;//k存放最小值下標(biāo)。每次循環(huán)最小值下標(biāo)+1  
            for(int j=i+1;j<a.length;j++){//找到最小值下標(biāo)  
                if(a[k]>a[j])  
                    k=j;  
            }  
            swap(a,k,i);//把最小值放到它該放的位置上  
        }  
    }  
    public void print(int a[]){  
        for(int i=0;i<a.length;i++){  
            System.out.print(a[i]+" ");  
        }  
    }  
     public  void swap(int[] data, int i, int j) {    
            if (i == j) {    
                return;    
            }    
            data[i] = data[i] + data[j];    
            data[j] = data[i] - data[j];    
            data[i] = data[i] - data[j];    
        }    
}  

簡單選擇排序的改進(jìn)——二元選擇排序
簡單選擇排序,每趟循環(huán)只能確定一個元素排序后的定位。我們可以考慮改進(jìn)為每趟循環(huán)確定兩個元素(當(dāng)前趟最大和最小記錄)的位置,從而減少排序所需的循環(huán)次數(shù)。改進(jìn)后對n個數(shù)據(jù)進(jìn)行排序,最多只需進(jìn)行[n/2]趟循環(huán)即可。具體實(shí)現(xiàn)如下:
void SelectSort(int r[],int n) {  
    int i ,j , min ,max, tmp;  
    for (i=1 ;i <= n/2;i++) {    
        // 做不超過n/2趟選擇排序   
        min = i; max = i ; //分別記錄最大和最小關(guān)鍵字記錄位置  
        for (j= i+1; j<= n-i; j++) {  
            if (r[j] > r[max]) {   
                max = j ; continue ;   
            }    
            if (r[j]< r[min]) {   
                min = j ;   
            }     
      }    
      //該交換操作還可分情況討論以提高效率  
      tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp;  
      tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp;   
  
    }   
}  
4. 選擇排序—堆排序(Heap Sort)

1、基本思想:
  堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進(jìn)。
  堆的定義下:具有n個元素的序列 (h1,h2,...,hn),當(dāng)且僅當(dāng)滿足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)時稱之為堆。在這里只討論滿足前者條件的堆。由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項(xiàng)(大頂堆)。完全二 叉樹可以很直觀地表示堆的結(jié)構(gòu)。堆頂為根,其它為左子樹、右子樹。
  思想:初始時把要排序的數(shù)的序列看作是一棵順序存儲的二叉樹,調(diào)整它們的存儲序,使之成為一個 堆,這時堆的根節(jié)點(diǎn)的數(shù)最大。然后將根節(jié)點(diǎn)與堆的最后一個節(jié)點(diǎn)交換。然后對前面(n-1)個數(shù)重新調(diào)整使之成為堆。依此類推,直到只有兩個節(jié)點(diǎn)的堆,并對 它們作交換,最后得到有n個節(jié)點(diǎn)的有序序列。從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數(shù)組成。一是建堆的滲透函數(shù),二是反復(fù)調(diào)用滲透函數(shù)實(shí)現(xiàn)排序的函數(shù)。
2、實(shí)例
初始序列:46,79,56,38,40,84
  建堆:


  交換,從堆中踢出最大數(shù)

依次類推:最后堆中剩余的最后兩個結(jié)點(diǎn)交換,踢出一個,排序完成。

public class HeapSort {
    public static void main(String[] args) {
        int[] a={49,38,65,97,76,13,27,49,78,34,12,64};
        int arrayLength=a.length;  
        //循環(huán)建堆  
        for(int i=0;i<arrayLength-1;i++){  
            //建堆  
            buildMaxHeap(a,arrayLength-1-i);  
            //交換堆頂和最后一個元素  
            swap(a,0,arrayLength-1-i);  
            System.out.println(Arrays.toString(a));  
        }  
    }
    //對data數(shù)組從0到lastIndex建大頂堆
    public static void buildMaxHeap(int[] data, int lastIndex){
         //從lastIndex處節(jié)點(diǎn)(最后一個節(jié)點(diǎn))的父節(jié)點(diǎn)開始 
        for(int i=(lastIndex-1)/2;i>=0;i--){
            //k保存正在判斷的節(jié)點(diǎn) 
            int k=i;
            //如果當(dāng)前k節(jié)點(diǎn)的子節(jié)點(diǎn)存在  
            while(k*2+1<=lastIndex){
                //k節(jié)點(diǎn)的左子節(jié)點(diǎn)的索引 
                int biggerIndex=2*k+1;
                //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k節(jié)點(diǎn)的右子節(jié)點(diǎn)存在
                if(biggerIndex<lastIndex){  
                    //若果右子節(jié)點(diǎn)的值較大  
                    if(data[biggerIndex]<data[biggerIndex+1]){  
                        //biggerIndex總是記錄較大子節(jié)點(diǎn)的索引  
                        biggerIndex++;  
                    }  
                }  
                //如果k節(jié)點(diǎn)的值小于其較大的子節(jié)點(diǎn)的值  
                if(data[k]<data[biggerIndex]){  
                    //交換他們  
                    swap(data,k,biggerIndex);  
                    //將biggerIndex賦予k,開始while循環(huán)的下一次循環(huán),重新保證k節(jié)點(diǎn)的值大于其左右子節(jié)點(diǎn)的值  
                    k=biggerIndex;  
                }else{  
                    break;  
                }  
            }
        }
    }
    //交換
    private static void swap(int[] data, int i, int j) {  
        int tmp=data[i];  
        data[i]=data[j];  
        data[j]=tmp;  
    } 
}
5、交換排序(冒泡排序)
//冒泡排序中每一趟比較都會將最大的那個數(shù)找出來

public static void sort1(int[] arr) {
        boolean sorted = true;// 假定有序
        // 理論上會進(jìn)行arr.length-1趟比較
        for (int i = arr.length - 1; i > 0; i--) {
            //sorted = true;// 每一趟比較初始都假定數(shù)組有序
            for (int j = 0; j < i; j++) {
                // 每趟比較的次數(shù)為arr.length - i;
                if (arr[j] > arr[j + 1]) {
                    int tmp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = tmp;
                    sorted = false;
                }
            }
            if (sorted == true) {
                break;
            }
        }
}

說明:

1、冒泡排序就是每次都是前一個數(shù)和后一個數(shù)進(jìn)行比較,如果前面的數(shù)大,那么兩者就交換位置。
2、冒泡排序每次都會找出一個“最大數(shù)”,所以每趟比較的次數(shù)遞減,同時如果在某躺中沒有發(fā)生過交換,那么顯然數(shù)組就已
經(jīng)是有序的了,無序再進(jìn)行下一趟排序。時間復(fù)雜度為O(n^2)。平均時間復(fù)雜度為O(n^2),是一種穩(wěn)定的排序算法,空間復(fù)
雜度為O(1)。
6、交換排序(快速排序)
/**  
 * 快速排序<br/>  
 * <ul>  
 * <li>從數(shù)列中挑出一個元素,稱為“基準(zhǔn)”</li>  
 * <li>重新排序數(shù)列,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一邊)。在這個分割之后,  
 * 該基準(zhǔn)是它的最后位置。這個稱為分割(partition)操作。</li>  
 * <li>遞歸地把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。</li>  
 * </ul>  
 *   
 * @param numbers  
 * @param start  
 * @param end  
 */  
public static void quickSort(int[] numbers, int start, int end) {   
    if (start < end) {   
        int base = numbers[start]; // 選定的基準(zhǔn)值(第一個數(shù)值作為基準(zhǔn)值)   
        int temp; // 記錄臨時中間值   
        int i = start, j = end;   
        do {   
            while ((numbers[i] < base) && (i < end))   
                i++;   
            while ((numbers[j] > base) && (j > start))   
                j--;   
            if (i <= j) {   
                temp = numbers[i];   
                numbers[i] = numbers[j];   
                numbers[j] = temp;   
                i++;   
                j--;   
            }   
        } while (i <= j);   
        if (start < j)   
            quickSort(numbers, start, j);   
        if (end > i)   
            quickSort(numbers, i, end);   
    }   
}  
7、歸并排序算法

基本思想:
  歸并(Merge)排序法是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合并為整體有序序列。
歸并排序示例:

合并方法:

  1. 設(shè)r[i…n]由兩個有序子表r[i…m]和r[m+1…n]組成,兩個子表長度分別為n-i +1、n-m。j=m+1;k=i;i=i; //置兩個子表的起始下標(biāo)及輔助數(shù)組的起始下標(biāo)
  2. 若i>m 或j>n,轉(zhuǎn)⑷ //其中一個子表已合并完,比較選取結(jié)束
  3. //選取r[i]和r[j]較小的存入輔助數(shù)組rf如果r[i]<r[j],rf[k]=r[i]; i++; k++; 轉(zhuǎn)⑵否則,rf[k]=r[j]; j++; k++; 轉(zhuǎn)⑵
  4. //將尚未處理完的子表中元素存入rf如果i<=m,將r[i…m]存入rf[k…n] //前一子表非空如果j<=n , 將r[j…n] 存入rf[k…n] //后一子表非空
  5. 合并結(jié)束。

算法實(shí)現(xiàn):

/**
     * 歸并排序
     * 簡介:將兩個(或兩個以上)有序表合并成一個新的有序表 即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合并為整體有序序列
     * 時間復(fù)雜度為O(nlogn)
     * 穩(wěn)定排序方式
     * @param nums 待排序數(shù)組
     * @return 輸出有序數(shù)組
     */
    public static int[] sort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左邊
            sort(nums, low, mid);
            // 右邊
            sort(nums, mid + 1, high);
            // 左右歸并
            merge(nums, low, mid, high);
        }
        return nums;
    }

    /**
     * 將數(shù)組中l(wèi)ow到high位置的數(shù)進(jìn)行排序
     * @param nums 待排序數(shù)組
     * @param low 待排的開始位置
     * @param mid 待排中間位置
     * @param high 待排結(jié)束位置
     */
    public static void merge(int[] nums, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指針
        int j = mid + 1;// 右指針
        int k = 0;

        // 把較小的數(shù)先移到新數(shù)組中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }

        // 把左邊剩余的數(shù)移入數(shù)組
        while (i <= mid) {
            temp[k++] = nums[i++];
        }

        // 把右邊邊剩余的數(shù)移入數(shù)組
        while (j <= high) {
            temp[k++] = nums[j++];
        }

        // 把新數(shù)組中的數(shù)覆蓋nums數(shù)組
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,220評論 0 52
  • 概述:排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,743評論 0 15
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,282評論 0 2
  • 概述排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部的...
    Luc_閱讀 2,299評論 0 35
  • 我在輸出力學(xué)院畢業(yè)了 ——我的收獲 2017.6.20第一次大課高效輸入法,第一次聆聽阿秋的課,分別從行動模式、心...
    宏丫頭閱讀 270評論 0 0