日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二)

發布時間:2024/9/19 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
我的上一篇文章向大家介紹了排序算法中的冒泡排序、插入排序和選擇排序。它們都是平均時間復雜度為 O(n^2) 的排序算法,同時還為大家講解了什么是原地排序和什么是排序的穩定性。下圖是這三種算法的比較,不熟悉的同學也可以點擊我的上一篇文章回顧一下。小R同學:[數據結構與算法系列]排序算法(一)?zhuanlan.zhihu.com


由于它們的時間復雜度都比較高,所以在處理大規模數據的時候這三種算法就不那么適用了。接下我將為大家介紹另外兩種更高效的排序算法——歸并排序和快速排序。

聲明:在下面所有的算法講解中,我們默認我們需要對待處理數組進行升序排序,即排序好的數組中,左邊的元素要小于右邊的元素。

1.分治思想(Divide and Conquer)

在計算機科學中,分治法是基于多項分支遞歸的一種重要的算法思想。從名字可以看出,“分治”也就是“分而治之”的意思,就是把一個復雜的問題分成兩個或多個相同或類似的子問題,直到子問題可以簡單直接地解決,原問題的解即為子問題的合并。

分治算法一般是用遞歸來實現的,具體的分治算法可以按照下面三個步驟來解決問題:

  • 分解: 將原問題分解為若干個規模較小,相對獨立,與原問題形式相同的子問題。
  • 解決: 若子問題規模較小且易于解決時,則直接解。否則,遞歸地解決各子問題。
  • 合并: 將各子問題的解合并為原問題的解。
  • 2.算法介紹

    Ⅰ.歸并排序(Merge Sort)

    該算法是利用分治思想解決問題的一個非常典型的應用,歸并排序的基本思路就是先把數組一分為二,然后分別把左右數組排好序,再將排好序的左右兩個數組合并成一個新的數組,最后整個數組就是有序的了。

    運用遞歸法歸并排序的主要步驟: 1. 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合并后的序列 2. 設定兩個指針,最初位置分別為兩個已經排序序列的起始位置 比較兩個指針所指向的元素,選擇相對小的元素放入到合并空間,并移動指針到下一位置 3. 重復步驟3直到某一指針到達序列尾 將另一序列剩下的所有元素直接復制到合并序列尾
    • 歸并算法圖解如下:

    • 歸并排序算法實現代碼:
    #include<iostream> #include<vector>using namespace std;void Merge(vector<int>& , int , int , int ); void MergeSort(vector<int>& , int , int );int main() {vector<int> test = { 3, 7, 6, 4, 5, 1, 2, 8 };MergeSort(test,0,test.size()-1);for (auto x : test)cout << x << " ";return 0; }void Merge(vector<int>& arr, int left, int mid, int right) {int i = left;int j = mid + 1;int k = 0;vector<int> temp(right - left + 1);while (i <= mid && j <= right)temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];while (i <= mid)temp[k++] = arr[i++];while (j <= right)temp[k++] = arr[j++];for (int m = 0; m < temp.size(); m++)arr[left + m] = temp[m]; }void MergeSort(vector<int>& arr,int left, int right) {if (left >= right) return;int mid = left + (right - left) / 2;MergeSort(arr, left, mid);MergeSort(arr, mid + 1, right);Merge(arr, left, mid, right); }

    代碼分析:

    歸并排序中需要用到兩個函數,一個是MergeSort函數,一個是Merge函數。MergeSort函數的作用是把數組中left至right的元素全部排列好。而Merge函數的作用是把左右兩個已經排序好的數組合并成一個數組。

    • Merge函數的編寫非常重要,我們需要創建一個新的數組temp,數組大小為right-left+1.然后定義兩個下標i和j, 其中i=left, j=mid+1,i表示第一個數組的起始位置,j表示第二個數組的起始位置。同時還需要一個下標k來標記temp數組中填入元素的位置。

    開始遍歷兩個數組,比較i和j所指元素的大小,將較小者放入temp數組中,同時腳標和k向后移動。當其中一個子數組循環完后,將剩下數組中的元素依次放入temp數組中。

    最終,將temp數組拷貝回原數組array,返回的數組就是經過歸并排序好的數組了。

    • MeergeSort函數主要是用于遞歸調用。當right>=left時,就直接return。否則,找到數組的中間下標,將數組一分為二,分別兩邊兩邊數組進行歸并排序,最后將兩個數組用Merge函數合并起來。

    算法分析:

    • 歸并排序的時間復雜度?

    歸并排序的遞推公式為T(n)=2*T(n/2)+n

    該遞歸式表明,對n個元素遞歸排序所需時間復雜度,等于左右子區間n/2個元素分別遞歸排序的時間,加上將兩個已排好的子區間合并起來的時間O(n)

    當遞歸循環至最后一層時,即n=1時,T(1)=1,于是可以推導出歸并排序的時間復雜度為O(nlongn)

    • 歸并排序是原地排序嗎?

    從原理中可以看出,在歸并排序過程中我們需要分配臨時數組temp,所以不是原地排序算法,空間復雜度為O(n).

    • 歸并排序是穩定的排序算法嗎?

    當我們遇到左右數組中的元素相同時,我們可以先把左邊的元素放入temp數組中,再放入右邊數組的元素,這樣就保證了相同元素的前后順序不發生改變。所以,歸并排序是一個穩定的排序算法。

    Ⅱ.快速排序(Quicksort)

    快速排序,也就是我們常說的“快排”。其實,快排也是利用的分治思想。它具體的做法是在數組中取一個基準pivot,pivot位置可以隨機選擇(一般我們選擇數組中的最后一個元素)。選擇完pivot之后,將小于pivot的所有元素放在pivot左邊,將大于pivot的所有元素放在右邊。最終,pivot左側元素都將小于右側元素。接下來我們依次把左側的子數組和右側子數組進行快速排序。如果左右兩側的數組都是有序的話,那么我們的整個數組就處于有序的狀態了。

    快速排序的主要步驟為: 1. 挑選基準值:從數組中挑出一個元素,稱為“基準”(pivot) 2. 分割:重新排序數組,所有比pivot小的元素擺放在pivot前面,所有比pivot值大的元素擺在pivot后面(與pivot值相等的數可以到任何一邊)。 3. 遞歸排序子數組:遞歸地將小于pivot元素的子序列和大于pivot元素的子序列排序。 4. 遞歸到最底部的判斷條件是數列的大小是零或一,此時該數列顯然已經有序。
    • 快速排序圖解如下:

    • 快速排序實現代碼
    #include<iostream> #include<vector>using namespace std;int partition(vector<int>& , int , int ); void QuickSort(vector<int>& , int , int );int main() {vector<int> test = { 3, 7, 6, 4, 5, 1, 2, 8 };QuickSort(test,0,test.size()-1);for (auto x : test)cout << x << " ";return 0; }int partition(vector<int>& arr, int left, int right) {int pivot = right;int location = left;for (int i = left; i < right; i++) {if (arr[i] < arr[pivot]) {int temp = arr[i]; arr[i] = arr[location]; arr[location] = temp;location++;}}int temp = arr[pivot]; arr[pivot] = arr[location]; arr[location] = temp;return location; }void QuickSort(vector<int>& arr,int left, int right) {if (left >= right) return;int pivot = partition(arr, left, right);QuickSort(arr, left, pivot-1);QuickSort(arr, pivot + 1, right); }

    代碼分析

    快速排序算法中有兩個函數,QuickSort函數和partition函數。partition函數的作用返回pivot下標,意思是此時,所有在pivot左側的元素都比pivot的值小,在右側的值比pivot大。接下來對左右兩側的數組遞歸調用QuickSort函數進行快排。

    我們每次指定pivot指向最后一個元素,同時定義一個變量location,用來標記pivot最后應該置于的位置。在location左側的所有元素都是比pivot值小的,從location開始,右側所有元素都比pivot大。

    只要遍歷到的元素比pivot的值小,就與location所指的元素進行交換,同時location加一,更新pivot應該在的位置。

    數組遍歷結束,最后元素pivot與location所指元素進行交換,這樣,pivot左側的元素就全部比pivot小,右側元素全部比pivot大了。

    算法分析:

    • 快速排序的時間復雜度?

    快排的時間復雜度也可以像歸并排序那樣用遞推公式計算出來。如果每次分區都剛好把數組分成兩個大小一樣的區間,那么它的時間復雜度也為O(nlogn).但是如果遇到最壞情況下,該算法可能退化成O(n^2).

    • 快速排序是原地排序嗎?

    根據原理可以知道,快速排序沒有額外的內存消耗,故是一種原地排序算法。

    • 快速排序是穩定的排序算法嗎?

    因為分區操作涉及元素之間的交換,例如下圖,當遍歷到第一個小于2的元素1時,會交換1與前面的3,因此兩個相等3的順序就發生了改變。所以快速排序不是一個穩定的排序算法。

    3.總結回顧

    本文介紹的兩個排序算法歸并排序和快速排序的平均時間復雜度都為O(nlogn),但是他們的思路有所不同。

    歸并排序:先排序左右子數組,然后合并兩個子數組成有序數組。

    快速排序:先通過pivot找出左右子數組,然后對左右子數組遞歸調用快速排序。

    下圖為本文總結

    未完待續......

    總結

    以上是生活随笔為你收集整理的数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。