【offerMe--數據結構】----排序算法

2021-02-24 程式設計師面試之道

1.

從今天開始我分模塊推出面試指南,首先作為程式設計師最重要的是數據結構,數據結構是我們的本科課程,同時也是我們的必備課程。排序是我們的必考內容,今天以排序算法引出我們的數據結構。

2.總

 

3.詳敘3.1插入排序3.1.1直接插入

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/100858281

 

敘述:

將待排序的記錄按其關鍵碼值的大小逐個插入到一個已經排好序的有序序列中,直到所有的記錄插入完為止,就得到一個新的序列。(抓撲克牌思想)

特點:元素越接近有序,直接插入排序算法的時間效率越高,算法的本質是減治算法;因此當數組接近有序或大概率有序的情況下,或是數組的數量比較小的時候採用插入排序。

時間複雜度

最好情況:(有序且為正序)O(n)
最壞情況:(逆序)O(n^2)
平均情況:O(n2)
空間複雜度:O(1)
具有穩定性



    private static void InsertSort(int[] arr) {        int tmp = 0;        int i ,j; for(i = 1; i < arr.length; i++) {
                        tmp = arr[i];            for(j = i-1;j >=0 && arr[j] > tmp;j--)            {                                arr[j+1] = arr[j];            }                        arr[j+1] = tmp;        }}

一般來說,插入排序都採用in-place在數組上實現。具體算法描述如下:

⒈ 從第一個元素開始,該元素可以認為已經被排序

⒉ 取出下一個元素,在已經排序的元素序列中從後向前掃描

⒊ 如果該元素(已排序)大於新元素,將該元素移到下一位置

⒋ 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置

⒌ 將新元素插入到下一位置中

⒍ 重複步驟2~5

如果比較操作的代價比交換操作大的話,可以採用二分查找法來減少比較操作的數目。該算法可以認為是插入排序的一個變種,稱為二分查找排序。

 

實例

基本思想:

  把n個待排序的元素看成一個有序表和一個無序表,開始時有序表中只有一個元素,無序表中有n-1個元素;排序過程即每次從無序表中取出第一個元素,將它插入到有序表中,使之成為新的有序表,重複n-1次完成整個排序過程。

實例:

0.初始狀態 3,1,5,7,2,4,9,6(共8個數)

有序表:3;無序表:1,5,7,2,4,9,6

1.第一次循環,從無序表中取出第一個數 1,把它插入到有序表中,使新的數列依舊有序

有序表:1,3;無序表:5,7,2,4,9,6

2.第二次循環,從無序表中取出第一個數 5,把它插入到有序表中,使新的數列依舊有序

有序表:1,3,5;無序表:7,2,4,9,6

3.第三次循環,從無序表中取出第一個數 7,把它插入到有序表中,使新的數列依舊有序

有序表:1,3,5,7;無序表:2,4,9,6

4.第四次循環,從無序表中取出第一個數 2,把它插入到有序表中,使新的數列依舊有序

有序表:1,2,3,5,7;無序表:4,9,6

5.第五次循環,從無序表中取出第一個數 4,把它插入到有序表中,使新的數列依舊有序

有序表:1,2,3,4,5,7;無序表:9,6

6.第六次循環,從無序表中取出第一個數 9,把它插入到有序表中,使新的數列依舊有序

有序表:1,2,3,4,5,7,9;無序表:6

7.第七次循環,從無序表中取出第一個數 6,把它插入到有序表中,使新的數列依舊有序

有序表:1,2,3,4,5,6,7,9;無序表:(空)

3.1.2希爾排序

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/101637859

 

敘述:

先將數據進行分組,通過插入排序的方式讓數據基本有序,然乎再進行插入排序,能夠讓插入排序最壞情況的概率降低;

分組的方法:固定步長取值分為一組的方法。分組越多(步長越小)大的數往後走的越快,分組越少排完後越接近有序
希爾排序是對直接插入排序的優化
時間複雜度:

最好情況(正序):O(n)
平均情況:O(n1.3–n2)
最壞情況(逆序):O(n2)
空間複雜度:O(1)
不具有穩定性的排序


    private static void ShellSort(int[] arr) {                int h = 1;                while(h < arr.length / 3) {            h = h * 3 + 1;        }        while(h > 0) {                        int tmp = 0;            for(int i = h; i < arr.length; i++) {                tmp = arr[i];                int j = i; while(j > h - 1 && arr[j - h] >= tmp) {

                    arr[j] = arr[j - h];                    j -= h;                }                arr[j] = tmp;            }                        h = (h - 1) / 3;        }}

3.2交換排序3.2.1冒泡排序

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/100858281

 

根據序列中兩個記錄的值的比較結構來交換兩個記錄在序列中的位置,將值較小的向前移動

特點:算法的本質是減治排序
時間複雜度:

最好情況(正序):O(n)

最壞情況(逆序):O(n2)

平均情況:O(n2)

空間複雜度:O(1)
穩定性:穩定

    private static void BubbleSort(int[] source) {        int tmp = 0;        for(int i =0;i<source.length;i++){            for (int j =0;j<source.length-1-i;j++){                if(source[j]>source[j+1]){                    tmp = source[j+1];                    source[j+1] = source[j];                    source[j] = tmp;                }            }        }}

 

3.2.2快速排序

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/101921177

 

一共包含三大步:
1.確定待排序元素序列中的某元素作為基準值(選區間最右邊的元素/選中間元素 與最右邊的元素交換)
2.以基準值為界,大於基準值的放在基準值的右邊,小於基準值的放在基準值左邊(hover 挖坑 前後下標)
3.然後對左右序列重複該過程,直到小區間中只有一個元素或沒有元素為止(size==0||size ==1)
特點:採用分治算法
時間複雜度:

最好情況:O(n*long(n)) (區間分割,組成形式可以考慮為二叉樹形式,最好情況就成了單支樹,分治算法變成了減治算法)
平均情況:O(n*long(n))
最差情況:O(n2)
空間複雜度:O(log(n))—O(n)
穩定性:不穩定

 

    private static void QuickSort(int[] arr, int low, int high) {        if(low < high){                        int index = GetIndex(arr, low, high);                        QuickSort(arr, 0, index - 1);            QuickSort(arr, index + 1, high);        }    }    private static int GetIndex(int[] arr, int low, int high) {        int i = low, j = high;        int x = arr[low];                while (i < j)        {                        while(i < j && arr[j] >= x){                j--;            }            if(i < j)            {                arr[i] = arr[j];                                i++;            }                        while(i < j && arr[i] < x)            {                i++;            }            if(i < j)            {                arr[j] = arr[i];                                j--;            }        }                arr[i] = x;        return i;    }

快速排序我們可以近似的想成一個完全二叉樹

 

一個數組的容量是N,第一層遞歸遍歷的次數是N,因為數組裡每個數字都需要和key比較,那麼接下來遍歷分出來的兩個區間,總的遍歷次數還會是近似是N,以此類推,直到分為最後一個,也就是說樹的高度是lgn,也就是遞歸次數,所以時間複雜度是N*lgN.

 

3.3交換排序3.3.1選擇排序

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/100858281

 

每一次從待排序的元素中選出最小的一個元素,若它不是這組元無序素中的最後一個元素,則將它與存放這組有序元素的起始位置交換,直到全部待排序元素全部排完。

或是從無序元素中選出一個最大的,若其不是無序區間的最後一個元素,則將其與存放有序區間的最後一個元素交換,直到無序元素全部排完

特點:效率不高,算法的本質是減治算法
時間複雜度:

最好情況:O(n2)
平均情況:O(n2)
最壞情況:O(n2)
空間複雜度:O(1)
穩定性:不穩定

     * @Author liuhaidong     * @Description 選擇排序     * @Date 17:10 2019/10/2 0002     */    private static void SelectionSort(int[] arr) {        int k =0;        int tmp = 0;        for(int i = 0;i<arr.length-1;i++){            k = i;            for(int j =i+1;j<arr.length;j++){                if(arr[j] <arr[k]){                    k = j;                }            }            tmp = arr[i];            arr[i] = arr[k];            arr[k] = tmp;        }    }

 

 3.3.1堆排序

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/101937543

 

利用堆的特點設計的一種排序算法,排升序要建大堆,排降序要建小堆,建堆是從第一個非葉子節點開始建,利用向下調整的思想逐步建立出整個堆。
每次將未排序序列的第一個與最後一個元素進行交換,然後利用向下調整,將最大的元素調整到最後一個。
特點:算法的本質是減治算法
時間複雜度:

最好情況:O(n*longn)
平均情況:O(n*longn)
最壞情況:O(n*longn)
空間複雜度:O(1)
穩定性:不穩定

    private static void HeadSort(int[] arr) {        int startIndex = (arr.length-1)/2;                for(int i =startIndex;i>=0;i--){            ToMaxHeap(arr,arr.length,i);                    }                for(int i = arr.length-1;i>0;i--){            int t = arr[0];            arr[0] = arr[i];            arr[i] = t;                        ToMaxHeap(arr,i,0);                    }    }        private static void ToMaxHeap(int[] arr, int size, int index) {        int leftNodeIndex = index * 2 + 1;        int rightNodeIndex = index * 2 + 2;                int maxIndx = index;        if (leftNodeIndex < size && arr[leftNodeIndex] > arr[maxIndx]) {            maxIndx = leftNodeIndex;        }        if (rightNodeIndex < size && arr[rightNodeIndex] > arr[maxIndx]) {            maxIndx = rightNodeIndex;        }                if (maxIndx != index) {            int t = arr[maxIndx];            arr[maxIndx] = arr[index];            arr[index] = t;                        ToMaxHeap(arr, size, index);        }}

3.4歸並排序

博客連結(詳解)

https://blog.csdn.net/weixin_41563161/article/details/101942087

 

把數組平均分成兩部分,分別對左右兩部分做均分,直到區間的size<=1,然後利用一個額外的數組空間,逐步將兩個數組按序排列,再將排好的數據拷貝回原始數組,這樣使子序列的段間有序,將子序列歸併為完整的序列
特點:分治算法,是外部排序最好的算法
時間複雜度:

最好情況:O(n*log(n))
空間複雜度:O(n)
穩定性:穩定
需要做外部排序的情況:
1.內存放不下了,切成小塊,將每一小塊排序,然後合併n個有序數組



    private static void MergrSort(int[] arr) {        Split(arr,0,arr.length-1);            }        private static void Split(int[] arr, int startIndex, int endIndex) {                int centerIndex = (startIndex + endIndex)/2;        if(startIndex < endIndex){            Split(arr, startIndex, centerIndex);            Split(arr, centerIndex+1, endIndex);            Mergr(arr,startIndex,centerIndex,endIndex);                    }    }        private static void Mergr(int[] arr, int startIndex, int centerIndex, int enIndex) {        int[] tempArr = new int[enIndex-startIndex+1];                int i = startIndex;                int j = centerIndex+1;                int index = 0;                while (i <= centerIndex && j<=enIndex){                        if(arr[i] <= arr[j]){                tempArr[index] = arr[i];                i++;            }else {                tempArr[index] = arr[j];                j++;            }            index++;        }                while(i <= centerIndex){            tempArr[index] = arr[i];            i++;            index++;        }        while(j <= enIndex){            tempArr[index] = arr[j];            j++;            index++;        }        for(int k = 0;k<tempArr.length;k++){            arr[k+startIndex] = tempArr[k];        } }

4.總結

其中快速排序、堆排序 、歸併排序必須要能默寫出來的。因為篇幅問題所以不能展開,之後會對每一個算法進行展開講解。一定要了解每個排序算法的原理,做到胸有成竹,這樣才可以夢想成真。

本公眾號分享自己從程式設計師小白到經歷春招秋招斬獲10幾個offer的面試筆試經驗,其中包括【Java】、【作業系統】、【計算機網絡】、【設計模式】、【數據結構與算法】、【大廠面經】、【資料庫】期待你加入!!!


優質文章推薦:

1.計算機網絡----三次握手四次揮手

2.夢想成真項目自我介紹

3.IntelliJ IDEA 2020.3.2激活破解(附安裝包及補丁、激活碼)

4.震驚!來看《這份程式設計師面試手冊》!!!

5.一字一句教你面試「個人簡介」

6.接近30場面試分享

7.你們要的免費書來了

8.思維導圖Xmind2020 3.1中文正式版激活指南

相關焦點

  • 手敲一遍數據結構和排序算法
    排序算法 性質記憶 冒泡排序 選擇排序 插入排序 快速排序 歸併排序 希爾排序 堆排序數據結構 數組 堆 棧 隊列 鍊表 二叉樹 二叉搜索樹 圖 哈希表搜索 廣度優先搜索 深度優先搜索附件 排序算法代碼點擊
  • 數據結構與算法之拓撲排序
    例如,圖形的頂點可以表示要執行的任務(Activity),並且邊可以表示一個任務必須在另一個任務之前執行的約束;在這個應用中,拓撲排序只是一個有效的任務順序。若且唯若圖中沒有定向環時(即有向無環圖),才有可能進行拓撲排序。任何有向無環圖至少有一個拓撲排序。已知有算法可以在線性時間內,構建任何有向無環圖的拓撲排序。
  • 數據結構常見的八大排序算法
    八大排序,三大查找是《數據結構》當中非常基礎的知識點,在這裡為了複習順帶總結了一下常見的八種排序算法。
  • 詳解JAVA數據結構與算法:排序算法
    排序算法排序也稱排序算法(Sort Algorithm),排序是將一組數據,依指定的順序進行排列的過程。排序的分類:1) 內部排序:指將需要處理的所有數據都加載到內部存儲器中進行排序。低從圖中可見,我們應該盡可 能 避 免使用 指數階的算法1)常數階O(1)無論代碼執行了多少行,只要是沒有循環等複雜結構,那這個代碼的時間複雜度就都是O(1)
  • 【圖文並茂】408數據結構中的排序算法講解
    排序是數據結構中的重要知識點,也是考研中的必考內容,一般會在選擇題第11題考察,最常見的題目類型是給出一串記錄和經過若干趟排序之後的記錄,判斷可能屬於哪種排序算法
  • JavaScript 數據結構與算法之美 - 十大經典排序算法匯總(上)
    筆者寫的 JavaScript 數據結構與算法之美系列用的語言是 JavaScript ,旨在入門數據結構與算法和方便以後複習。文中包含了十大經典排序算法的思想、代碼實現、一些例子、複雜度分析、動畫、還有算法可視化工具。
  • 《數據結構與算法》十大經典排序算法動圖演示(2019最新Python代碼)
    /JS-Sorting-Algorithm排序算法是《數據結構與算法》中最基本的算法之一。排序算法可以分為內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序算法有:插入排序、希爾排序、選擇排序、冒泡排序、歸併排序、快速排序、堆排序、基數排序等。
  • 面試經驗分享之數據結構、算法題
    前言面試 IT 企業的研發崗位,數據結構和算法顯然是必考的項目。俺只學過普通的數據結構課程,沒讀過 STL,也沒有過 ACM 的訓練和比賽經歷,在一開始面對這樣類型題目的時候,心裡還是十分忐忑的。大大小小几十場面試下來,自己在這方面總算有了一定的心得積累,在此拋磚引玉, 以饗讀者。
  • 前端學習數據結構與算法系列(一):初識數據結構與算法
    前言作為一個對算法沒有任何認知,非科班出身的前端程式設計師,如果想提高自己的能力,不再只寫業務代碼當一個應用工程師,算法是必須掌握的一門本領。算法也是一種思想,當你去讀一些優秀框架的源碼,如果對算法和數據結構一無所知,讀起來很困難,你無法理解人家為什麼要那樣寫,那樣寫的好處是什麼,接下來就跟大家分享下作為一個前端程式設計師,如何學習數據結構與算法。
  • 快速入門數據結構和算法
    本文簡要分享算法基礎、常見的數據結構以及排序算法,給同學們帶來一堂數據結構和算法的基礎課。文末福利:阿里雲開發者訓練營來了。一  前言1  為什麼要學習算法和數據結構?2  業務開發要掌握到程度? 二  數據結構基礎1  什麼是數據結構?數據結構是數據的組織、管理和存儲格式,其使用目的是為了高效的訪問和修改數據。數據結構是算法的基石。如果把算法比喻成美麗靈動的舞者,那麼數據結構就是舞者腳下廣闊而堅實的舞臺。2  物理結構和邏輯結構的區別?
  • 三面騰訊、頭條拿到offer後才知道,數據結構和算法太TM重要了
    一個數據元素可由若干個數據項組成。數據類型:在一種程序設計語言中,變量所具有的數據種類。整型、浮點型、字符型等等邏輯結構:數據之間的相互關係。集合 結構中的數據元素除了同屬於一種類型外,別無其它關係。線性結構 數據元素之間一對一的關係樹形結構 數據元素之間一對多的關係圖狀結構或網狀結構 結構中的數據元素之間存在多對多的關係物理結構/存儲結構:數據在計算機中的表示。
  • 【第3篇 | 數據結構與算法】一舉消滅十大常見(常考)排序算法(原理+動圖+代碼+)
    作者:書偉時間:2020/4/28數據結構與算法 | 系列第0篇 | 不會數據結構與算法的碼農有多牛?以及對應的要排序的原始數據是什麼樣的,因為有序度不同的數據對執行效率是有影響的。時間複雜度的係數、常數、低階。時間複雜度反應的是增長趨勢,通常忽略係數、常數、低階,但是對於小數據量,同階時間複雜度排序算法對比也要把這些考慮進來。基於比較的排序算法,涉及到元素的比較和交換,所以也要考慮比較次數和交換(或移動)次數。內存的消耗通過空間複雜度來衡量。
  • 數據結構與算法?看這篇就夠了!!!
    真正讓程式設計師有區分度,企業招聘萬年不變的重點 —— 算法與數據結構但無論半路出家還是科班出身,除學生時代搞算法競賽的同學外真正用心學習過算法與數據結構太少太少對於後期想要學習算法與數據結構卻不得不面對以下問題:於是我們推出了「數據結構與算法365天特訓營」實時視頻直播
  • 算法與數據結構?看這篇就夠了
    真正讓程式設計師有區分度,企業招聘萬年不變的重點 —— 算法與數據結構但無論半路出家還是科班出身,除學生時代搞算法競賽的同學外真正用心學習過算法與數據結構太少太少對於後期想要學習算法與數據結構卻不得不面對以下問題:於是我們推出了「數據結構與算法365天特訓營」實時視頻直播
  • JAVA必須掌握的數據結構和算法
    常見的數據結構鍊表LinkedHashSet LinkedList 底層數據結構由鍊表和哈希表組成。數據的添加和刪除都較為方便,就是訪問比較耗費時間。數組ArrayList 訪問數據十分簡單,而添加和刪除數據比較耗工夫堆堆是一種圖的樹形結構,被用於實現「優先隊列",優先隊列是一種數據結構,可以自由添加數據,但取出數據時要從最小值開始按順序取出
  • 考研計算機 | 數據結構—結構算法
    2021計算機考研數據結構—結構算法算法的設計取決於數據(邏輯)結構,而算法的實現依賴於採用的存儲結構
  • JS家的排序算法 十大經典算法排序總結
    然而,在傳統的計算機算法和數據結構領域,大多數專業教材和書籍的默認語言都是Java或者C/C+ +。這給最近想惡補算法和數據結構知識的我造成了一定困擾,因為我想尋找一本以JavaScript為默認語言的算法書籍。當我了解到O』REILLY家的動物叢書系列裡有一本叫做《數據結構與算法JavaScript描述》時,便興奮的花了兩天時間把這本書從頭到尾讀了一遍。
  • Java實現冒泡排序算法
    1.引子1.1.為什麼要學習數據結構與算法?有人說,數據結構與算法,計算機網絡,與作業系統都一樣,脫離日常開發,除了面試這輩子可能都用不到呀!有人說,我是做業務開發的,只要熟練API,熟練框架,熟練各種中間件,寫的代碼不也能「飛」起來嗎?於是問題來了:為什麼還要學習數據結構與算法呢?
  • 資料| 數據結構與算法 JavaScript 描述
    內容簡介 · · · · · ·通過本書的學習,讀者將能自如地選擇最合適的數據結構與算法,並在JavaScript開發中懂得權衡使用。此外,本書也概述了與數據結構與算法相關的JavaScript特性。數組和列表:最常用的數據結構。棧和隊列:與列表類似但更複雜的數據結構。鍊表:如何通過它們克服數組的不足。字典:將數據以鍵-值對的形式存儲。散列:適用於快速查找和檢索。集合:適用於存儲只出現一次的元素。二叉樹:以層級的形式存儲數據。圖和圖算法:網絡建模的理想選擇。
  • 八大排序算法的 Python 實現