數據結構和算法(3)-- 排序算法

3、排序算法

1)內部排序:

歸并排序沪么、交換排序(冒泡排序硼婿、快排)、選擇排序禽车、插入排序


冒泡排序

(1)比較相鄰的元素寇漫。如果第一個比第二個大,就交換他們兩個殉摔。

(2)對每一對相鄰元素作同樣的工作猪腕,從開始第一對到結尾的最后一對。這步做完后钦勘,最后的元素會是最大的數陋葡。

(3)針對所有的元素重復以上的步驟,除了最后一個彻采。

(4)持續(xù)每次對越來越少的元素重復上面的步驟腐缤,直到沒有任何一對數字需要比較。

時間復雜度:O(n^2)肛响,最優(yōu)時間復雜度:O(n)岭粤,平均時間復雜度:O(n^2)

public static void bubbleSort(Comparable[] a) {

??? int j, flag;

??? Comparable temp;

??? for (int i = 0; i

??????? flag = 0;

??????? for (j = 1; j

??????????? if(a[j].compareTo(a[j - 1]) < 0) {

??????????????? temp = a[j];

??????????????? a[j] = a[j -1];

??????????????? a[j - 1] =temp;

??????????????? flag = 1;

??????????? }

??????? }

??????? //如果沒有交換,代表已經排序完畢特笋,直接返回

??????? if (flag == 0) {

????? ??????return;

??????? }

??? }

}


插入排序

(1)從第一個元素開始剃浇,該元素可以認為已經被排序

(2)取出下一個元素,在已經排序的元素序列中從后向前掃描

(3)如果該元素(已排序)大于新元素猎物,將該元素移到下一位置

(4)重復步驟3虎囚,直到找到已排序的元素小于或者等于新元素的位置

(5)將新元素插入到該位置后

(6)重復步驟2~5

時間復雜度:O(n^2),最優(yōu)時間復雜度:O(n),平均時間復雜度:O(n^2)

public static void insertionSort(Comparable[] a) {

??? int length = a.length;

??? Comparable temp;

??? for (int i = 1; i??< length; i++) {

??????? for (int j = i; j> 0 && a[j].compareTo(a[j - 1]) < 0; j--) {

??????????? temp = a[j];

??????????? a[j] = a[j - 1];

??????????? a[j - 1] = temp;

??????? }

??? }

}

// 對實現Comparable的類型進行排序蔫磨,先將大的元素都向右移動淘讥,減少一半交換次數

public static void insertionSort(Comparable[] a) {

??? int length = a.length;

??? Comparable temp;

??? int j;

??? for (int i = 1; i??< length; i++) {

? ? ? temp = a[i];

??????? for (j = i; j > 0&& temp.compareTo(a[j - 1]) < 0; j--) {

??????????? a[j] = a[j - 1];

??????? }

??????? a[j] = temp;

??? }

}


選擇排序

首先在未排序序列中找到最小元素,存放到排序序列的起始位置堤如,然后蒲列,再從剩余未排序元素中繼續(xù)尋找最小元素窒朋,然后放到已排序序列的末尾。

時間復雜度:O(n^2)蝗岖,最優(yōu)時間復雜度:O(n^2)侥猩,平均時間復雜度:O(n^2)

public static Integer[] selectSort(Integer[] arr)

{

??? List sortList = new ArrayList<>();

??? List numList =?new LinkedList(Arrays.asList(arr));

? ? int len = arr.length;

? ?for (int i = 0; i < len; i++)

??? {

? ? ? ? int smallest =findSmallest(numList);

??????? arr[i] = numList.get(smallest);

??????? numList.remove(smallest);

??? }

return arr;

}

private static int findSmallest(List numList)

{

int smallest = numList.get(0);

int smallestIndex = 0;

int len = numList.size();

for (int i = 0; i < len; i++)

??? {

if (numList.get(i) < smallest)

??????? {

??????????? smallest = numList.get(i);

??????????? smallestIndex = i;

??????? }

??? }

return smallestIndex;

}


希爾排序

希爾排序通過將比較的全部元素分為幾個區(qū)域來提升插入排序的性能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步抵赢。然后算法再取越來越小的步長進行排序拭宁,算法的最后一步就是普通的插入排序,但是到了這步瓣俯,需排序的數據幾乎是已排好的了(此時插入排序較快)。

