溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

數(shù)據(jù)結(jié)構(gòu)中各種排序的思路

發(fā)布時間:2020-04-05 18:29:42 來源:網(wǎng)絡(luò) 閱讀:2523 作者:小止1995 欄目:編程語言

排序算法中的穩(wěn)定和不穩(wěn)定指的是什么?

若在待排序的紀(jì)錄中,存在兩個或兩個以上的關(guān)鍵碼值相等的紀(jì)錄,經(jīng)排序后這些記錄的相對次序仍然保持不變,則稱相應(yīng)的排序方法是穩(wěn)定的方法,否則是不穩(wěn)定的方法。


內(nèi)部排序和外部排序

根據(jù)排序過程中涉及的存儲器不同,可以講排序方法分為兩大類:一類是內(nèi)部排序,指的是待排序的幾率存放在計算機(jī)隨機(jī)存儲器中進(jìn)行的排序過程;另一類的外部排序,指的是排序中要對外存儲器進(jìn)行訪問的排序過程。

內(nèi)部排序是排序的基礎(chǔ),在內(nèi)部排序中,根據(jù)排序過程中所依據(jù)的原則可以將它們分為5類:插入排序、交換排序、選擇排序、歸并排序和基數(shù)排序;

根據(jù)排序過程的時間復(fù)雜度來分,可以分為三類:簡單排序、先進(jìn)排序、基數(shù)排序。

評價排序算法優(yōu)劣的標(biāo)準(zhǔn)主要是兩條:一是算法的運算量,這主要是通過記錄的比較次數(shù)和移動次數(shù)來反應(yīng);另一個是執(zhí)行算法所需要的附加存儲單元的的多少。

插入排序:

有一個已經(jīng)有序的數(shù)據(jù)序列,要求在這個已經(jīng)排好的數(shù)據(jù)序列中插入一個數(shù),但要求插入后此數(shù)據(jù)序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數(shù)據(jù)插入到已經(jīng)排好序的有序數(shù)據(jù)中,從而得到一個新的、個數(shù)加一的有序數(shù)據(jù),算法適用于少量數(shù)據(jù)的排序,時間復(fù)雜度為O(n^2)。是穩(wěn)定的排序方法。插入算法把要排序的數(shù)組分成兩部分:第一部分包含了這個數(shù)組的所有元素,但將最后一個元素除外(讓數(shù)組多一個空間才有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成后,再將這個最后元素插入到已排好序的第一部分中。

插入排序的基本思想是:每步將一個待排序的紀(jì)錄,按其關(guān)鍵碼值的大小插入前面已經(jīng)排序的文件中適當(dāng)位置上,直到全部插入完為止。

void InsertSort(int *arr, size_t size)
{
    assert(arr);
    for (int i = 0; i < size - 1; ++i)
    {
        int tmp = arr[i+1];
        int end = i;
        while (end >= 0&&arr[end]>tmp)
        {
            arr[end + 1] = arr[end];
            --end;
        }
        arr[end + 1] = tmp;//即使都大于tmp,將tmp放置arr[0]處
    }
}

希爾排序:

希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進(jìn)版本。希爾排序是非穩(wěn)定排序算法。

希爾排序是把記錄按下標(biāo)的一定增量分組,對每組使用直接插入排序算法排序;隨著增量逐漸減少,每組包含的關(guān)鍵詞越來越多,當(dāng)增量減至1時,整個文件恰被分成一組,算法便終止。

希爾排序是基于插入排序的以下兩點性質(zhì)而提出改進(jìn)方法的:

  1. 插入排序在對幾乎已經(jīng)排好序的數(shù)據(jù)操作時,效率高,即可以達(dá)到線性排序的效率。

  2. 但插入排序一般來說是低效的,因為插入排序每次只能將數(shù)據(jù)移動一位。

  3. 先取一個小于n的整數(shù)d1作為第一個增量,把文件的全部記錄分組。所有距離為d1的倍數(shù)的記錄放在同一個組中。先在各組內(nèi)進(jìn)行直接插入排序;然后,取第二個增量d2<d1重復(fù)上述的分組和排序,直至所取的增量 數(shù)據(jù)結(jié)構(gòu)中各種排序的思路 =1( 數(shù)據(jù)結(jié)構(gòu)中各種排序的思路 < 數(shù)據(jù)結(jié)構(gòu)中各種排序的思路 …<d2<d1),即所有記錄放在同一組中進(jìn)行直接插入排序為止。該方法實質(zhì)上是一種分組插入方法。

  4. void  ShellSort(int *arr, size_t size)
    {
        assert(arr);
        int gap = size;
        while (gap>1)//注意跳出條件
        {
            gap = gap / 3 + 1;
            for (int i = 0; i < size - gap; ++i)//i可取到size-1-gap
            {
                int tmp = arr[i + gap];
                int end = i;
                while (end >= 0 && arr[end]>tmp)
                {
                    arr[end + gap] = arr[end];
                    end-=gap;
                }
                arr[end + gap] = tmp;//即使都大于tmp,將tmp放置arr[0]處
            }
        }
    }

選擇排序:

選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工作原理是每一次從待排序的數(shù)據(jù)元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數(shù)據(jù)元素排完。 

n個記錄的文件的直接選擇排序可經(jīng)過n-1趟直接選擇排序得到有序結(jié)果:

①初始狀態(tài):無序區(qū)為R[1..n],有序區(qū)為空。

②第1趟排序

在無序區(qū)R[1..n]中選出關(guān)鍵字最小的記錄R[k],將它與無序區(qū)的第1個記錄R[1]交換,使R[1..1]和R[2..n]分別變?yōu)橛涗泜€數(shù)增加1個的新有序區(qū)和記錄個數(shù)減少1個的新無序區(qū)。

