十大經(jīng)典排序算法

一、選擇排序算法描述

  1. 初始狀態(tài):無(wú)序區(qū)為R[1..n],有序區(qū)為空;
  2. 第i趟排序(i=1,2,3…n-1)開(kāi)始時(shí),當(dāng)前有序區(qū)和無(wú)序區(qū)分別為R[1..i-1]和R(i..n)。該趟排序從當(dāng)前無(wú)序區(qū)中-選 出關(guān)鍵字最小的記錄 R[k],將它與無(wú)序區(qū)的第1個(gè)記錄R交換,使R[1..i]和R[i+1..n)分別變?yōu)橛涗泜€(gè)數(shù)增加1個(gè)的新有序區(qū)和記錄個(gè)數(shù)減少1個(gè)的新無(wú)序區(qū);
  3. n-1趟結(jié)束,數(shù)組有序化了。
    /**
     * 選擇排序算法:最好、平均、最壞時(shí)間復(fù)雜度均為O(n2),空間復(fù)雜度為1,穩(wěn)定性:不穩(wěn)
     */
    static void selectSort(int[] arr) {
        for (int j = 0; j < arr.length - 1; j++) {
            int minPos = j;
            for (int i = j + 1; i < arr.length; i++) {
                if (arr[minPos] > arr[i]) {
                    minPos = i;
                }
            }
            //交換起始位置和最小值的位置
            swap(arr, j, minPos);
        }
    }

    static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

二、冒泡排序算法描述

  1. 比較相鄰的元素。如果第一個(gè)比第二個(gè)大,就交換它們兩個(gè);
  2. 對(duì)每一對(duì)相鄰元素作同樣的工作,從開(kāi)始第一對(duì)到結(jié)尾的最后一對(duì),這樣在最后的元素應(yīng)該會(huì)是最大的數(shù);
  3. 針對(duì)所有的元素重復(fù)以上的步驟,除了最后一個(gè);
  4. 重復(fù)步驟1~3,直到排序完成。
    /**
     * 冒泡排序算法:最好時(shí)間復(fù)雜度為O(n)、平均、最壞時(shí)間復(fù)雜度均為O(n2),空間復(fù)雜度為1,穩(wěn)定性:穩(wěn)
     */
    static void bubbleSort(int[] arr) {
        for (int j = 0; j < arr.length; j++) {
            //遍歷數(shù)組,比較當(dāng)前值和后一個(gè)值,如果當(dāng)前值比后一個(gè)值大,則交換2個(gè)值的位置
            for (int i = 0; i < arr.length - 1 - j; i++) {
                if (arr[i] > arr[i + 1]) {
                    //交換2個(gè)元素的位置
                    swap(arr, i, i + 1);
                }
            }
        }
    }

三、插入排序算法描述

  1. 從第一個(gè)元素開(kāi)始,該元素可以認(rèn)為已經(jīng)被排序;
  2. 取出下一個(gè)元素,在已經(jīng)排序的元素序列中從后向前掃描;
  3. 如果該元素(已排序)大于新元素,將該元素移到下一位置;
  4. 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 將新元素插入到該位置后;
  6. 重復(fù)步驟2~5。
    /**
     * 插入排序:最好時(shí)間復(fù)雜度為O(n),平均、最壞時(shí)間復(fù)雜度為O(n2),空間復(fù)雜度為1,穩(wěn)定性:穩(wěn)
     */
    static void insertionSort(int[] arr) {
        //從第一個(gè)元素開(kāi)始,比較該元素與前一個(gè)元素
        //如果該元素大于前一個(gè)元素,則將該元素放到該索引的位置,循換此步驟
        for (int i = 1; i < arr.length; i++) {
            //該數(shù)為遞減
            for (int j = i; j > 0; j--) {
                if (arr[j] < arr[j - 1]) {
                    //交換后一個(gè)元素和前一個(gè)元素的位置
                    swap(arr, j, j - 1);
                } else {
                    break;
                }
            }
        }
    }

四、希爾排序(增量縮小排序)算法描述

  1. 選擇一個(gè)增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列個(gè)數(shù)k,對(duì)序列進(jìn)行k 趟排序;
  3. 每一趟排序,根據(jù)對(duì)應(yīng)的增量ti,將待排序列分割成若干長(zhǎng)度為m 的子序列,分別對(duì)各子表進(jìn)行直接插入排序。僅增量因子為1時(shí),整個(gè)序列作為一個(gè)表來(lái)處理,表長(zhǎng)度即為整個(gè)序列的長(zhǎng)度。
    注意:希爾排序可能改變數(shù)據(jù)原本順序,所以不穩(wěn)定
    /**
     * 希爾排序:改進(jìn)的插入排序,最好時(shí)間復(fù)雜度O(n),平均時(shí)間復(fù)雜度O(n^1.3),最壞時(shí)間復(fù)雜度O(n2),空間復(fù)雜度為1,穩(wěn)定性:不穩(wěn)
     */
    static void shellSort(int[] arr) {
        int h = 1;
        while (h <= arr.length / 3) {
            h = 3 * h + 1;
        }
        for (int gap = h; gap >= 1; gap = (gap - 1) / 3) {
            for (int i = gap; i < arr.length; i++) {
                for (int j = i; j >= gap; j -= gap) {
                    if (arr[j] < arr[j - gap]) {
                        swap(arr, j, j - gap);
                    }
                }
            }
        }
    }