時間復雜度:根據步長而不同兵怯,最優(yōu)時間復雜度:O(n),平均時間復雜度:根據步長而不同

public static void shellSort(Comparable[] a) {

??? int length = a.length;

??? int h = 1;

??? Comparable temp;

??? while (h < length /3) {

??????? h = 3 * h + 1;

??? }

??? while (h >= 1) {

??????? for (int i = h; i< length; i++) {

??????????? for (int j = i;j >= h && a[j].compareTo(a[j - h]) < 0; j -= h) {

??????????????? temp = a[j];

??????????????? a[j] = a[j -h];

??????????????? a[j - h] =temp;

??????????? }

??????? }

??????? h /= 3;

??? }

}


堆排序

(1)創(chuàng)建最大堆(Build_Max_Heap):將堆所有數據重新排序

(2)堆排序(HeapSort):移除位在第一個數據的根節(jié)點彩匕,并做最大堆調整的遞歸運算

時間復雜度:O(nlogn),最優(yōu)時間復雜度:O(nlogn),平均時間復雜度:O(nlogn)

public static void heapSort(Comparable[] a) {

??? int length = a.length;

??? Comparable temp;

??? for (int k = length / 2;k >= 1; k--) {

??????? sink(a, k, length);

??? }

??? while (length > 0) {

??????? temp = a[0];

??????? a[0] = a[length -1];

??????? a[length - 1] =temp;

??????? length--;

??????? sink(a, 1, length);

??? }

}


private static void sink(Comparable[] a, int k, int n) {

??? Comparable temp;

??? while (2 * k <= n) {

??????? int j = 2 * k;

??????? if (j < n&& a[j - 1].compareTo(a[j]) < 0) {

?????? ?????j++;

??????? }

??????? if (a[k -1].compareTo(a[j - 1]) >= 0) {

??????????? break;

??????? }

??????? temp = a[k - 1];

??????? a[k - 1] = a[j - 1];

??????? a[j - 1] = temp;

??????? k = j;

??? }

}


歸并排序

歸并操作(merge)媒区,也叫歸并算法驼仪,指的是將兩個已經排序的序列合并成一個序列的操作。歸并排序算法依賴歸并操作袜漩。

時間復雜度:O(nlogn)绪爸,最優(yōu)時間復雜度:O(n),平均時間復雜度:O(nlogn),空間復雜度O(n)

private staticComparable[] aux;

// 自頂向下

public staticvoid mergeSort(Comparable[] a) {

??? aux = new Comparable[a.length];

??? mergeSort(a, 0, a.length - 1);

}

public static void mergeSort(Comparable[] a, int lo, int hi) {

??? if (hi <= lo) {

??????? return;

??? }

??? int mid = (lo + hi)>>> 1;

??? mergeSort(a, lo, mid);

??? mergeSort(a, mid + 1,hi);

??? merge(a, lo, mid, hi);

}

public static void merge(Comparable[] a, int lo, int mid, int hi){

??? int i = lo, j = mid + 1;

??? for (int k = lo; k <=hi; k++) {

??????? aux[k] = a[k];

??? }

??? for (int k = lo; k <=hi; k++) {

??????? if (i > mid) {

??????????? a[k] = aux[j++];

??????? } else if (j >hi) {

??????????? a[k] = aux[i++];

??????? } else if(aux[j].compareTo(aux[i]) < 0) {

??????????? a[k] = aux[j++];

??????? } else {

??????????? a[k] = aux[i++];

??????? }

??? }

}


快速排序

快排步驟:

?*(1)選擇基準值;

?*(2)將數組分為兩個子數組:小于基準值的元素和大于基準值的元素宙攻;

?*(3)對這兩個子數組進行快速排序奠货。


時間復雜度:O(n^2),最優(yōu)時間復雜度:O(nlogn),平均時間復雜度:O(nlogn)

快排的時間復雜度跟選取基準的方法有關座掘,一下是默認選擇了第一個元素作為基準递惋,隨機性較大。

可以在序列中選取開始中間結尾三個數的中位數作為基準溢陪,進行優(yōu)化萍虽。

public static List quickSort(List list)