……

③第i趟排序

第i趟排序開始時,當(dāng)前有序區(qū)和無序區(qū)分別為R[1..i-1]和R(i..n)。該趟排序從當(dāng)前無序區(qū)中選出關(guān)鍵字最小的記錄 R[k],將它與無序區(qū)的第1個記錄R交換,使R[1..i]和R分別變?yōu)橛涗泜€數(shù)增加1個的新有序區(qū)和記錄個數(shù)減少1個的新無序區(qū)


思路:對比數(shù)組中前一個元素跟后一個元素的大小,如果后面的元素比前面的元素小則用一個變量k來記住他的位置,接著第二次比較,前面“后一個元素”現(xiàn)變成了“前一個元素”,繼續(xù)跟他的“后一個元素”進(jìn)行比較如果后面的元素比他要小則用變量k記住它在數(shù)組中的位置(下標(biāo)),等到循環(huán)結(jié)束的時候,我們應(yīng)該找到了最小的那個數(shù)的下標(biāo)了,然后進(jìn)行判斷,如果這個元素的下標(biāo)不是第一個元素的下標(biāo),就讓第一個元素跟他交換一下值,這樣就找到整個數(shù)組中最小的數(shù)了。然后找到數(shù)組中第二小的數(shù),讓他跟數(shù)組中第二個元素交換一下值,以此類推。

void  SearchSort(int *arr, size_t size)
{
    assert(arr);
    for (int i = 0,j=size-1; i < j; ++i,-j)
    {
        int min = i;
        int max = j;
        for (int k = i; k<=j;++k)
        {
            if (arr[min]>arr[k])
                min = k;
            if (arr[max] < arr[k])
                max = k;
        }
        if (min != i)
        {
            int temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
 
            if (max == i) //如果最大值在最左邊,肯定要被移走,此時要轉(zhuǎn)移到相應(yīng)的新位置 
            {
                max = min;
            }
        }
 
        if (max != j)
        {
            int tmp = arr[j];
            arr[j] = arr[max];
            arr[max] = tmp;
        }
    }
}

快速排序:

快速排序(Quicksort)是對冒泡排序的一種改進(jìn)。

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,整個排序過程可以遞歸進(jìn)行,以此達(dá)到整個數(shù)據(jù)變成有序序列

在c++中可以用函數(shù)qsort()可以直接為數(shù)組進(jìn)行排序。

用 法:

void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *));

參數(shù):
  1 待排序數(shù)組首地址
  2 數(shù)組中待排序元素數(shù)量
  3 各元素的占用空間大小
  4 指向函數(shù)的指針,用于確定排序的順序。

int Quick_Sort(int *arr, int left, int right)
{
    assert(arr);
    if (left >= right)
        return right;
    int tmp = arr[right];
    int index = right;
    --right;
    while (left < right)
    {
        while (left < right&&arr[left] <= tmp)
            left++;
        while (left < right&&arr[right] >=tmp)
            right--;
        if (left < right)
            swap(arr[left], arr[right]);
    }
    if (arr[right] >= tmp)
        swap(arr[right], arr[index]);//重點
    return right;
}
void QuickSort(int* arr,int left,int right)
{
    assert(arr);
    if (left < right)
    {
        int mid = Quick_Sort(arr, left, right);
        QuickSort(arr, left, mid-1);
        QuickSort(arr, mid+1, right);
    }
     
}

