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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

入秋的第一篇数据结构算法:看看归并与快排的风采,三面蚂蚁金服成功拿到offer

發布時間:2023/12/16 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 入秋的第一篇数据结构算法:看看归并与快排的风采,三面蚂蚁金服成功拿到offer 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

package com.liuxing.sort;

import com.liuxing.util.Print;

/**

  • @author liuxing007

  • @ClassName MergeSort

  • @Description 歸并排序(Merge Sort)

  • 歸并排序的核心思想還是蠻簡單的。如果要排序一個數組,

  • 我們先把數組從中間分成前后兩部分,然后對前后兩部分分別排序,

  • 再將排好序的兩部分合并在一起,這樣整個數組就都有序了。

  • 時間復雜度:O(nlogn)

  • 空間復雜度:O(n)

  • 不是原地排序算法

  • @date 2020/9/17 15:04

*/

public class MergeSort {

public static void main(String[] args) {

int[] arr = new int[]{6,4,1,7,2,5,8,3};

int length = arr.length;

System.out.println(“排序前數組===========”);

Print.print(arr, length);

sort(arr, length);

System.out.println(“排序后數組===========”);

Print.print(arr, length);

}

/**

  • 排序算法

  • @param arr 數組

  • @param l 數組長度

*/

private static void sort(int[] arr,int l){

sortMerge(arr,0,l-1);

}

/**

  • 遞歸

  • @param arr 數組

  • @param p 開始位置下表

  • @param r 結束位置下表

*/

private static void sortMerge(int[] arr,int p,int r){

if(p >= r){

return ;

}

//分治的下標,這里我采用p到r的中間位置index。

int index = p + (r-p)/2;

//左側遞歸

sortMerge(arr,p,index);

//右側遞歸

sortMerge(arr, index + 1, r);

merge(arr, p, index,r);

System.out.println(“排序后數組===========”);

Print.print(arr, length);

}

/**

  • 合并計算

  • @param arr 原素組

  • @param l 左側數組開始位置下標

  • @param index 左側數組結束位置下標

  • @param r 右側數組結束位置下標

*/

private static void merge(int[] arr, int l,int index, int r) {

//臨時數組,這里可以優化,數組的頻繁創建會降低程序運行的效率,

// 所以這里可以將這個臨時數組改成參數傳遞進來,在數量較大的時候執行效率變化變焦顯著

int[] temp = new int[r-l+1];

//左側開始下標

int i= l;

//右側開始下標

int j = index+1;

//臨時數組下標

int k=0;

// 左側數組與右側數組進行對比,將小的元素放入臨時數組中

while(i<=index && j<=r){

if(arr[i]<arr[j]){

temp[k++] = arr[i++];

}else{

temp[k++] = arr[j++];

}

}

//對比完成之后,需要把兩側數組中還沒有對比的數據加入到臨時數組中

//把左邊剩余元素加入臨時數組中

while(i<=index){

temp[k++] = arr[i++];

}

//把右邊剩余元素加入臨時數組中

while(j<=r){

temp[k++] = arr[j++];

}

//將臨時數組的元素拷貝原數組中

for(int x=0;x<temp.length;x++){

arr[x+l] = temp[x];

}

}

}

package com.liuxing.util;

/**

  • @author liuxing007

  • @ClassName Print

  • @Description 打印

  • @date 2020/9/17 11:13

*/

public class Print {

/***

  • 打印數據

  • @param arr 數組

  • @param length 數組長度

*/

public static void print(int[] arr, int length) {

for (int i = 0; i < length; ++i) {

System.out.print(arr[i] + " ");

}

System.out.println("");

}

}

排序前數組===========

6 4 1 7 2 5 8 3

合并后的數據

4 6 1 7 2 5 8 3

合并后的數據

4 6 1 7 2 5 8

《一線大廠Java面試題解析+后端開發學習筆記+最新架構講解視頻+實戰項目源碼講義》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整內容開源分享

