關(guān)于數(shù)據(jù)結(jié)構(gòu)排序?qū)W習(xí)

概述

排序算法分類

在我們?nèi)粘L幚頂?shù)據(jù)的時候外构,排序是最經(jīng)常用到,如果一層層的嵌套for循環(huán)會讓代碼的效率變得非常低播掷,這個時候审编,我們就要借用排序的理念來優(yōu)化我們的代碼,目前有十個比較經(jīng)典的排序算法:
1歧匈、冒泡排序
2垒酬、快速排序
3、插入排序
4件炉、希爾排序
5勘究、選擇排序
6、堆排序
7斟冕、歸并排序
8口糕、計數(shù)排序
9、桶排序
10磕蛇、基數(shù)排序
具體的如下圖所示:(圖轉(zhuǎn)自https://www.cnblogs.com/onepixel/articles/7674659.html

image.png

非線性時間比較類排序:通過比較來決定元素間的相對次序景描,由于其時間復(fù)雜度不能突破O(nlogn),因此稱為非線性時間比較類排序秀撇。
線性時間非比較類排序:不通過比較來決定元素間的相對次序超棺,它可以突破基于比較排序的時間下界,以線性時間運行呵燕,因此稱為線性時間非比較類排序说搅。

算法復(fù)雜度
image.png

穩(wěn)定:如果a原本在b前面,而a=b虏等,排序之后a仍然在b的前面。
不穩(wěn)定:如果a原本在b的前面适肠,而a=b霍衫,排序之后 a 可能會出現(xiàn)在 b 的后面。

1侯养、冒泡排序

冒泡排序:
1敦跌、設(shè)置個標識位;
2逛揩、不斷的比較前后兩個元素的大小柠傍,如果前一個值比后一個值大就交換兩個元素的位置,改變標識位的值辩稽,否則不變惧笛,并且指針指向后一個元素;
3逞泄、重復(fù)2的步驟患整,當遇到標識位沒有變化或者循環(huán)結(jié)束拜效,排序完成
代碼塊:

    boolean flag = true;
    for (int i = 0;i<data.length;i++){
        for (int j = 0;j<data.length-i-1;j++){
            if (data[j]>data[j+1]){
                int temp = data[j+1];
                data[j+1] = data[j];
                data[j] = temp;
                //轉(zhuǎn)換標識位
                flag = false;
            }
        }
        if (flag)
        //如果沒有進行轉(zhuǎn)換,flag=true各谚,跳出循環(huán)
            break;
        else
            flag = true;
    }

2紧憾、快速排序

快速排序:(申明兩個指針,head指向鏈表頭昌渤,tail指向鏈表尾)
從鏈表尾tail開始赴穗,當tail指向元素的值比head大的時候,則tail往前移動一個位置膀息,當tail指向元素的值小于head指向元素的值得時候般眉,交換head和tail所指元素的值,然后從head繼續(xù)開始比較履婉,如果head指向元素的值大于tail指向的值煤篙,則交換head和tail所指元素的值,否則head往后移動一個位置毁腿,直到head=tail的時候辑奈,結(jié)束第一輪循環(huán)。
其實head第一次指向的值已烤,我們稱它為一個基準鸠窗,然后通過上面的思路循環(huán),最后可以做到這個基準值的左邊是小于它的胯究,基準值得右邊是大于它的稍计,這樣才算完成第一輪的循環(huán),而左右兩個區(qū)間還是無序數(shù)列裕循,需要通過遞歸的方式蟋恬,一步步對這些無序數(shù)列進行排序。
代碼塊:

    public void kpSort(int[] source,int start,int end){
        //保留大區(qū)間的頭尾
        int i = start;
        int j = end;
        int value = source[start];
        //當i!=j的時候具温,說明本輪循環(huán)還未結(jié)束
        while (i<j){
            //從鏈表尾開始循環(huán)
            while (source[j]>=source[i] && j>i)
                j--;
            source[i] = source[j];
            source[j] = value;

            //從鏈表頭開始循環(huán)
            while (source[i]<=source[j] && i<j)
                i++;
            source[j] = source[i];
            source[i] = value;
        }
         //遞歸左右兩個無序區(qū)間的數(shù)列
            if (i>start)kpSort(source,start,i-1);
            if (j<end)kpSort(source,j+1,end);
    }

3冒掌、插入排序

插入排序的思路:
1、從當前排序數(shù)列的第二個元素開始株婴,比較第二個元素與第一個元素的大小怎虫,如果小于則兩個元素互換位置
2、當進行步驟一之后困介,會將指針繼續(xù)指向下一個元素大审,指針前的元素都是排好序的了,那么指針指向下一個元素座哩,比較指針指向元素的值與前一個值的大小徒扶,如果大于則不變,如果小于則將前一個元素后移一位根穷,然后繼續(xù)比較移動元素前一位元素的值酷愧,直到找到比指針指向元素值來的小的值驾诈,則插到該值的后邊
3、循環(huán)第二步操作溶浴,直到數(shù)列尾
這個插入排序還是很好理解的乍迄,也就是當前有個排好序的數(shù)列,如果你要插入一個元素士败,你就需要從后面一個個的往前進行比較闯两,比較的過程中還要將比當前元素大的值往后移動一位,直到找到符合條件的位置谅将,才可以插入漾狼,這樣當前數(shù)列還是一個有序數(shù)列,以此類推循環(huán)到數(shù)列尾饥臂。
代碼塊:

    int nowValue;
    int preIndex;
    for (int i = 1 ;i<data.length;i++){
        nowValue = data[i];
        preIndex = i-1;
        //如果需要比較的前一個元素的索引比0來的大
        //或者preIndex所指元素的值大于需要插入的值則后移比較的元素
        while (preIndex>=0 && data[preIndex]>nowValue){
            data[preIndex+1] = data[preIndex];
            preIndex--;
        }
        data[preIndex+1] = nowValue;
    }

4逊躁、希爾排序

希爾排序:希爾排序其實是插入排序的改進版,因為在上面插入排序的過程中隅熙,我們發(fā)現(xiàn)這個排序需要從后面一直往前進行比較稽煤,也就是如果值特別小的情況下,可能還需要進行n次的比較才能進入下一次的插入囚戚,這樣效率上很慢酵熙,在這個基礎(chǔ)上,這個排序算法增多了一個區(qū)間驰坊,也就是保證左邊區(qū)間的值在固定規(guī)則上比右邊來的小匾二,這樣就減少了插入排序可能比較的時間。
代碼塊:

    //間隔區(qū)間的值
    int grep = data.length/2;
    int j;
    for (;grep>0;grep/=2){
        for (int i = grep;i<data.length;i++){
            //nowValue表示當前需要比較元素的值
            int nowValue = data[i];
            //將當前元素與相隔grep元素的前一個元素進行比較拳芙,找到交換點
            for (j = i-grep;j>=0 && data[j]>nowValue;j-=grep){
                data[j+grep] = data[j];
            }
            data[j+grep] = nowValue;
        }
    }

這個代碼塊最需要注意的其實是最里層的循環(huán)察藐,這個循環(huán)有兩個跳出循環(huán)的條件
1、j>=0:這個條件表示當前指針指向的元素沒有再相隔grep個元素的前一個元素了舟扎,也就是當j為負數(shù)的時候转培,跳出循環(huán),將比較的值賦值給第一個元素浆竭。
2、data[j]>nowValue:這個條件又是怎么回事呢惨寿,你會發(fā)現(xiàn)我們一開始i是從比較小的值開始的邦泄,也就是從數(shù)組前面的某個位置開始跟相隔grep元素進行比較,在外層裂垦,i的值一直增加顺囊,也就意味著后面j一開始的值也會變得越來越大,然后我們要比較相隔grep元素蕉拢,可能就不止一個了特碳,比如你當前元素可能比相隔2*grep的元素還小诚亚,那么就不可能直接交換了,基于此也可以看出午乓,這個交換也是基于插入排序的站宗,按照插入排序的思路,需要一步步的往前進行比較益愈,直到找到比當前元素還小的值梢灭,就在這個元素后的grep替換元素的值。這個時候蒸其,我們應(yīng)該知道跳出循環(huán)后為啥還有個data[j+grep] = nowValue;語句了敏释,因為如果你要插入的元素剛剛好是第一個元素的情況下(相隔grep前沒有元素),你跳出了循環(huán)摸袁,就沒可能給第一個元素進行賦值操作钥顽,那么最后得到的結(jié)果也就是錯的,這個地方就是為了解決這種情況靠汁。

5蜂大、選擇排序

選擇排序也是一個非常容易理解的排序算法
1、將數(shù)組第一個值作為最小值膀曾,然后遍歷整個數(shù)組县爬,找到真正的最小值,然后跟這個值進行替換
2添谊、循環(huán)一的步驟财喳,直到數(shù)組尾

代碼塊:

    int minValue;
    int index;
    for (int i = 0 ;i<data.length;i++){
        minValue = data[i];
        index = i;
        for (int j = i+1;j<data.length;j++){
            if (data[j]<minValue){
                minValue = data[j];
                index = j;
            }
        }
        data[index] = data[i];
        data[i] = minValue;
    }

關(guān)于為什么說選擇排序也是不穩(wěn)定排序呢,我舉個例子斩狱,需要排序{5,4,3,5,2}
發(fā)現(xiàn)了什么耳高,我第一次交換的時候,把第一個元素5的值交換到數(shù)組最后所踊,然后再繼續(xù)進行交換的操作泌枪,但是數(shù)組第四個元素5的值會一直在第一個元素5的值得前面,也就是相同元素值排序之后無法保證前后順序秕岛,所以說選擇排序是不穩(wěn)定的排序碌燕。

6、堆排序

堆排序的結(jié)構(gòu)類似于完全二叉樹的結(jié)構(gòu)继薛,有兩種方式進行排序修壕,一種是小根堆,小根堆的根是完全二叉樹中最小的遏考,可以取最小的值保存為數(shù)組的第一個元素慈鸠,以此遞歸,直到數(shù)組結(jié)束灌具;另一種是大根堆青团,大根堆正好和小根堆相反譬巫,大根堆的根是完全二叉樹中最大的,可以取最大的值保存為數(shù)組的最后一個元素督笆,以此遞歸芦昔。
我以大根堆舉例,一個完整的節(jié)點有它的本身節(jié)點胖腾、父節(jié)點烟零、左孩子、右孩子咸作,而大根堆的結(jié)構(gòu)是要保證父節(jié)點肯定比本身節(jié)點來的大锨阿,并且本身節(jié)點、左孩子记罚、右孩子三者中墅诡,本身節(jié)點的值也必須是最大的,只有實現(xiàn)這的規(guī)則才算的上是大根堆桐智。
代碼塊如下:

    public static void heapSort(int[] a) {
        int i;
        //i的第一個取值很重要末早,a.length/2-1的值代表有葉子結(jié)點的最后一個根節(jié)點
        //獲取到這個節(jié)點,我們就可以一層層的往上進行調(diào)整
        for (i = a.length / 2 -1; i >= 0; i--) {// 構(gòu)建一個大頂堆
            adjustHeap(a, i, a.length - 1);
        }
        for (i = a.length-1; i > 0; i--) {// 將堆頂記錄和當前未經(jīng)排序子序列的最后一個記錄交換
            int temp = a[0];
            a[0] = a[i];
            a[i] = temp;
            adjustHeap(a, 0, i - 1);// 將a中前i-1個記錄重新調(diào)整為大頂堆
        }
    }  


    public static void adjustHeap(int[] a, int i, int len) {
        int temp, j;
        temp = a[i];
        //2*i+1代表當前節(jié)點的左孩子
        for (j = 2 * i+1; j < len; j = j*2+1) {// 沿關(guān)鍵字較大的孩子結(jié)點向下篩選
            //如果左孩子比右孩子小说庭,則將指針指向右孩子
            if (j < len && a[j] < a[j + 1])
                ++j; // j為關(guān)鍵字中較大記錄的下標
            //如果左右孩子中最大的一個值不大于根節(jié)點的值然磷,則直接跳出
            if (temp >= a[j])
                break;
            //將左右孩子值中最大的一個并且大于根節(jié)點的值賦值給當前的根節(jié)點
            //將指針移到左右孩子中最大的一個,往值大的左或者右二叉樹進行調(diào)整
            a[i] = a[j];
            i = j;
        }
        //將最初始指向的值賦值給當前i指向的節(jié)點
        a[i] = temp;
    }

代碼塊中需要注意幾個點:
第一刊驴,我們建立大頂堆需要獲取到帶有葉子結(jié)點的最后一個根節(jié)點姿搜,從這個根節(jié)點開始往回進行調(diào)整,而這個節(jié)點的值=數(shù)組長度/2-1
第二捆憎,當我們比較完當前節(jié)點和左右孩子中最大的一個值得時候舅柜,同時也要保證左右孩子的下一層結(jié)構(gòu)也符合大頂堆的結(jié)構(gòu),這樣就需要一層層的往下進行遍歷躲惰,直到?jīng)]有左右孩子為止致份。

7、歸并排序

歸并排序是建立在歸并操作上的一種有效的排序算法础拨。該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用氮块。將已有序的子序列合并,得到完全有序的序列诡宗;即先使每個子序列有序滔蝉,再使子序列段間有序。若將兩個有序表合并成一個有序表僚焦,稱為2-路歸并。
1曙痘、不斷分裂成子序列芳悲,遞歸到左邊界大于等于右邊界返回立肘;
2、將已經(jīng)排序好的子序列不斷合并名扛,直到?jīng)]有子序列谅年,返回結(jié)果。

代碼塊:

    public int[] sort(int[] source, int left, int right) {
        if (left >= right)
            return source;

        int mid = (right + left) / 2;
        //排序左邊區(qū)域
        sort(source, left, mid);
        //排序右邊區(qū)域
        sort(source, mid + 1, right);
        //返回合并的值
        return merge(source, left, mid, right);
    }

    public int[] merge(int[] source, int left, int mid, int right) {
        //申請個臨時的數(shù)組空間存儲排序好的子序列
        int[] temp = new int[right - left + 1];
        //左子序列第一個元素指針
        int i = left;
        //右子序列第一個元素指針
        int j = mid + 1;
        //臨時數(shù)組空間索引
        int index = 0;
        //對左右子序列進行比較大小后插入到臨時數(shù)組
        while (i <= mid && j <= right) {
            if (source[i] > source[j])
                temp[index++] = source[j++];
            else
                temp[index++] = source[i++];
        }

        //如果i<=mid說明左子序列已經(jīng)全部插入到臨時數(shù)組空間
        //將右子序列剩下的值插入到臨時數(shù)組空間的尾部
        while (i <= mid)
            temp[index++] = source[i++];

        //跟上面正好相反肮韧,右子序列已經(jīng)全部插入融蹂,左子序列剩余部分插入操作
        while (j <= right)
            temp[index++] = source[j++];

        //將臨時數(shù)組數(shù)據(jù)覆蓋本次左右子序列所涉及的數(shù)組范圍
        for (int x = 0; x < temp.length; x++)
            source[x + left] = temp[x];

        return source;
    }