冒泡排序:

它重復(fù)地走訪過要排序的數(shù)列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換,也就是說該數(shù)列已經(jīng)排序完成。

這個算法的名字由來是因為越大的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端。

冒泡排序算法的運作如下:(從后往前)

  1. 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。

  2. 對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對。在這一點,最后的元素應(yīng)該會是最大的數(shù)。

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

  4. 持續(xù)每次對越來越少的元素重復(fù)上面的步驟,直到?jīng)]有任何一對數(shù)字需要比較

  5. void BubbleSort(int* arr, size_t size)
    {
        assert(arr);
        int flag = 0;
        for (int i = 0; i < size - 1; ++i)
        {
            flag = 0;
            for (int j = 0; j < size - i - 1;++j)
            {
                if (arr[j]>arr[j + 1])
                {
                    swap(arr[j], arr[j + 1]);
                }
                flag = 1;
            }
            if (flag == 0)
                break;
        }
    }

歸并排序:

歸并排序(MERGE-SORT)是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為二路歸并。

歸并過程為:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元素a[i]復(fù)制到r[k]中,并令i和k分別加上1;否則將第二個有序表中的元素a[j]復(fù)制到r[k]中,并令j和k分別加上1,如此循環(huán)下去,直到其中一個有序表取完,然后再將另一個有序表中剩余的元素復(fù)制到r中從下標(biāo)k到下標(biāo)t的單元。歸并排序的算法我們通常用遞歸實現(xiàn),先把待排序區(qū)間[s,t]以中點二分,接著把左邊子區(qū)間排序,再把右邊子區(qū)間排序,最后把左區(qū)間和右區(qū)間用一次歸并操作合并成有序的區(qū)間[s,t]

歸并操作(merge),也叫歸并算法,指的是將兩個順序序列合并成一個順序序列的方法。

如 設(shè)有數(shù)列{6,202,100,301,38,8,1}

初始狀態(tài):6,202,100,301,38,8,1

第一次歸并后:{6,202},{100,301},{8,38},{1},比較次數(shù):3;

第二次歸并后:{6,100,202,301},{1,8,38},比較次數(shù):4;

第三次歸并后:{1,6,8,38,100,202,301},比較次數(shù):4;

總的比較次數(shù)為:3+4+4=11,;

逆序數(shù)為14;


歸并操作的工作原理如下:

第一步:申請空間,使其大小為兩個已經(jīng)排序序列之和,該空間用來存放合并后的序列

第二步:設(shè)定兩個指針,最初位置分別為兩個已經(jīng)排序序列的起始位置

第三步:比較兩個指針?biāo)赶虻脑?,選擇相對小的元素放入到合并空間,并移動指針到下一位置

重復(fù)步驟3直到某一指針超出序列尾

將另一序列剩下的所有元素直接復(fù)制到合并序列尾


/歸并[)升序
//使用鏈表合并思想
void Merge(int* src, int* dest, int begin1, int end1, int begin2, int end2)
{
    assert(src&&dest);
    int index = begin1;//兩個區(qū)間挨著
    while (begin1 < end1&&begin2 < end2)
    {
        if (src[begin1] < src[begin2])
        {
            dest[index++] = src[begin1++];
        }
        else
        {
            dest[index++] = src[begin2++];
        }
    }
    if (begin1 < end1)
    {
        while (begin1 < end1)
            dest[index++] = src[begin1++];
    }
    else
    {
        while (begin2 < end2)
            dest[index++] = src[begin2++];
    }
}
void _MergeSort(int* src, int* dst, int left, int right)
{
    if (left < right - 1)//[)
    {
        int mid = left + ((right - left) >> 1);
        _MergeSort(src, dst, left, mid);
        _MergeSort(src, dst, mid, right);
 
        Merge(src, dst, left, mid, mid, right);
        memcpy(src + left, dst + left, sizeof(int)*(right - left));
    }
}
void MergeSort(int *a, size_t size)
{
    int *dest = new int[size];
    _MergeSort(a, dest, 0, 10);
    delete[] dest;
}

基數(shù)排序:

基數(shù)排序(radix sort)屬于“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達(dá)到排序的作用,基數(shù)排序法是屬于穩(wěn)定性的排序,其時間復(fù)雜度為O (nlog(r)m),其中r為所采取的基數(shù),而m為堆數(shù),在某些時候,基數(shù)排序法的效率高于其它的穩(wěn)定性排序法。

第一步

