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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

排序算法 | 快速排序算法原理及实现和优化(一)

發布時間:2023/12/31 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 排序算法 | 快速排序算法原理及实现和优化(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

快速排序是對冒泡排序的一種改進,由 C.A.R.Hoare(Charles Antony Richard Hoare,東尼·霍爾)在 1962 年提出。

快速排序的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據比另一部分的所有數據要小,再按這種方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,使整個數據變成有序序列。

快速排序的原理


排序算法的思想非常簡單,在待排序的數列中,我們首先要找一個數字作為基準數(這只是個專用名詞)。為了方便,我們一般選擇第 1 個數字作為基準數(其實選擇第幾個并沒有關系)。接下來我們需要把這個待排序的數列中小于基準數的元素移動到待排序的數列的左邊,把大于基準數的元素移動到待排序的數列的右邊。這時,左右兩個分區的元素就相對有序了;接著把兩個分區的元素分別按照上面兩種方法繼續對每個分區找出基準數,然后移動,直到各個分區只有一個數時為止。

這是典型的分治思想,即分治法。下面我們對一個實際例子進行算法描述,講解快速排序的排序步驟。

以 47、29、71、99、78、19、24、47 的待排序的數列為例進行排序,為了方便區分兩個 47,我們對后面的 47 增加一個下畫線,即待排序的數列為 47、29、71、99、78、19、24、47。

首先我們需要在數列中選擇一個基準數,我們一般會選擇中間的一個數或者頭尾的數,這里直接選擇第 1 個數 47 作為基準數,接著把比 47 小的數字移動到左邊,把比 47 大的數字移動到右邊,對于相等的數字不做移動。所以實際上我們需要找到中間的某個位置 k,這樣 k 左邊的值全部比 k 上的值小,k 右邊的值全部比 k 上的值大。

接下來開始移動元素。怎么移動呢?其實冒泡排序也涉及對元素的移動,但是那樣移動起來很累,比如把最后一個元素移動到第 1 個,就需要比較 n-1 次,同時交換 n-1 次,效率很低。其實,只需把第 1 個元素和最后一個元素交換就好了,這種思想是不是在排序時可以借鑒呢?之前說快速排序就是對冒泡排序的一個改進,就是這個原因。

快速排序的操作是這樣的:首先從數列的右邊開始往左邊找,我們設這個下標為 i,也就是進行減減操作(i–),找到第 1 個比基準數小的值,讓它與基準值交換;接著從左邊開始往右邊找,設這個下標為 j,然后執行加加操作(j++),找到第 1 個比基準數大的值,讓它與基準值交換;然后繼續尋找,直到 i 與 j 相遇時結束,最后基準值所在的位置即 k 的位置,也就是說 k 左邊的值均比 k 上的值小,而 k 右邊的值都比 k 上的值大。

所以對于上面的數列 47、29、71、99、78、19、24、47,進行第 1 趟第 1 個交換的排序情況如下,第 1 次的操作情況如圖 1 所示。

圖 1 第 1 次發現可以交換的數

交換之后,i 移動到了下標為 6 的位置,對 j 繼續掃描,如圖 2 所示。

圖 2 第 2 次發現可交換的值

此時交換后的數列變為 24、29、47、99、78、19、71、47。接下來我們繼續對 i、j 進行操作,如圖 3 所示,繼續進行 i-- 及 j++ 的比較操作。

圖 3 繼續進行 i 與 j 的移動

進行了這兩次 i、j 的移動、比較、交換之后,我們最終得到的數列是 24、29、19、47、78、99、71、47。接下來我們繼續進行 i-- 的操作,發現在 i 為 4 時比 47 大不用交換,在 i 為 3 時與 j 相遇,這時就不需要繼續移動、比較了,已經找到 k 了,并且 k 的值為 3。我們可以確認一下當前的數列是不是 k 左邊的值都比 47 小,而 k 右邊的值都比 47 大(由于要保持相對位置不變,所以 47 同樣在基準值 47 的右邊)。

47 這個值已經落到了它該在的位置,第 1 趟排序完成了。接下來就是以 k 為基準,分為兩部分,然后在左右兩部分分別執行上述排序操作,最后數據會分為 4 部分;接著對每部分進行操作,直到每部分都只有一個值為止。

接下來進行第 2 趟排序,現在左邊部分為 24、29、19,我們選擇第 1 個數 24 作為基準數,接著進行 i–、j++ 的操作,我們發現 i 最初的值為 19,比 24 這個基準值小,所以與基準值進行交換,得到的數列為 19、29、24;當 j 為 1 時,我們發現 29 比 24 大,所以與基準值進行交換,得到的數列 19、24、29,此時 i 為 2,j 為 1;繼續 i-- 時發現 i 為 1,與 j 相遇,左邊部分的數列的 k 為 1,并且左右兩部分分別只有一個元素,此時第 2 輪排序的左邊部分的排序結束,同時左邊部分的所有數據都排序完成。

我們接著看右邊部分的排序,待排序的數列為 78、99、71、47,我們同樣選擇第 1 個值 78 為基準值,接下來進行 i 與 j 的移動與比較,發現 47 比 78 小,進行交換,得到的數列 47、99、71、78;從左往右發現 99 比基準值 78 大,進行交換,得到的數列為 47、78、71、99;繼續從右向左看,發現 71 比基準值 78 小,進行交換,得到的數列為 47、71、78、99。此時 i 在整體數組中的下標為 6,j 為 5,若繼續 j++ 則與 i 相遇,所以完成此輪排序。

此時右邊數列的 k 為 6,一般會是相遇的位置,也就是基準值所在的位置,這時數列又被分為兩部分,左邊是 47、71,右邊是 99,需要繼續對左邊部分的數據進行排序,雖然只有兩個數據,但我們還是繼續按照快速排序的思想操作一下,選擇 47 作為基準數,將i進行從右向左的移動、比較,發現 i 與 j 相等時沒有產生移動,完成第 2 輪排序。

至此,所有排序都已經完成,最終數列的結果是 19、24、29、47、47、71、78、99,怎么樣,快速排序是不是非常簡單地完成了所有的排序呢?雖然本次快速排序沒有改變相同值的元素的順序,但是由于快速排序需要對數列中的元素來回移動,有時還是會改變相對順序的,所以快速排序并不是一個穩定的算法。

快速排序的實現


通過以上的學習,你是否可以自己寫出快速排序的實現代碼呢?在接著學習之前,最好自己能對代碼的實現進行一些思考,然后和下面的內容進行比對,看看自己有哪些疏忽之處。

其實快速排序有一個比較簡單的思想,就是遞歸。對于每一趟排序都是一樣的思想,只不過需要進行排序的數組的范圍越來越小了,使用遞歸實現這種排序最適合不過了。

public class QuickSort {public static void main(String[] args) {int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序數組sort(array, 0, array.length - 1);print(array);}/*** 快速排序** @param array 待排序的數組* @param low 數組的起始地址* @param high 數組的結束地址*/public static void sort(int array[], int low, int high) {// 直到兩個下標相遇,程序結束if (low < high) {// 查找 k 的位置int k = partition(array, low, high);// 對 k 左側的子表進行排序sort(array, low, k - 1);// 對 k 右側的子表進行排序sort(array, k + 1, high);}}/*** 快速排序,分割的過程** @param array 待排序的數組* @param low 數組的起始地址* @param high 數組的結束地址* @return k 值*/public static int partition(int array[], int low, int high) {// 基準點int point = array[low];// 直到兩個下標相遇,程序結束while (low < high) {// high 向左移動,直至遇到比point值小的記錄,停止移動while (low < high && array[high] >= point) {high--;}// 交換兩個元素的位置swap(array, low, high);//low 向右移動,直至遇到比point值大的記錄,停止移動while (low < high && array[low] <= point) {low++;}// 交換兩個元素的位置swap(array, low, high);}return low;}/*** 交換數組中兩個元素的位置*/public static void swap(int array[], int low, int high) {int temp = array[low];array[low] = array[high];array[high] = temp;}/*** 打印數組*/public static void print(int array[]) {for (int i = 0; i < array.length; i++) {System.out.print(array[i] + " ");}System.out.println();} }

快速排序的特點及性能


快速排序是在冒泡排序的基礎上改進而來的,冒泡排序每次只能交換相鄰的兩個元素,而快速排序是跳躍式的交換,交換的距離很大,因此總的比較和交換次數少了很多,速度也快了不少。

但是快速排序在最壞情況下的時間復雜度和冒泡排序一樣,是 O(n2),實際上每次比較都需要交換,但是這種情況并不常見。我們可以思考一下如果每次比較都需要交換,那么數列的平均時間復雜度是 O(nlogn),事實上在大多數時候,排序的速度要快于這個平均時間復雜度。這種算法實際上是一種分治法思想,也就是分而治之,把問題分為一個個的小部分來分別解決,再把結果組合起來。

快速排序只是使用數組原本的空間進行排序,所以所占用的空間應該是常量級的,但是由于每次劃分之后是遞歸調用,所以遞歸調用在運行的過程中會消耗一定的空間,在一般情況下的空間復雜度為 O(logn),在最差的情況下,若每次只完成了一個元素,那么空間復雜度為 O(n)。所以我們一般認為快速排序的空間復雜度為 O(logn)。

快速排序是一個不穩定的算法,在經過排序之后,可能會對相同值的元素的相對位置造成改變。

快速排序基本上被認為是相同數量級的所有排序算法中,平均性能最好的。

總結

以上是生活随笔為你收集整理的排序算法 | 快速排序算法原理及实现和优化(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

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