五、歸并排序(遞歸思想)算法描述

  1. 把長(zhǎng)度為n的輸入序列分成兩個(gè)長(zhǎng)度為n/2的子序列;
  2. 對(duì)這兩個(gè)子序列分別采用歸并排序;
  3. 將兩個(gè)排序好的子序列合并成一個(gè)最終的排序序列。
    /**
     * 歸并排序:最好、平均、最壞時(shí)間復(fù)雜度均為O(nlogn),空間復(fù)雜度為O(n),穩(wěn)定性:穩(wěn)
     */
    static void mergeSort(int[] arr){
        sort(arr,0 , arr.length - 1);
    }

    static void sort(int[] arr, int left, int right) {
        //把數(shù)組切割然后調(diào)用merge排序,直到left==right為止
        if(left == right) return;
        //找出數(shù)組中間索引,將數(shù)組分成2段分別排序
        int mid = left + (right - left) / 2;
        sort(arr, left, mid);
        sort(arr, mid + 1, right);
        //將這2段數(shù)據(jù)進(jìn)行排序歸并
        merge(arr, left, mid + 1, right);

    }

    static void merge(int[] arr, int left, int right, int rightBound) {
        //將一個(gè)數(shù)組分為2段,把每一段數(shù)組進(jìn)行排序
        int mid = right - 1;
        int[] temp = new int[rightBound - left + 1];
        //左邊數(shù)組起點(diǎn)
        int leftPtr = left;
        //右邊數(shù)組起點(diǎn)
        int rightPtr = right;
        //新數(shù)組起點(diǎn)
        int k = 0;
        while (leftPtr <= mid && rightPtr <= rightBound) {
            temp[k++] = arr[leftPtr] <= arr[rightPtr] ? arr[leftPtr++] : arr[rightPtr++];
        }
        //將剩下的數(shù)組直接放到temp中
        while (leftPtr <= mid) temp[k++] = arr[leftPtr++];
        while (rightPtr <= rightBound) temp[k++] = arr[rightPtr++];

        //temp完成后給傳入的數(shù)組賦值
        System.arraycopy(temp, 0, arr, left, temp.length);
    }

六、快速排序算法描述

  1. 從數(shù)列中挑出一個(gè)元素,稱為 “基準(zhǔn)”(pivot);
  2. 重新排序數(shù)列,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一邊)。在這個(gè)分區(qū)退出之后,該基準(zhǔn)就處于數(shù)列的中間位置。這個(gè)稱為分區(qū)(partition)操作;
  3. 遞歸地(recursive)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。
    /**
     * 快速排序:最好、平均時(shí)間復(fù)雜度均為O(nlogn),最壞時(shí)間復(fù)雜度為O(n2),空間復(fù)雜度為O(logn),穩(wěn)定性:不穩(wěn)
     */
    static void quickSort(int[] arr, int leftPtr, int rightBound) {
        if(leftPtr >= rightBound) return;
        //定義軸的索引
        int pivotIndex = partition(arr, leftPtr, rightBound);

        quickSort(arr, leftPtr, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, rightBound);
    }

    static int partition(int[] arr, int leftPtr, int rightBound) {
        //以最右邊的數(shù)為軸,數(shù)組兩段同時(shí)向中間擠壓
        int pivot = arr[rightBound];
        int leftIndex = leftPtr;
        int rightIndex = rightBound - 1;

        //注意:這里的等于是為了最后索引交匯時(shí),把leftIndex移到rightBound的位置
        while (leftIndex <= rightIndex) {
            //從左邊開(kāi)始,找到比軸數(shù)大的數(shù)為止
            while (leftIndex <= rightIndex && arr[leftIndex] <= pivot) leftIndex++;
            //從右邊開(kāi)始,找到比軸數(shù)小的數(shù)為止
            while (leftIndex <= rightIndex && arr[rightIndex] > pivot) rightIndex--;
            //如果索引沒(méi)有交叉,則交換兩個(gè)位置的數(shù)
            if(leftIndex < rightIndex) swap(arr, leftIndex, rightIndex);
        }
        //將軸上的值放到該數(shù)本該處的位置
        swap(arr, leftIndex, rightBound);
        return leftIndex;
    }

七、計(jì)數(shù)排序算法描述

  1. 找出待排序的數(shù)組中最大和最小的元素;
  2. 統(tǒng)計(jì)數(shù)組中每個(gè)值為i的元素出現(xiàn)的次數(shù),存入數(shù)組C的第i項(xiàng);
  3. 對(duì)所有的計(jì)數(shù)累加(從C中的第一個(gè)元素開(kāi)始,每一項(xiàng)和前一項(xiàng)相加);
  4. 反向填充目標(biāo)數(shù)組:將每個(gè)元素i放在新數(shù)組的第C(i)項(xiàng),每放一個(gè)元素就將C(i)減去1。
    /**
     * 計(jì)數(shù)排序:最好、平均、最壞時(shí)間復(fù)雜度均為O(n + k),空間復(fù)雜度為O(n + k),穩(wěn)定性:穩(wěn)
     */
    static void countingSort(int[] arr) {
        int bucketLen = findMax(arr) + 1;
        int[] bucket = new int[bucketLen];
        //count中,下標(biāo)即為數(shù)字本身,下標(biāo)上的值即為該數(shù)字的數(shù)量
        for (int value : arr) {
            bucket[value]++;
        }
        int sortedIndex = 0;
        for (int j = 0; j < bucketLen; j++) {
            //將計(jì)數(shù)數(shù)組挨個(gè)遞減直到0為止
            while (bucket[j] > 0) {
                //把原數(shù)組重新賦值
                arr[sortedIndex++] = j;
                //賦值后計(jì)數(shù)數(shù)組當(dāng)前索引上的值自減
                bucket[j]--;
            }
        }
    }

    static int findMax(int[] arr) {
        int result = arr[0];
        for (int i = 0; i < arr.length; i++) {
            result = Math.max(result, arr[i]);
        }
        return result;
    }

八、基數(shù)排序算法描述

  1. 取得數(shù)組中的最大數(shù),并取得位數(shù);
  2. arr為原始數(shù)組,從最低位開(kāi)始取每個(gè)位組成radix數(shù)組;
  3. 對(duì)radix進(jìn)行計(jì)數(shù)排序(利用計(jì)數(shù)排序適用于小范圍數(shù)的特點(diǎn));
   /***
     * 基數(shù)排序:最好、平均、最壞時(shí)間復(fù)雜度均為O(n * k),空間復(fù)雜度為O(n + k),穩(wěn)定性:穩(wěn)
     * @return
     */
    static int[] radixSort(int[] arr, int maxDigit) {
        int mod = 10;
        int dev = 1;
        //最高有幾位就循環(huán)幾次
        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
            // 考慮負(fù)數(shù)的情況,這里擴(kuò)展一倍隊(duì)列數(shù),其中 [0-9]對(duì)應(yīng)負(fù)數(shù),[10-19]對(duì)應(yīng)正數(shù) (bucket + 10)
            int[][] counter = new int[mod * 2][0];

            for (int j = 0; j < arr.length; j++) {
                int bucket = ((arr[j] % mod) / dev) + mod;
                counter[bucket] = arrayAppend(counter[bucket], arr[j]);
            }

            int pos = 0;
            for (int[] bucket : counter) {
                for (int value : bucket) {
                    arr[pos++] = value;
                }
            }
        }

        return arr;
    }

    /**
     * 自動(dòng)擴(kuò)容,并保存數(shù)據(jù)
     *
     * @param arr
     * @param value
     */
    static int[] arrayAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }

    /**
     * 獲取最高位數(shù)
     */
    static int getMaxDigit(int[] arr) {
        int maxValue = findMax(arr);
        return getNumLenght(maxValue);
    }

    //獲取數(shù)字位數(shù),幾位數(shù)
    static int getNumLenght(long num) {
        if (num == 0) {
            return 1;
        }
        int lenght = 0;
        for (long temp = num; temp != 0; temp /= 10) {
            lenght++;
        }
        return lenght;
    }

九、桶排序算法描述

  1. 設(shè)置一個(gè)定量的數(shù)組當(dāng)作空桶;
  2. 遍歷輸入數(shù)據(jù),并且把數(shù)據(jù)一個(gè)一個(gè)放到對(duì)應(yīng)的桶里去;
  3. 對(duì)每個(gè)不是空的桶進(jìn)行排序;
  4. 從不是空的桶里把排好序的數(shù)據(jù)拼接起來(lái)。
    /***
     * 桶排序:最好時(shí)間復(fù)雜度均為O(n),平均時(shí)間復(fù)雜度均為O(n + k),最壞時(shí)間復(fù)雜度均為O(n2),空間復(fù)雜度為O(n + k),穩(wěn)定性:穩(wěn)
     */
    static int[] bucketSort(int[] arr, int bucketSize) {
        if (arr.length == 0) {
            return arr;
        }

        int minValue = arr[0];
        int maxValue = arr[0];
        for (int value : arr) {
            if (value < minValue) {
                minValue = value;
            } else if (value > maxValue) {
                maxValue = value;
            }
        }

        int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
        int[][] buckets = new int[bucketCount][0];

        // 利用映射函數(shù)將數(shù)據(jù)分配到各個(gè)桶中
        for (int i = 0; i < arr.length; i++) {
            int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
            buckets[index] = arrayAppend(buckets[index], arr[i]);
        }

        int arrIndex = 0;
        for (int[] bucket : buckets) {
            if (bucket.length <= 0) {
                continue;
            }
            // 對(duì)每個(gè)桶進(jìn)行排序,這里使用了插入排序
            bucket = insertionSort(bucket);
            for (int value : bucket) {
                arr[arrIndex++] = value;
            }
        }

        return arr;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,285評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,548評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,878評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,098評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,361評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,613評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,339評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評(píng)論 2 370

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