以LSD為例,假設(shè)原來有一串?dāng)?shù)值如下所示:

73, 22, 93, 43, 55, 14, 28, 65, 39, 81

首先根據(jù)個位數(shù)的數(shù)值,在走訪數(shù)值時將它們分配至編號0到9的桶子中:

0

1 81

2 22

3 73 93 43

4 14

5 55 65

6

7

8 28

9 39


第二步

接下來將這些桶子中的數(shù)值重新串接起來,成為以下的數(shù)列:

81, 22, 73, 93, 43, 14, 55, 65, 28, 39

接著再進(jìn)行一次分配,這次是根據(jù)十位數(shù)來分配:

0

1 14

2 22 28

3 39

4 43

5 55

6 65

7 73

8 81

9 93


第三步

接下來將這些桶子中的數(shù)值重新串接起來,成為以下的數(shù)列:

14, 22, 28, 39, 43, 55, 65, 73, 81, 93

這時候整個數(shù)列已經(jīng)排序完畢;如果排序的對象有三位數(shù)以上,則持續(xù)進(jìn)行以上的動作直至最高位數(shù)為止。

//基數(shù)排序:利用哈希桶原理把數(shù)據(jù)排序,可選擇從低位到高位或從高位到低位
//利用稀疏矩陣壓縮存儲進(jìn)行數(shù)據(jù)定位
int  GetDigit(int* arr, size_t size)
{
    int maxDigit = 1;
    int maxNum = 10;
    for (int i = 0; i < size; ++i)
    {
        if (arr[i] >= maxNum)
        {
            int count = maxDigit;
            while (maxNum <= arr[i])
            {
                maxNum *= 10;
                ++count;
            }
            maxDigit = count;
        }
    }
    return maxDigit;
}
void LSDSort(int* arr, size_t size)//從低位開始排  MSD從高位開始排
{
    int counts[10] = { 0 };//存位數(shù)對應(yīng)數(shù)據(jù)個數(shù)
    int startCounts[10] = { 0 };
    int *bucket = new int[size];
    int digit = 1;//表示先取各位
    int maxDigit = GetDigit(arr, size);
    int divider = 1;//除數(shù)
    while (digit++ <= maxDigit)
    {
        memset(counts, 0, sizeof(int) * 10);
        memset(startCounts, 0, sizeof(int) * 10);
        for (int i = 0; i < size; ++i)
            counts[(arr[i]/divider)% 10]++;
        for (int i = 1; i < 10; ++i)
            startCounts[i] = startCounts[i - 1] + counts[i - 1];
        for (int i = 0; i < size; ++i)
        {
            bucket[startCounts[(arr[i] / divider) % 10]++] = arr[i];
        }
        divider *= 10;
        memcpy(arr, bucket, sizeof(int)*size);
    }
    memcpy(arr, bucket, sizeof(int)*size);
    delete[] bucket;
}

計數(shù)排序:

計數(shù)排序是一個非基于比較的排序算法,該算法于1954年由 Harold H. Seward 提出。它的優(yōu)勢在于在對一定范圍內(nèi)的整數(shù)排序時,它的復(fù)雜度為Ο(n+k)(其中k是整數(shù)的范圍),快于任何比較排序算法。

計數(shù)排序?qū)斎氲臄?shù)據(jù)有附加的限制條件:

1、輸入的線性表的元素屬于有限偏序集S;

2、設(shè)輸入的線性表的長度為n,|S|=k(表示集合S中元素的總數(shù)目為k),則k=O(n)。

在這兩個條件下,計數(shù)排序的復(fù)雜性為O(n)。

計數(shù)排序的基本思想是對于給定的輸入序列中的每一個元素x,確定該序列中值小于x的元素的個數(shù)(此處并非比較各元素的大小,而是通過對元素值的計數(shù)和計數(shù)值的累加來確定)。

void CountSort(int *arr, size_t size,int len)
{
    int* bitMap = new int[len];
    memset(bitMap, 0, sizeof(int)*len);
    for (int i = 0; i < size; ++i)
    {
        int index = arr[i] >>5;//除以32
        int count = arr[i] % 32;
        bitMap[index] |= (1 << count);
         
    }
    int index = 0;
     
    for (int i = 0; i < len; ++i)
    {
        int count = 0;
        while (count <32&&index<size )
 
        {
            if (bitMap[i] & (1 << count))
                arr[index++] = i * 32 + count;
            ++count;
        }
    }
    delete[] bitMap;
}



數(shù)據(jù)結(jié)構(gòu)中各種排序的思路

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI