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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

归并排序java代码实现

發布時間:2023/12/9 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 归并排序java代码实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

歸并排序,是一種分治算法。利用遞歸,將一個大的數據集合分解成小的子集合。將子集合排好序后,再合并起來。歸并排序不是原地排序算法,因為它使用到了臨時空間,這也是歸并排序沒有快速排序應用廣泛的主要原因,雖然歸并排序的時間復雜度,最好、最壞都是O(logn)。但是,這個也看使用場景,如果在空間換時間的場合,個人認為這種算法也有一定的用武之處。
我在網上找了一個動圖,很直觀。大家可以看一下。來源:https://www.cnblogs.com/fivestudy/p/10064969.html

下面看一下java的代碼實現

public static void main(String[] args) {int[] arr = new int[]{10, 7, 8, 9, 1, 5};mergeSort(arr, arr.length);System.out.println(Arrays.toString(arr));}private static void mergeSort(int[] arr, int n) {mergeSortInternally(arr, 0, n - 1);}private static void mergeSortInternally(int[] arr, int p, int r) {if (p >= r) {return;}//find middle pointint q = p + (r - p) / 2;//遞歸分解數組元素下標,處理前半部分mergeSortInternally(arr, p, q);//處理后半部分mergeSortInternally(arr, q + 1, r);mergeBySentry(arr, p, q, r);}/*** 普通的合并算法*/private static void merge(int[] arr, int p, int q, int r) {//左半個數組的開始下標int i = p;//右半個數組的開始下標int j = q + 1;//臨時數組的起始下標int k = 0;//初始化一個和當前分裂好的數組相同大小的臨時數組int[] temp = new int[r - p + 1];while (i <= q && j <= r) {//比較左右兩個數組的起始值,較小的元素放在臨時數組的第一個位置if (arr[i] <= arr[j]) {temp[k++] = arr[i++];} else {temp[k++] = arr[j++];}}//分裂好的兩個數組,很可能不是平均分配,所以可能會有一個數組先遍歷完成,//另外一個數組還有未進行比較數據,此時直接將未進行比較的數組的數據添加到臨時數組即可//初始化下標,先將下標初始化為左半部分的數組int start = i;int end = q;//說明右半部分未比較完,此時將下標再重置為右半部分的數組if (j <= r) {start = j;end = r;}//將未比較完的有序數組直接添加到臨時數組中while (start <= end) {temp[k++] = arr[start++];}//將臨時數組的數據拷貝到原數組中for (i = 0; i <= r - p; i++) {arr[p + i] = temp[i];}}/*** 添加了哨兵節點的合并算法** @param arr* @param p* @param q* @param r*/private static void mergeBySentry(int[] arr, int p, int q, int r) {//初始化左邊數組對應的臨時空間,增加一個哨兵節點的位置int[] leftArr = new int[q - p + 2];//初始化右邊數組對應的臨時空間,增加一個哨兵節點的位置int[] rightArr = new int[r - q + 1];//將原數組中,左邊的數據拷貝到臨時數組中for (int i = 0; i <= q - p; i++) {leftArr[i] = arr[p + i];}//左邊數組增加哨兵節點leftArr[q - p + 1] = Integer.MAX_VALUE;//將原數組中,右邊的數據拷貝到臨時數組中for (int i = 0; i < r - q; i++) {rightArr[i] = arr[q + 1 + i];}//右邊數組增加哨兵節點rightArr[r - q] = Integer.MAX_VALUE;int i = 0;int j = 0;int k = p;while (k <= r) {//左節點小于右節點時,將排好序的臨時空間數據加入到原數組中,當i到達哨兵節點時,i不再增加,只增加j即可.if (leftArr[i] <= rightArr[j]) {arr[k++] = leftArr[i++];} else {arr[k++] = rightArr[j++];}}}

這里面的關鍵點是合并函數的實現邏輯,我貼出了2種合并函數的實現。
一種是:原始的合并實現,方法名是:merge
另一種是改良版本,增加了哨兵節點,方法名是:mergeBySentry。
改良版本,明顯更容易理解一點。在臨界值的情況,大家注意使用哨兵節點,可以讓代碼邏輯更清晰。

上一篇快速排序java實現,我們聊了一下快速排序的實邏輯,這2種排序經常會在一起進行比較,大家覺著這2種實現邏輯有什么區別嗎?最主要的區別是:
歸并排序是先將大問題拆解成小問題,然后處理小問題,最后將小問題合并。
快速排序是先處理小的排序區間,然后慢慢處理大問題。
兩種排序原理截然不同

(歸并排序的代碼主要來自于極客時間<數據結構與算法之美>的專欄,大家對算法感興趣的,可以訂閱一下這個專欄,專欄質量很高)

時間復雜度分析
極客的專欄里分析了這個過程,我直接抄過來,給大家看一下如何進行分析。想看全文的,可以移步專欄。
我們假設對 n 個元素進行歸并排序需要的時間是 T(n),那分解成兩個子數組排序的時間都是 T(n/2)。我們知道,merge() 函數合并兩個有序子數組的時間復雜度是 O(n)。所以,套用前面的公式,歸并排序的時間復雜度的計算公式就是:

T(1) = C; n=1時,只需要常量級的執行時間,所以表示為CT(n) = 2*T(n/2) + n; n>1

通過這個公式,如何來求解 T(n) 呢?還不夠直觀?那我們再進一步分解一下計算過程:

T(n) = 2*T(n/2) + n= 2*(2*T(n/4) + n/2) + n = 4*T(n/4) + 2*n= 4*(2*T(n/8) + n/4) + 2*n = 8*T(n/8) + 3*n= 8*(2*T(n/16) + n/8) + 3*n = 16*T(n/16) + 4*n......= 2^k * T(n/2^k) + k * n......

通過這樣一步一步分解推導,我們可以得到 T(n) = 2kT(n/2k)+kn。當 T(n/2^k)=T(1) 時,也就是 n/2^k=1,我們得到 k=log2n 。我們將 k 值代入上面的公式,得到 T(n)=Cn+nlog2n 。如果我們用大 O 標記法來表示的話,T(n) 就等于 O(nlogn)。所以歸并排序的時間復雜度是 O(nlogn)
空間復雜度分析
遞歸代碼的空間復雜度并不能像時間復雜度那樣累加。原因是,盡管每次合并操作都需要申請額外的內存空間,但在合并完成之后,臨時開辟的內存空間就被釋放掉了。在任意時刻,CPU 只會有一個函數在執行,也就只會有一個臨時的內存空間在使用。臨時內存空間最大也不會超過 n 個數據的大小,所以空間復雜度是 O(n)。
原地排序算法分析
很明顯,歸并排序的空間復雜度不是O(1),所以不是原地排序算法。
穩定排序算法分析
歸并排序穩不穩定,關鍵要看merge函數。我們每次都讓a[p,q]的元素都先進入臨時數組,a[q+1,r]的元素后進入臨時數組,最后的結果就是穩定的。所以,歸并排序就是穩定排序算法。

總結

以上是生活随笔為你收集整理的归并排序java代码实现的全部內容,希望文章能夠幫你解決所遇到的問題。

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