{

int length = list.size();

if (length < 2)

??? {

??????? System.out.println("The size of list less than 2!");

? ? ? ? return list; // 基線條件:為空或者只包含一個元素的數組是有序

??? }

int pivot = list.get(length / 2); // 遞歸條件,如果總是將第一個元素用作基準值形真,則最差算法運行時間為On^2,如果選擇中間元素為基準值則最佳算法時間為Onlogn)杉编,每次隨機選擇基準值則平均算法時間為Onlogn

??? List<Integer> less = new ArrayList<>();

??? List greater =?new ArrayList<>();

??? list.remove(length / 2);

for (int element : list)

??? {

if (element < pivot)

??????? {

??????????? less.add(element);

// 由所有小于基準值的元素組成的子數組

??????? }

else

??? ????{

??????????? greater.add(element);

// 由所有大于基準值的元素組成的子數組

??????? }

??? }

??? System.out.println("less is " + less);

??? List lessSort =quickSort(less);

// 對小于基準值的子數組遞歸執(zhí)行快速排序

??? List greaterSort =quickSort(greater);// 對大于基準值的子數組遞歸執(zhí)行快速排序

??? lessSort.add(pivot); // 組合小于基準值的子數組、基準值咆霜、大于基準值的子數組

??? lessSort.addAll(greaterSort);

return lessSort;

}

public static void main(String[] args)

{

??? List list =?new ArrayList<>();

??? list.add(9);

??? list.add(6);

??? list.add(0);

??? list.add(10);

??? list.add(-1);

??? list.add(2);

??? System.out.println(quickSort(list));

??? List linkedList =?new ArrayList<>(Arrays.asList(new Integer[] { 1, 2, 3 }));

??? linkedList.remove(1);

??? System.out.println(linkedList);

}


2)外部排序:

掌握利用內存和外部存儲處理超大數據集邓馒,至少要理解過程和思路、排序的穩(wěn)定性和效率等蛾坯。

1绒净、定義問題

??? 外部排序指的是大文件的排序,即待排序的記錄存儲在外存儲器上偿衰,待排序的文件無法一次裝入內存挂疆,需要在內存和外部存儲器之間進行多次數據交換改览,以達到排序整個文件的目的。外部排序最常用的算法是多路歸并排序缤言,即將原文件分解成多個能夠一次性裝入內存的部分宝当,分別把每一部分調入內存完成排序。然后胆萧,對已經排序的子文件進行多路歸并排序庆揩。

2、處理過程

? (1)按可用內存的大小跌穗,把外存上含有n個記錄的文件分成若干個長度為L的子文件订晌,把這些子文件依次讀入內存,并利用有效的內部排序方法對它們進行排序蚌吸,再將排序后得到的有序子文件重新寫入外存锈拨;

? (2)對這些有序子文件逐趟歸并,使其逐漸由小到大羹唠,直至得到整個有序文件為止奕枢。

內存排序環(huán)節(jié):磁盤中的數據序列被分割成多個段(假定內存有限)讀入到內存中,在內存中用模板sort實現排序過程佩微,效率高缝彬!

多路歸并排序環(huán)節(jié):依次從從每個已排序的段文件中(什么是段文件,看上面的內存排序環(huán)節(jié)哺眯,形象了點9惹场!)讀入一個數據奶卓,注意是一個壳贪;挑選最小的寫入的目標文件中。

假設文件需要分成k塊讀入寝杖,需要從小到大進行排序违施。

(1)依次讀入每個文件塊,在內存中對當前文件塊進行排序(應用恰當的內排序算法)瑟幕。此時磕蒲,每塊文件相當于一個由小到大排列的有序隊列。

(2)在內存中建立一個最小值堆只盹,讀入每塊文件的隊列頭辣往。

(3)彈出堆頂元素,如果元素來自第i塊殖卑,則從第i塊文件中補充一個元素到最小值堆站削。彈出的元素暫存至臨時數組。

(4)當臨時數組存滿時孵稽,將數組寫至磁盤许起,并清空數組內容十偶。

(5)重復過程(3)、(4)园细,直至所有文件塊讀取完畢惦积。

? 先從一個例子來看外排序中的歸并是如何進行的?

? 假設有一個含10000個記錄的文件猛频,首先通過10次內部排序得到10個初始歸并段R1~R10狮崩,其中每一段都含1000個記錄。然后對它們作如圖10.11所示的兩兩歸并鹿寻,直至得到一個有序文件為止如下圖

多路歸并排序

