1. 程式人生 > >淺談演算法和資料結構: 四 快速排序

淺談演算法和資料結構: 四 快速排序

上篇文章介紹了時間複雜度為O(nlgn)的合併排序,本篇文章介紹時間複雜度同樣為O(nlgn)但是排序速度比合並排序更快的快速排序(Quick Sort)。

Quick Sort founder

快速排序也是一種採用分治法解決問題的一個典型應用。在很多程式語言中,對陣列,列表進行的非穩定排序在內部實現中都使用的是快速排序。而且快速排序在面試中經常會遇到。

本文首先介紹快速排序的思路,演算法的實現、分析、優化及改進,最後分析了.NET 中列表排序的內部實現。

一 原理

Sorting_quicksort_anim

快速排序的基本思想如下:

  1. 對陣列進行隨機化。
  2. 從數列中取出一個數作為中軸數(pivot)。
  3. 將比這個數大的數放到它的右邊,小於或等於它的數放到它的左邊。
  4. 再對左右區間重複第三步,直到各區間只有一個數。

Basic Step of Quick Sort

如上圖所示快速排序的一個重要步驟是對序列進行以中軸數進行劃分,左邊都小於這個中軸數,右邊都大於該中軸數,然後對左右的子序列繼續這一步驟直到子序列長度為1。

下面來看某一次劃分的步驟,如下圖:

Partition trace in Quick Sort

上圖中的劃分操作可以分為以下5個步驟:

  1. 獲取中軸元素
  2. i從左至右掃描,如果小於基準元素,則i自增,否則記下a[i]
  3. j從右至左掃描,如果大於基準元素,則i自減,否則記下a[j]
  4. 交換a[i]和a[j]
  5. 重複這一步驟直至i和j交錯,然後和基準元素比較,然後交換。

劃分過程的程式碼實現如下:

/// <summary>
/// 
快速排序中的劃分過程 /// </summary> /// <param name="array">待劃分的陣列</param> /// <param name="lo">最左側位置</param> /// <param name="hi">最右側位置</param> /// <returns>中間元素位置</returns> private static int Partition(T[] array, int lo, int hi) { int i = lo, j = hi + 1; while
(true) { //從左至右掃描,如果碰到比基準元素array[lo]小,則該元素已經位於正確的分割槽,i自增,繼續比較i+1; //否則,退出迴圈,準備交換 while (array[++i].CompareTo(array[lo]) < 0) { //如果掃描到了最右端,退出迴圈 if (i == hi) break; } //從右自左掃描,如果碰到比基準元素array[lo]大,則該元素已經位於正確的分割槽,j自減,繼續比較j-1 //否則,退出迴圈,準備交換 while (array[--j].CompareTo(array[lo]) > 0) { //如果掃描到了最左端,退出迴圈 if (j == lo) break; } //如果相遇,退出迴圈 if (i >= j) break; //交換左a[i],a[j]右兩個元素,交換完後他們都位於正確的分割槽 Swap(array, i, j); } //經過相遇後,最後一次a[i]和a[j]的交換 //a[j]比a[lo]小,a[i]比a[lo]大,所以將基準元素與a[j]交換 Swap(array, lo, j); //返回掃描相遇的位置點 return j; }

劃分前後,元素在序列中的分佈如下圖:

before and after partition

二 實現

與合併演算法基於合併這一過程一樣,快速排序基於分割(Partition)這一過程。只需要遞迴呼叫Partition這一操作,每一次以Partition返回的元素位置來劃分為左右兩個子序列,然後繼續這一過程直到子序列長度為1,程式碼的實現如下:

public class QuickSort<T> where T : IComparable<T>
{
    public static void Sort(T[] array)
    {
        Sort(array, 0, array.Length - 1);
    }

    private static void Sort(T[] array, int lo, int hi)
    {
        //如果子序列為1,則直接返回
        if (lo >= hi) return;
        //劃分,劃分完成之後,分為左右序列,左邊所有元素小於array[index],右邊所有元素大於array[index]
        int index = Partition(array, lo, hi);

       //對左右子序列進行排序完成之後,整個序列就有序了
        //對左邊序列進行遞迴排序
        Sort(array, lo, index - 1);
        //對右邊序列進行遞迴排序
        Sort(array, index + 1, hi);
    }
}

下圖說明了快速排序中,每一次劃分之後的結果:

the two part sorted

一般快速排序的動畫如下:

quicksort

三 分析

  1. 在最好的情況下,快速排序只需要大約nlgn次比較操作,在最壞的情況下需要大約1/2 n2 次比較操作。

    在最好的情況下,每次的劃分都會恰好從中間將序列劃分開來,那麼只需要lgn次劃分即可劃分完成,是一個標準的分治演算法Cn=2Cn/2+N,每一次劃分都需要比較N次,大家可以回想下我們是如何證明合併排序的時間複雜度的。

    the compare complexity in  quick sort at the bese case

    在最壞的情況下,即序列已經排好序的情況下,每次劃分都恰好把陣列劃分成了0,n兩部分,那麼需要n次劃分,但是比較的次數則變成了n, n-1, n-2,….1, 所以整個比較次數約為n(n-1)/2~n2/2.

    the compare complexity in  quick sort at the worst case

  2. 快速排序平均需要大約2NlnN次比較,來對長度為n的排序關鍵字唯一的序列進行排序。 證明也比較簡單:假設CN為快速排序平均花在比較上的時間,初始C0=C1=0,對於N>1的情況,有:

    image

    其中N+1是分割時的比較次數,image 表示將序列分割為0,和N-1左右兩部分的概率為1/N, 劃分為1,N-2左右兩部分的概率也為1/N,都是等概率的。

    然後對上式左右兩邊同時乘以N,整理得到:

    image

    然後,對於N為N-1的情況:

    image

    兩式相減,然後整理得到:

    image

    然後左右兩邊同時除以N(N+1),得到:

    image

    可以看到,這是一個遞迴式,我們將image 遞迴展開得到:

    image

    然後處理一下得到:

    image

  3. 平均情況下,快速排序需要大約1.39NlgN次比較,這比合並排序多了39%的比較,但是由於涉及了較少的資料交換和移動操作,他要比合並排序更快。
  4. 為了避免出現最壞的情況,導致序列劃分不均,我們可以首先對序列進行隨機化排列然後再進行排序就可以避免這一情況的出現。
  5. 快速排序是一種就地(in-place)排序演算法。在分割操作中只需要常數個額外的空間。在遞迴中,也只需要對數個額外空間。
  6. 另外,快速排序是非穩定性排序。

Quick Sort is not Stable

四 改進

對一般快速排序進行一些改進可以提高其效率。

1. 當劃分到較小的子序列時,通常可以使用插入排序替代快速排序

對於較小的子序列(通常序列元素個數為10個左右),我們就可以採用插入排序直接進行排序而不用繼續遞迴,演算法改造如下:

private const int CUTTOFF = 10;
private static void Sort(T[] array, int lo, int hi)
{
    //如果子序列為1,則直接返回
    if (lo >= hi) return;
    //對於小序列,直接採用插入排序替代
    if (hi - lo <= CUTTOFF - 1) 
    {
        Sort<int>.InsertionSort(array, lo, hi);
        return;
    }
    //劃分,劃分完成之後,分為左右序列,左邊所有元素小於array[index],右邊所有元素大於array[index]
    int index = Partition(array, lo, hi);

    //對左右子序列進行排序完成之後,整個序列就有序了

    //對左邊序列進行遞迴排序
    Sort(array, lo, index - 1);
    //對右邊序列進行遞迴排序
    Sort(array, index + 1, hi);
}

2. 三平均分割槽法(Median of three partitioning)

在一般的的快速排序中,選擇的是第一個元素作為中軸(pivot),這會出現某些分割槽嚴重不均的極端情況,比如劃分為了1和n-1兩個序列,從而導致出現最壞的情況。三平均分割槽法與一般的快速排序方法不同,它並不是選擇待排陣列的第一個數作為中軸,而是選用待排陣列最左邊、最右邊和最中間的三個元素的中間值作為中軸。這一改進對於原來的快速排序演算法來說,主要有兩點優勢:

(1) 首先,它使得最壞情況發生的機率減小了。

(2) 其次,未改進的快速排序演算法為了防止比較時陣列越界,在最後要設定一個哨點。如果在分割槽排序時,中間的這個元素(也即中軸)是與最右邊數過來第二個元素進行交換的話,那麼就可以省略與這一哨點值的比較。

對於三平均分割槽法還可以進一步擴充套件,在選取中軸值時,可以從由左中右三個中選取擴大到五個元素中或者更多元素中選取,一般的,會有(2t+1)平均分割槽法(median-of-(2t+1)。常用的一個改進是,當序列元素小於某個閾值N時,採用三平均分割槽,當大於時採用5平均分割槽。

採用三平均分割槽法對快速排序的改進如下:

private static void Sort(T[] array, int lo, int hi)
{
    //對於小序列,直接採用插入排序替代
    if (hi - lo <= CUTTOFF - 1) 
    {
        //Sort<int>.InsertionSort(array, lo, hi);
        return;
    }
    //採用三平均分割槽法查詢中軸
    int m = MedianOf3(array, lo, lo + (hi - lo) / 2, hi);
    Swap(array, lo, m);
    //劃分,劃分完成之後,分為左右序列,左邊所有元素小於array[index],右邊所有元素大於array[index]
    int index = Partition(array, lo, hi);

    //對左右子序列進行排序完成之後,整個序列就有序了

    //對左邊序列進行遞迴排序
    Sort(array, lo, index - 1);
    //對右邊序列進行遞迴排序
    Sort(array, index + 1, hi);
}

/// <summary>
/// 查詢三個元素中位於中間的那個元素
/// </summary>
/// <param name="array"></param>
/// <param name="lo"></param>
/// <param name="center"></param>
/// <param name="hi"></param>
/// <returns></returns>
private static int MedianOf3(T[] array, int lo, int center, int hi)
{
    return (Less(array[lo], array[center]) ?
           (Less(array[center], array[hi]) ? center : Less(array[lo], array[hi]) ? hi : lo) :
           (Less(array[hi], array[center]) ? center : Less(array[hi], array[lo]) ? hi : lo));
}

private static bool Less(T t1, T t2)
{
    return t1.CompareTo(t2) < 0;
}

使用插入排序對小序列進行排序以及使用三平均分割槽法對一般快速排序進行改進後執行結果示意圖如下:

Improvement using Insertsort and 3 mediation partition

3. 三分割槽(3-way partitioning) 快速排序

通常,我們的待排序的序列關鍵字中會有很多重複的值,比如我們想對所有的學生按照年齡進行排序,按照性別進行排序等,這樣每一類別中會有很多的重複的值。理論上,這些重複的值只需要處理一次就行了。但是一般的快速排序會遞迴進行劃分,因為一般的快速排序只是將序列劃分為了兩部分,小於或者大於等於這兩部分。

既然要利用連續、相等的元素不需要再參與排序這個事實,一個直接的想法就是通過劃分讓相等的元素連續地擺放:

 3-way partition quick sort

然後只對左側小於V的序列和右側大於V對的序列進行排序。這種三路劃分與電腦科學中無處不在,它與Dijkstra提出的“荷蘭國旗問題”(The Dutch National Flag Problem)非常相似。

Dijkstra的方法如上圖:

從左至右掃描陣列,維護一個指標lt使得[lo…lt-1]中的元素都比v小,一個指標gt使得所有[gt+1….hi]的元素都大於v,以及一個指標i,使得所有[lt…i-1]的元素都和v相等。元素[i…gt]之間是還沒有處理到的元素,i從lo開始,從左至右開始掃描:

· 如果a[i]<v: 交換a[lt]和a[i],lt和i自增

· 如果a[i]>v:交換a[i]和a[gt], gt自減

· 如果a[i]=v: i自增

下面是使用Dijkstra的三分割槽快速排序程式碼:

private static void Sort(T[] array, int lo, int hi)
{
    //對於小序列,直接採用插入排序替代
    if (hi - lo <= CUTTOFF - 1)
    {
        Sort<int>.InsertionSort(array, lo, hi);
        return;
    }
    //三分割槽
    int lt = lo, i = lo + 1, gt = hi;
    T v = array[lo];
    while (i<=gt)
    {
        int cmp = array[i].CompareTo(v);
        if (cmp < 0) Swap(array, lt++, i++);
        else if (cmp > 0) Swap(array, i, gt--);
        else i++;
    }

    //對左邊序列進行遞迴排序
    Sort(array, lo, lt - 1);
    //對右邊序列進行遞迴排序
    Sort(array, gt + 1, hi);
}

三分割槽快速排序的每一步如下圖所示:

3-way partitioning trace

三分割槽快速排序的示意圖如下:

3 way quick sort visual trace

Dijkstra的三分割槽快速排序雖然在快速排序發現不久後就提出來了,但是對於序列中重複值不多的情況下,它比傳統的2分割槽快速排序需要更多的交換次數。

Bentley 和D. McIlroy在普通的三分割槽快速排序的基礎上,對一般的快速排序進行了改進。在劃分過程中,i遇到的與v相等的元素交換到最左邊,j遇到的與v相等的元素交換到最右邊,i與j相遇後再把陣列兩端與v相等的元素交換到中間

Bentley  McIlroy 3 way partition 

這個方法不能完全滿足只掃描一次的要求,但它有兩個好處:首先,如果資料中沒有重複的值,那麼該方法幾乎沒有額外的開銷;其次,如果有重複值,那麼這些重複的值不會參與下一趟排序,減少了無用的劃分。

下面是採用 Bentley&D. McIlroy 三分割槽快速排序的演算法改進:

private static void Sort(T[] array, int lo, int hi)
{
    //對於小序列,直接採用插入排序替代
    if (hi - lo <= CUTTOFF - 1)
    {
        Sort<int>.InsertionSort(array, lo, hi);
        return;
    }
    // Bentley-McIlroy 3-way partitioning
    int i = lo, j = hi + 1;
    int p = lo, q = hi + 1;
    T v = array[lo];
    while (true)
    {
        while (Less(array[++i], v))
            if (i == hi) break;
        while (Less(v, array[--j]))
            if (j == lo) break;

        // pointers cross
        if (i == j && Equal(array[i], v))
            Swap(array, ++p, i);
        if (i >= j) break;

        Swap(array, i, j);
        if (Equal(array[i], v)) Swap(array, ++p, i);
        if (Equal(array[j], v)) Swap(array, --q, j);
    }

    //將相等的元素交換到中間
    i = j + 1;
    for (int k = lo; k <= p; k++) Swap(array, k, j--);
    for (int k = hi; k >= q; k--) Swap(array, k, i++);

    Sort(array, lo, j);
    Sort(array, i, hi);
}

三分割槽快速排序的動畫如下:

3wayquick sort

4.並行化

和前面討論對合並排序的改進一樣,對所有使用分治法解決問題的演算法其實都可以進行並行化,快速排序的並行化改進我在之前的淺談併發與並行這篇文章中已經有過介紹,這裡不再贅述。

五 .NET 中元素排序的內部實現

快速排序作為一種優秀的排序演算法,在很多程式語言的元素內部排序中均有實現,比如Java中對基本資料型別(primitive type)的排序,C++,Matlab,Python,FireFox Javascript等語言中均將快速排序作為其內部元素排序的演算法。同樣.NET中亦是如此。

.NET這種對List<T>陣列元素進行排序是通過呼叫Sort方法實現的,其內部則又是通過Array.Sort實現,MSDN上說在.NET 4.0及之前的版本,Array.Sort採用的是快速排序,然而在.NET 4.5中,則對這一演算法進行了改進,採用了名為Introspective sort 的演算法,即保證在一般情況下達到最快排序速度,又能保證能夠在出現最差情況是進行優化。他其實是一種混合演算法:

  • 當待分割槽的元素個數小於16個時,採用插入排序
  • 當分割槽次數超過2*logN,N是輸入陣列的區間大小,則使用堆排序(Heapsort)
  • 否則,使用快速排序。

有了Reflector這一神器,我們可以檢視.NET中的ArraySort的具體實現:

Array.Sort這一方法在mscorlib這一程式集中,具體的實現方法有分別針對泛型和普通型別的SortedGenericArray和SortedObjectArray,裡面的實現大同小異,我們以SortedGenericArray這個類來作為例子看:

ArraySort implementation in .NET_1

首先要看的是Sort方法,其實現如下:

ArraySort implementation in .NET_2

該方法中,首先判斷執行的.NET對的版本,如果是4.5及以上版本,則用IntrospectiveSort演算法,否則採用限定深度的快速排序演算法DepthLimitedQuickSort。先看IntrospectiveSort:

ArraySort implementation in .NET_3

該方法第一個元素為陣列的最左邊元素位置,第二個引數為最右邊元素位置,第三個引數為2*log2N,繼續看方法內部:

ArraySort implementation in .NET_4

可以看到,當num<=16時,如果元素個數為1,2,3,則直接呼叫SwapIfGreaterWithItem進行排序了。否則直接呼叫InsertSort進行插入排序。

這裡面也是一個迴圈,每迴圈一下depthLimit就減小1個,如果為0表示劃分的次數超過了2logN,則直接呼叫基排序(HeapSort),這裡面的劃分方法PickPivortAndPartitin的實現如下:

ArraySort implementation in .NET_5

它其實是一個標準的三平均快速排序。可以看到在.NET 4.5中對Quick進行優化的部分主要是在元素個數比較少的時候採用選擇插入,並且在遞迴深度超過2logN的時候,採用基排序。

下面再來看下在.NET 4.0及以下平臺下排序DepthLimitedQuickSort方法的實現:

從名稱中可以看出這是限定深度的快速排序,在第三個引數傳進去的是0x20,也就是32。

DepthLimitedQuickSort

可以看到,當劃分的次數大於固定的32次的時候,採用了基排序,其他的部分是普通的快速排序。

六 總結

由於快速排序在排序演算法中具有排序速度快,而且是就地排序等優點,使得在許多程式語言的內部元素排序實現中採用的就是快速排序,本問首先介紹了一般的快速排序,分析了快速排序的時間複雜度,然後就分析了對快速排序的幾點改進,包括對小序列採用插入排序替代,三平均劃分,三分區劃分等改進方法。最後介紹了.NET不同版本下的對元素內部排序的實現。

快速排序很重要,希望本文對您瞭解快速排序有所幫助。

相關推薦

演算法資料結構: 快速排序

上篇文章介紹了時間複雜度為O(nlgn)的合併排序,本篇文章介紹時間複雜度同樣為O(nlgn)但是排序速度比合並排序更快的快速排序(Quick Sort)。 快速排序也是一種採用分治法解決問題的一個典型應用。在很多程式語言中,對陣列,列表進行的非穩定排序在內部實現中都使用的是快速排序。而且快速排序在

演算法資料結構: 三 合併排序

合併排序,顧名思義,就是通過將兩個有序的序列合併為一個大的有序的序列的方式來實現排序。合併排序是一種典型的分治演算法:首先將序列分為兩部分,然後對每一部分進行迴圈遞迴的排序,然後逐個將結果進行合併。   合併排序最大的優點是它的時間複雜度為O(nlgn),這個是我們之前的選擇排序和插入排序所達不到的。他還

演算法資料結構: 二 基本排序演算法

本篇開始學習排序演算法。排序與我們日常生活中息息相關,比如,我們要從電話簿中找到某個聯絡人首先會按照姓氏排序、買火車票會按照出發時間或者時長排序、買東西會按照銷量或者好評度排序、查詢檔案會按照修改時間排序等等。在計算機程式設計中,排序和查詢也是最基本的演算法,很多其他的演算法都是以排序演算法為基礎,在一般的資

查詢演算法 演算法資料結構: 七 二叉查詢樹 演算法資料結構: 十一 雜湊表

閱讀目錄 1. 順序查詢 2. 二分查詢 3. 插值查詢 4. 斐波那契查詢 5. 樹表查詢 6. 分塊查詢 7. 雜湊查詢   查詢是在大量的資訊中尋找一個特定的資訊元素,在計算機應用中,查詢是常用的基本運算,例如編譯程式中符號表的查詢。本文

演算法資料結構: 五 優先順序佇列與堆排序

在很多應用中,我們通常需要按照優先順序情況對待處理物件進行處理,比如首先處理優先順序最高的物件,然後處理次高的物件。最簡單的一個例子就是,在手機上玩遊戲的時候,如果有來電,那麼系統應該優先處理打進來的電話。 在這種情況下,我們的資料結構應該提供兩個最基本的操作,一個是返回最高優先

演算法資料結構: 八 平衡查詢樹之2-3樹

前面介紹了二叉查詢樹(Binary Search Tree),他對於大多數情況下的查詢和插入在效率上來說是沒有問題的,但是他在最差的情況下效率比較低。本文及後面文章介紹的平衡查詢樹的資料結構能夠保證在最差的情況下也能達到lgN的效率,要實現這一目標我們需要保證樹在插入完成之後

演算法資料結構: 九 平衡查詢樹之紅黑樹

前面一篇文章介紹了2-3查詢樹,可以看到,2-3查詢樹能保證在插入元素之後能保持樹的平衡狀態,最壞情況下即所有的子節點都是2-node,樹的高度為lgN,從而保證了最壞情況下的時間複雜度。但是2-3樹實現起來比較複雜,本文介紹一種簡單實現2-3樹的資料結構,即紅黑樹(

演算法資料結構: 十 平衡查詢樹之B樹

前面講解了平衡查詢樹中的2-3樹以及其實現紅黑樹。2-3樹種,一個節點最多有2個key,而紅黑樹則使用染色的方式來標識這兩個key。 維基百科對B樹的定義為“在電腦科學中,B樹(B-tree)是一種樹狀資料結構,它能夠儲存資料、對其進行排序並允許以O(log n)的時間複雜度執行進行查詢、順序讀取、插入和刪

演算法資料結構: 十一 雜湊表

在前面的系列文章中,依次介紹了基於無序列表的順序查詢,基於有序陣列的二分查詢,平衡查詢樹,以及紅黑樹,下圖是他們在平均以及最差情況下的時間複雜度: 可以看到在時間複雜度上,紅黑樹在平均情況下插入,查詢以及刪除上都達到了lgN的時間複雜度。 那麼有沒有查詢效率更高的資料結構呢,答案就是本文接下來要介紹了

演算法資料結構: 六 符號表及其基本實現

前面幾篇文章介紹了基本的排序演算法,排序通常是查詢的前奏操作。從本文開始介紹基本的查詢演算法。 在介紹查詢演算法,首先需要了解符號表這一抽象資料結構,本文首先介紹了什麼是符號表,以及這一抽象資料結構的的API,然後介紹了兩種簡單的符號表的實現方式。 一符號表 在開始介紹查詢演算法之前,我們需要定義一個名

演算法資料結構----無向圖相關演算法基礎

最近幾個專案用到了求所有最小哈密爾頓迴路,貪婪遍歷查詢等演算法,都是自己想或者查論文,雖然都是資料結構的基礎內容,但感覺比較零散,很糾結。 前幾天突然聽到“圖計算”這個名詞,覺得應該是找到組織了,因此轉載如下,後續會不斷轉載其他有用的文章。 以下內容轉載自:http:/

演算法資料結構: 一 棧佇列

最近晚上在家裡看Algorithems,4th Edition,我買的英文版,覺得這本書寫的比較淺顯易懂,而且“圖碼並茂”,趁著這次機會打算好好學習做做筆記,這樣也會印象深刻,這也是寫這一系列文章的原因。另外普林斯頓大學在Coursera 上也有這本書同步的公開課,還有另外一門演算法分析課,這門課程的作者也是

演算法資料結構(11):雜湊表

在前面的系列文章中,依次介紹了基於無序列表的順序查詢,基於有序陣列的二分查詢,平衡查詢樹,以及紅黑樹,下圖是它們在平均以及最差情況下的時間複雜度: 可以看到在時間複雜度上,紅黑樹在平均情況下插入,查詢以及刪除上都達到了lgN的時間複雜度。 那麼

演算法資料結構(7):二叉查詢樹

前文介紹了符號表的兩種實現,無序連結串列和有序陣列,無序連結串列在插入的時候具有較高的靈活性,而有序陣列在查詢時具有較高的效率,本文介紹的二叉查詢樹(Binary Search Tree,BST)這一資料結構綜合了以上兩種資料結構的優點。 二叉查詢樹具有很高的靈活性

演算法資料結構:雜湊表

在前面的系列文章中,依次介紹了基於無序列表的順序查詢,基於有序陣列的二分查詢,平衡查詢樹,以及紅黑樹,下圖是它們在平均以及最差情況下的時間複雜度: 可以看到在時間複雜度上,紅黑樹在平均情況下插入,查詢以及刪除上都達到了lgN的時間複雜度。 那麼有沒

《常見演算法資料結構》元素排序(1)——簡單排序(附動畫)

元素排序(1)——簡單排序 本系列文章主要介紹常用的演算法和資料結構的知識,記錄的是《Algorithms I/II》課程的內容,採用的是“演算法(第4版)”這本紅寶書作為學習教材的,語言是

(筆記)如何快速攻破傳統演算法資料結構

知乎上的SimonS大神的講座,給想學習演算法的童鞋的一些建議: https://www.zhihu.com/lives/795264798257479680 一,QA部分: 1. 985 大二 專業軟工 沒有oi經歷。如何一年時間拿到ACM區預賽銀牌? 主要參考後續的

拒絕調包俠,不需要高階演算法資料結構技巧

前言 大多數工科學生或者剛剛入門近年來比較火的“人工智慧”相關演算法的同學,在選擇語言的時候,都會選擇MATLAB、Python、R等等這些高階語言,對自己所學的演算法進行實現和除錯。這些高階語言中,包含了實現複雜演算法的基礎數學演算法、基本統計演算法、基礎資料結構的實現,比如均值(mean)、方差(std

java中各種演算法資料結構的使用場景

一。通用資料結構:陣列,連結串列,樹,雜湊表 通用資料結構通過關鍵字的值來儲存並查詢資料,如報表,合同,記錄,業績等資料。通用資料結構可以用速度的快慢來分類,陣列和連結串列是最慢的,樹相對較快,雜湊表是最快的。請注意,並不是最快的就一定是最好的,因為最快的結構的

演算法資料結構

資料結構 堆 長度為n的陣列構建成最小堆的時間複雜度 B、B+樹、紅黑樹 說一下B+樹和二叉搜尋樹的區別? 說一下二叉搜尋樹和AVL樹、紅黑樹之間的差別 說下紅黑樹原理,紅黑樹你看虛擬碼的時候他有兩坨比較一樣的有沒有注意過 哪些情況下用棧 知道雜湊嗎?二叉樹比