3

合并后的數據

1 4 6 7 2 5 8 3

合并后的數據

1 4 6 7 2 5 8 3

合并后的數據

1 4 6 7 2 5 3 8

合并后的數據

1 4 6 7 2 3 5 8

合并后的數據

1 2 3 4 5 6 7 8

排序后數組===========

1 2 3 4 5 6 7 8

Process finished with exit code 0


3.時間空間復雜度分析


我們假設對 n 個元素進行歸并排序需要的時間是 T(n),那分解成兩個子數組排序的時間都是 T(n/2)。我們知道,merge() 函數合并兩個有序子數組的時間復雜度是 O(n)。所以,套用前面的公式,歸并排序的時間復雜度的計算公式就是:

T(1) = C; n=1時,只需要常量級的執行時間,所以表示為C。

T(n) = 2*T(n/2) + n; n>1

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

T(n) = 2*T(n/2) + n

= 2*(2T(n/4) + n/2) + n = 4T(n/4) + 2*n

= 4*(2T(n/8) + n/4) + 2n = 8T(n/8) + 3n

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

= 2^k * T(n/2^k) + k * n

通過這樣一步一步分解推導,我們可以得到 T(n) = 2 k 2^k 2k T(n/ 2 k 2^k 2k)+kn。當 T( n / 2 k n/2^k n/2k)=T(1) 時,也就是 2 k 2^k 2k=1,我們得到 k= l o g 2 n log_2n log2?n 。我們將 k 值代入上面的公式,得到 T(n)=Cn+ l o g 2 n log_2n log2?n 。如果我們用大 O 標記法來表示的話,T(n) 就等于 O(nlogn)。所以歸并排序的時間復雜度是 O(nlogn)。從我們的原理分析和偽代碼可以看出,歸并排序的執行效率與要排序的原始數組的有序程度無關,所以其時間復雜度是非常穩定的,不管是最好情況、最壞情況,還是平均情況,時間復雜度都是 O(nlogn)-----摘自-極客時間-數據結構與算法-王爭

歸并排序的時間復雜度已經很優秀了,但為什么我們在日常開發中卻很少看到他的身影呢?我們先來分析一下歸并排序的空間復雜度。

我們需要注意的是合并方法,這個方法中我們使用了一個臨時數組用來存儲數據,但是合并之后這個臨時數組就會釋放,又因為臨時數組的最大長度不會超過原始數組長度n,所以歸并排序的空間復雜度為:O(n)

為什么開發中很少人使用到歸并排序呢?原因很簡單,因為它不是一個原地排序算法,這個時候你可能會有疑惑了,什么是原地排序算法?簡單來說:不通過其他空間來完成的排序,我們稱它為原地排序算法,但歸并排序很明顯借用了一個臨時數組,所以它不是一個原地排序算法,即使它的時間復雜都很穩定,使用的人也比較少。


三、快速排序

=====================================================================

1.什么是快速排序


如果要排序數組中下標從 p 到 r 之間的一組數據, 我們選擇 p 到 r 之間的任意一個數據作為 pivot(分區點)。我們遍歷 p 到 r 之間的數據,將小于 pivot 的放到左邊, 將大于 pivot 的放到右邊,將 pivot 放到中間。經過這一步驟之后, 數組 p 到 r 之間的數據就被分成了三個部分,前面 p 到 q-1 之間都是小于 pivot 的,中間是 pivot,后面的 q+1 到 r 之間是大于 pivot 的.根據分治、遞歸的處理思想,我們可以用遞歸排序下標從 p 到 q-1 之間的數據和下標從 q+1 到 r 之間的數據,直到區間縮小為 1,就說明所有的數據都有序了。(摘自-極客時間-數據結構與算法-王爭)

快速排序的思想和歸并排序有點類似,都是通過分治的思想,利用遞歸實現排序,只不過實現的細節有所不同,快排(快速排序)需要一個分區點,可以在數組中隨便去一個元素作為分區點即可,后面就是前面講到的概念了,歸并的核心在于合并,而快排的核心在于分區點,所以我們就一起來看看在獲取分區點的時候快排都干了些啥?