多路歸并排序算法在常見數據結構書中都有涉及睦柴。從2路到多路(k路),增大k可以減少外存信息讀寫時間毡熏,但k個歸并段中選取最小的記錄需要比較k-1次坦敌,為得到u個記錄的一個有序段共需要(u-1)(k-1)次,若歸并趟數為s次招刹,那么對n個記錄的文件進行外排時,內部歸并過程中進行的總的比較次數為s(n-1)(k-1)窝趣,若共有m個歸并段疯暑,則s=logkm,所以總的比較次數為: (向上取整)(logkm)(k-1)(n-1)=(向上取整)(log2m/log2k)(k-1)(n-1),而(k-1)/log2k隨k增而增因此內部歸并時間隨k增長而增長了哑舒,抵消了外存讀寫減少的時間妇拯,這樣做不行,由此引出了“敗者樹”tree of loser的使用洗鸵。在內部歸并過程中利用敗者樹將k個歸并段中選取最小記錄比較的次數降為(向上取整)(log2k)次使總比較次數為(向上取整)(log2m)(n-1)越锈,與k無關。

多路歸并排序算法的過程大致為:

1)首先將k個歸并段中的首元素關鍵字依次存入b[0]--b[k-1]的葉子結點空間里膘滨,然后調用CreateLoserTree創(chuàng)建敗者樹甘凭,創(chuàng)建完畢之后最小的關鍵字下標(即所在歸并段的序號)便被存入ls[0]中。然后不斷循環(huán):

2)把ls[0]所存最小關鍵字來自于哪個歸并段的序號得到為q火邓,將該歸并段的首元素輸出到有序歸并段里丹弱,然后把下一個元素關鍵字放入上一個元素本來所 在的葉子結點b[q]中,調用Adjust順著b[q]這個葉子結點往上調整敗者樹直到新的最小的關鍵字被選出來铲咨,其下標同樣存在ls[0]中躲胳。循環(huán)這個 操作過程直至所有元素被寫到有序歸并段里。

敗者樹

葉子節(jié)點記錄k個段中的最小數據纤勒,然后兩兩進行比賽坯苹。敗者樹是在雙親節(jié)點中記錄下剛剛進行完的這場比賽的敗者,讓勝者去參加更高一層的比賽摇天。決賽粹湃,根節(jié)點記錄輸者恐仑,所以需要重建一個新的根節(jié)點,記錄勝者(如下圖節(jié)點0)再芋。

示例:我們這里以四路歸并為例菊霜,假設每個歸并段已經在輸入緩沖區(qū)如下圖。

每路的第一個元素為勝利樹的葉子節(jié)點济赎,(5,7)比較出5勝出7失敗成為其根節(jié)點鉴逞,(29,9)比較9勝出29失敗成為其根節(jié)點,勝者(5,9)進行下次的比賽9失敗成為其根節(jié)點5勝出輸出到輸出緩沖區(qū)司训。由第一路歸并段輸出构捡,所有將第一路歸并段的第二個元素加到葉子節(jié)點如下圖:

加入葉子節(jié)點16進行第二次的比較,跟勝利樹一樣壳猜,由于右子樹葉子節(jié)點沒有發(fā)生變化其右子樹不用再繼續(xù)比較勾徽。


位圖算法:

問題描述:

輸入:給定一個文件,里面最多含有n個不重復的正整數(也就是說可能含有少于n個不重復正整數)统扳,且其中每個數都小于等于n喘帚,n=10^7。

輸出:得到按從小到大升序排列的包含所有輸入的整數的列表咒钟。

條件:最多有大約1MB的內存空間可用吹由,但磁盤空間足夠。且要求運行時間在5分鐘以下朱嘴,10秒為最佳結果倾鲫。

位圖方案。熟悉位圖的朋友可能會想到用位圖來表示這個文件集合萍嬉。例如正如編程珠璣一書上所述乌昔,用一個20位長的字符串來表示一個所有元素都小于20的簡單的非負整數集合,邊框用如下字符串來表示集合{1,2,3,5,8,13}:

0 1 1 1 0 1 0 01 0 0 0 0 1 0 0 0 0 0 0

上述集合中各數對應的位置則置1壤追,沒有對應的數的位置則置0磕道。