在只有十個元素的數(shù)據(jù)量情況下排序費時情況

冒泡排序

image.png

選擇排序

image.png

插入排序

image.png

希爾排序

image.png

歸并排序

image.png

快速排序

image.png

堆排序

image.png

總結(jié)

本次學(xué)習(xí)了七種排序算法,受益良多弄企,從上面的時間也可以看出超燃,在數(shù)據(jù)量比較少的時候,歸并排序拘领、快速排序意乓、堆排序其實費時還比較長,所以應(yīng)該根據(jù)不同情況來選擇適合的排序算法约素。

參考鏈接:
https://www.cnblogs.com/onepixel/articles/7674659.html
https://blog.csdn.net/jianyuerensheng/article/details/51263453
https://blog.csdn.net/qq_27680317/article/details/78549875?utm_source=blogxgwz0
https://blog.csdn.net/qq_39776901/article/details/77387923?utm_source=blogxgwz2
https://blog.csdn.net/min996358312/article/details/68068689?utm_source=blogxgwz1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末届良,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子圣猎,更是在濱河造成了極大的恐慌士葫,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件送悔,死亡現(xiàn)場離奇詭異慢显,居然都是意外死亡,警方通過查閱死者的電腦和手機放祟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門鳍怨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人跪妥,你說我怎么就攤上這事鞋喇。” “怎么了眉撵?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵侦香,是天一觀的道長。 經(jīng)常有香客問我纽疟,道長罐韩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任污朽,我火速辦了婚禮散吵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己矾睦,他們只是感情好晦款,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著枚冗,像睡著了一般缓溅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赁温,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天坛怪,我揣著相機與錄音,去河邊找鬼股囊。 笑死袜匿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的毁涉。 我是一名探鬼主播沉帮,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贫堰!你這毒婦竟也來了穆壕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤其屏,失蹤者是張志新(化名)和其女友劉穎喇勋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偎行,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡川背,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛤袒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熄云。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖妙真,靈堂內(nèi)的尸體忽然破棺而出缴允,到底是詐尸還是另有隱情,我是刑警寧澤珍德,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布练般,位于F島的核電站,受9級特大地震影響锈候,放射性物質(zhì)發(fā)生泄漏薄料。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一泵琳、第九天 我趴在偏房一處隱蔽的房頂上張望摄职。 院中可真熱鬧誊役,春花似錦、人聲如沸谷市。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歌懒。三九已至,卻和暖如春溯壶,著一層夾襖步出監(jiān)牢的瞬間及皂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工且改, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留验烧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓又跛,卻偏偏與公主長得像碍拆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子慨蓝,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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

  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系感混,并對這種結(jié)構(gòu)定義相應(yīng)的運算,而且確保經(jīng)過這...
    Winterfell_Z閱讀 5,816評論 0 13
  • 1 初級排序算法 排序算法關(guān)注的主要是重新排列數(shù)組元素礼烈,其中每個元素都有一個主鍵弧满。排序算法是將所有元素主鍵按某種方...
    深度沉迷學(xué)習(xí)閱讀 1,408評論 0 1
  • 痛苦的背后 是無盡的欲望 是你的 或者是別人的 惡魔掌控了噩夢 虛弱了身體 疲憊了心靈 趕走了快樂 你看到 或者沒...
    壺上春秋閱讀 230評論 0 0
  • 晨練第413天:深蹲100個 讀經(jīng)第293天: 詩經(jīng)·考槃 考槃在澗,碩人之寬此熬。獨寐寤言庭呜,永矢弗諼。 考槃在阿犀忱,碩...
    山緣有約閱讀 160評論 0 0
  • 皇尊御一品:凱旋親王【太皇太后的曾孫子也是皇上的大兒子】王妃 皇尊從一品:凱翼親王【太皇太后的曾孫子也是皇上的二兒...
    女二號葉以瀾閱讀 5,727評論 1 2