之前說過,快排選擇一個分區點(pivot)之后,將小于分區點(pivot)的元素放左邊,分區點(pivot)放中間,大于分區點(pivot)的放右邊,這個一看就很好解決嘛,和歸并排序一樣,我先申請兩個臨時數組,一個存放小于分區點元素的數組,一個存放大于分區點元素的數組,這樣,就能完美的解決了,非常簡單,但是這個就和歸并排序面臨這同一樣的一個問題:它不是一個原地排序算法,那如果我希望快排是一個原地排序算法呢?我們應該如何實現呢?其實也不難,我們可以參考一下選擇排序:【數據結構與算法】常見的三種排序(冒泡排序、插入排序、選擇排序)

我們定義一個游標 i (數組下標) 把數組a[p-(r-1)]分成兩部分,A[p-(i-1)]都是小于分區點(pivot)的,我們叫他“已排序區間”。a[(i+1) - (r-1)]都是大于分區點(pivot)元素的,我們叫他“未排序區間”,只要從未排序區間去值與分區點(pivot)進行比較,如果小于分區點(pivot),那么將此元素追加到已排序區間中(a[i]),否者不需要變動。

我還是準備了一張圖給大家參考,也許大家就能明白了。

這是一次分區交換的結果,當把所有分區都交換完成之后,整個數組也就有序了,既然快速排序的思想已經講的差不多了,下面我們一起來看看代碼怎么實現

快排代碼實現

package com.liuxing.sort;

import com.liuxing.util.DataUtil;

import com.liuxing.util.Print;

/**

  • @author liuxing007

  • @ClassName Quicksort

  • @Description 快速排序

  • 如果要排序數組中下標從 p 到 r 之間的一組數據,

  • 我們選擇 p 到 r 之間的任意一個數據作為 pivot(分區點)。

  • 我們遍歷 p 到 r 之間的數據,將小于 pivot 的放到左邊,

  • 將大于 pivot 的放到右邊,將 pivot 放到中間。經過這一步驟之后,

  • 數組 p 到 r 之間的數據就被分成了三個部分,

  • 前面 p 到 q-1 之間都是小于 pivot 的,中間是 pivot,

  • 后面的 q+1 到 r 之間是大于 pivot 的.

  • 根據分治、遞歸的處理思想,

  • 我們可以用遞歸排序下標從 p 到 q-1 之間的數據和下標從 q+1 到 r 之間的數據,

  • 直到區間縮小為 1,就說明所有的數據都有序了(摘自-極客時間-數據結構與算法-王爭)

  • 時間復雜度:O(nlogn)

  • 空間復雜度:O(1)

  • 原地排序算法,但不是穩點排序算法

  • @date 2020/9/18 10:22

*/

public class Quicksort {

public static void main(String[] args) {

int[] arr = new int[]{6, 5, 4, 3, 2, 1};

// int[] arr = DataUtil.createIntArrData();

int length = arr.length;

System.out.println(“排序前數組===========”);

Print.print(arr, length);

sort(arr, length);

System.out.println(“排序后數組===========”);

Print.print(arr, length);

}

/**

  • 排序算法

  • @param arr 數組

  • @param l 數組長度

*/

private static void sort(int[] arr,int l){

sortRec(arr,0,l-1);

}

/**

  • 遞歸

  • @param arr

  • @param p

  • @param r

*/

private static void sortRec(int[] arr, int p, int r) {

//遞歸終止條件

if (p >= r){

return;

}

//獲取分區點

int q = partition(arr, p, r);

總結

以上是生活随笔為你收集整理的入秋的第一篇数据结构算法:看看归并与快排的风采,三面蚂蚁金服成功拿到offer的全部內容,希望文章能夠幫你解決所遇到的問題。

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