參考編程珠璣一書上的位圖方案,針對我們的10^7個數據量的磁盤文件排序問題行冰,我們可以這么考慮捅厂,由于每個7位十進制整數表示一個小于1000萬的整數。我們可以使用一個具有1000萬個位的字符串來表示這個文件资柔,其中焙贷,當且僅當整數i在文件中存在時,第i位為1贿堰。采取這個位圖的方案是因為我們面對的這個問題的特殊性:1辙芍、輸入數據限制在相對較小的范圍內,2、數據沒有重復故硅,3庶灿、其中的每條記錄都是單一的整數,沒有任何其它與之關聯的數據吃衅。

所以往踢,此問題用位圖的方案分為以下三步進行解決:

第一步,將所有的位都置為0徘层,從而將集合初始化為空峻呕。

第二步,通過讀入文件中的每個整數來建立集合趣效,將每個對應的位都置為1瘦癌。

第三步,檢驗每一位跷敬,如果該位為1讯私,就輸出對應的整數。

經過以上三步后西傀,產生有序的輸出文件斤寇。令n為位圖向量中的位數(本例中為1000 0000),程序可以用偽代碼表示如下:

上述的位圖方案拥褂,共需要掃描輸入數據兩次娘锁,具體執(zhí)行步驟如下:

第一次,只處理1—4999999之間的數據肿仑,這些數都是小于5000000的致盟,對這些數進行位圖排序碎税,只需要約5000000/8=625000Byte尤慰,也就是0.625M,排序后輸出雷蹂。

第二次伟端,掃描輸入文件時,只處理4999999-10000000的數據項匪煌,也只需要0.625M(可以使用第一次處理申請的內存)责蝠。

因此,總共也只需要0.625M

位圖的的方法有必要強調一下萎庭,就是位圖的適用范圍為針對不重復的數據進行排序霜医,若數據有重復,位圖方案就不適用了驳规。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末肴敛,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌医男,老刑警劉巖砸狞,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異镀梭,居然都是意外死亡刀森,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門报账,熙熙樓的掌柜王于貴愁眉苦臉地迎上來研底,“玉大人,你說我怎么就攤上這事笙什∑冢” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵琐凭,是天一觀的道長芽隆。 經常有香客問我,道長统屈,這世上最難降的妖魔是什么胚吁? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮愁憔,結果婚禮上腕扶,老公的妹妹穿的比我還像新娘。我一直安慰自己吨掌,他們只是感情好半抱,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膜宋,像睡著了一般窿侈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秋茫,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天史简,我揣著相機與錄音,去河邊找鬼肛著。 笑死圆兵,一個胖子當著我的面吹牛蜘醋,可吹牛的內容都是我干的葵孤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蟀淮,長吁一口氣:“原來是場噩夢啊……” “哼局荚!你這毒婦竟也來了超凳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聪建,沒想到半個月后钙畔,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡金麸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年擎析,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挥下。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棚瘟,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情偎蘸,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布迷雪,位于F島的核電站限书,受9級特大地震影響,放射性物質發(fā)生泄漏倦西。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一扰柠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疼约,春花似錦卤档、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昙沦,卻和暖如春载荔,著一層夾襖步出監(jiān)牢的瞬間盾饮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工普办, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衔蹲。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓呈础,卻偏偏與公主長得像,于是被迫代替她去往敵國和親而钞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容

  • 查找和排序都是程序設計中經常用到的算法撬陵。查找相對而言較為簡單网缝,不外乎順序查找、二分查找途凫、哈希表查找和二叉排序樹查找...
    eagleRock閱讀 5,568評論 0 14
  • 在C語言中,五種基本數據類型存儲空間長度的排列順序是: A)char B)char=int<=float C)ch...
    夏天再來閱讀 3,333評論 0 2
  • 排序算法總結 分類編程技術 排序算法平均時間復雜度 冒泡排序O(n2) 選擇排序O(n2) 插入排序O(n2) 希...
    Zhs_Android閱讀 199評論 0 0
  • 首先總結以下Java和C维费、C++中的一般控制臺輸入方式,方便以后的編程題: java鍵盤輸入 java讀文件(會自...
    androidjp閱讀 2,281評論 0 16
  • 大端&小端 用C/C++寫網絡程序時而晒,要注意字節(jié)的網絡順序和主機順序的問題。 大端:高位在前倡怎,低位在后 小端:高位...
    zhimingcow閱讀 658評論 0 0