[數據結構]排序 --2

目錄

8、快速排序

8.1、Hoare版

8.2、挖坑法

8.3、前后指針法

9、快速排序優化

9.1、三數取中法

9.2、采用插入排序

10、快速排序非遞歸

11、歸并排序

12、歸并排序非遞歸

13、排序類算法總結

14、計數排序

15、其他排序

15.1、基數排序

15.2、桶排序


8、快速排序

快速排序是Hoare于1962年提出的一種二叉樹結構的交換排序方法

基本思想:任取待排序元素序列中的某元 素作為基準值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小于基準值,右子序列中所有 元素均大于基準值,然后最左右子序列重復該過程,直到所有元素都排列在相應位置上為止

8.1、Hoare版

1. 把第一個值作為基準值 pivot

2. right 從右邊走,遇到比 pivot 大的就停下;left 從左邊走,遇到比 pivot 小的就停下

3. 交換 left 和 right 的值

4.?left 和 right 繼續走,直到 left 和?right 相遇

5.?相遇的位置就是要找的位置,把基準值與該位置交換

    public static void quickSort(int[] array) {quick(array,0,array.length-1);}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partitionHoare(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}private static int partitionHoare(int[] array, int left, int right) {int tmp = array[left];int pivot = left;while (left < right) {// 單獨的循環 不能一直減到超過最左邊的邊界while (left < right && array[right] >= tmp) {right--;}while (left < right && array[left] <= tmp) {left++;}swap(array,left,right);}swap(array,pivot,left);return left;}

兩個問題:

1. 為什么 array[right] >= tmp 必須帶等于號

????????可能會出現 left 和 right 無限交換的死循環

2. 為什么從 right 先走而不是 left

????????如果 left 先走可能會出現相遇的是比基準大的數據,最后把大的數據放到了最前面

快速排序總結:

1. 快速排序整體的綜合性能和使用場景都是比較好的,所以才敢叫快速排序

2. 時間復雜度:O(N*logN)

????????最好的情況下:O(N*logN)

????????最壞情況下:O(N^2)? -- 逆序/有序

3. 空間復雜度:O(logN)? -- 遞歸了logN層

????????最好的情況下:O(logN)

????????最壞情況下:O(N)? ?-- 逆序/有序

4. 穩定性:不穩定

8.2、挖坑法

1. 把第一個值拿出來作為基準值 tmp,第一個值的位置就是第一個坑

2. right 從右邊走,遇到比 pivot 大的就停下,然后把這個值放到上一個坑里,right 就形成了新的坑

3. left 從左邊走,遇到比 tmp 小的就停下,然后把這個值放到坑里,left?就是新的坑

4.?循環2、3,直到 left 和?right 相遇

5.?相遇的位置就是要找的位置,把基準值放在這個位置

    public static void quickSort(int[] array) {quick(array,0,array.length-1);}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partitionHole(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}private static int partitionHole(int[] array, int left, int right) {int tmp = array[left];while (left < right) {// 單獨的循環 不能一直減到超過最左邊的邊界while (left < right && array[right] >= tmp) {right--;}array[left] = array[right];while (left < right && array[left] <= tmp) {left++;}array[right] = array[left];}array[left] = tmp;return left;}

8.3、前后指針法

1.?定義兩根指針cur和prev,初始位置如下圖

2. cur開始往后走,如果遇到比key小的值,則++prev,然后交換prev和cur指向的元素,再++cur,如果遇到比key大的值,則只++cur

3.?當cur訪問過最后一個元素后,將key的元素與prve訪問的元素交換位置。cur訪問完整個數組后的各元素位置如下圖

private static int partition(int[] array, int left, int right) {int prev = left ;int cur = left+1;while (cur <= right) {if(array[cur] < array[left] && array[++prev] != array[cur]) {swap(array,cur,prev);}cur++;}swap(array,prev,left);return prev;
}

總結:
1. Hoare
2. 挖坑法
3. 前后指針法
這3種方式 ?每次劃分之后的前后順序 有可能是不一樣的

9、快速排序優化

優化的出發點:減少遞歸的次數

9.1、三數取中法

既然有序數組或者有序數組片段會使效率下降,我們就可以讓基準值每次都取大小靠中的數,然后在進行快速排序這樣就可以避免了。但不是完全避免,只是減少了最壞情況出現的概率,最壞情況還是O(n2),但有效提升了運行效率,主要提升的部分是數組中有序的數組片段,減少了循環次數。

    // 求中位數的下標private static int middleNum(int[] array,int left,int right) {int mid = (left+right)/2;if(array[left] < array[right]) {if(array[mid] < array[left]) {return left;}else if(array[mid] > array[right]) {return right;}else {return mid;}}else {//array[left] > array[right]if(array[mid] < array[right]) {return right;}else if(array[mid] > array[left]) {return left;}else {return mid;}}}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}//1 2 3 4 5 6 7int index = middleNum(array,start,end);swap(array,index,start);//4 2 3 1 5 6 7int pivot = partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}

9.2、采用插入排序

往往一棵樹的最后兩層的結點占整棵樹的絕大多數,所以當遞歸到一定深度時,采用直接插入排序

    public static void insertSort(int[] array,int left,int right) {for (int i = left+1; i <= right; i++) {int tmp = array[i];int j = i-1;for (; j >= left ; j--) {if(array[j] > tmp) {array[j+1] = array[j];}else {break;}}array[j+1] = tmp;}}private static void quick(int[] array,int start,int end) {if(start >= end) {return;}if(end - start + 1 <= 15) {insertSort(array, start, end);return;}//1 2 3 4 5 6 7int index = middleNum(array,start,end);swap(array,index,start);//4 2 3 1 5 6 7int pivot = partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}

10、快速排序非遞歸

1. 先調用partition方法找到基準
2. 基準左邊和右邊有沒有2個及以上個元素,有就把下標放到棧中

3. 判斷棧空不空,不空出棧2個,第一個是新的end,第二個是新的start

4. 棧不為空時,循環執行上述1.2.3

    public static void quickSortNor(int[] array) {int start = 0;int end = array.length-1;Stack<Integer> stack = new Stack<>();int pivot = partition(array,start,end);if(pivot > start+1) {stack.push(start);stack.push(pivot-1);}if(pivot+1 < end) {stack.push(pivot+1);stack.push(end);}while (!stack.isEmpty()) {end = stack.pop();start = stack.pop();pivot = partition(array,start,end);if(pivot > start+1) {stack.push(start);stack.push(pivot-1);}if(pivot+1 < end) {stack.push(pivot+1);stack.push(end);}}}

11、歸并排序

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

    public static void mergeSort(int[] array) {mergeSortFun(array,0,array.length-1);}private static void mergeSortFun(int[] array,int start,int end) {if(start >= end) {return;}int mid = (start+end)/2;mergeSortFun(array,start,mid);mergeSortFun(array,mid+1,end);//合并merge(array,start,mid,end);}private static void merge(int[] array, int left, int mid, int right) {// s1,e1,s2,e2 可以不定義,這樣寫為了好理解int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;//定義一個新的數組int[] tmpArr = new int[right-left+1];int k = 0;//tmpArr數組的下標//同時滿足 證明兩個歸并段 都有數據while (s1 <= e1 && s2 <= e2) {if(array[s1] <= array[s2]) {tmpArr[k++] = array[s1++];}else {tmpArr[k++] = array[s2++];}}while (s1 <= e1) {tmpArr[k++] = array[s1++];}while (s2 <= e2) {tmpArr[k++] = array[s2++];}//把排好序的數據 拷貝回原來的數組array當中for (int i = 0; i < tmpArr.length; i++) {array[i+left] = tmpArr[i];}}

兩個有序數組合并為一個有序數組代碼:

public static int[] mergeArray(int[] arrayl,int[] array2) {// 注意判斷參數int[] tmp = new int[array1.length+array2.length];int k = 0;int s1 = 0;int el = array1.length-1;int s2 = 0;int e2 = array2.length-1;while (s1 <= el && s2 <= e2) {if(array1[s1] <= array2[s2]) {tmp[k++] = array1[s1++];}else {tmp[k++]=array2[s2++];}}while (s1 <= el) {tmp[k++] = array1[s1++];}while (s2 <= e2) {tmp[k++]= array2[s2++];}return tmp;
}

歸并排序總結
1. 歸并的缺點在于需要O(N)的空間復雜度,歸并排序更多的是解決在磁盤中的外排序問題。
2. 時間復雜度:O(N*logN)
3. 空間復雜度:O(N)

?????????遞歸調用棧空間:O(logN)

????????合并操作空間:O(N)
4. 穩定性:穩定


海量數據的排序

外部排序:排序過程需要在磁盤等外部存儲進行的排序
前提:內存只有 1G,需要排序的數據有 100G
因為內存中因為無法把所有數據全部放下,所以需要外部排序,而歸并排序是最常用的外部排序
1. 先把文件切分成 200 份,每個 512 M
2. 分別對 512 M 排序,因為內存已經可以放的下,所以任意排序方式都可以
3. 進行2路歸并,同時對 200 份有序文件做歸并過程,最終結果就有序了(在外部存儲進行)

12、歸并排序非遞歸

1. 找到 left、mid、right 的位置和關系,然后調用merge合并

2. 定義 gap 表示當前的分組是每組幾個數據

    public static void mergeSortNor(int[] array) {int gap = 1;//每組幾個數據while (gap < array.length) {for (int i = 0; i < array.length; i = i+gap*2) {int left = i;// mid、right 可能會越界int mid = left+gap-1;int right = mid+gap;if(mid >= array.length) {mid = array.length-1;}if(right >= array.length) {right = array.length-1;}merge(array,left,mid,right);}gap*=2;}

13、排序類算法總結

14、計數排序

思想:計數排序又稱為鴿巢原理,是對哈希直接定址法的變形應用。 操作步驟:
1. 統計相同元素出現次數
2. 根據統計的結果將序列回收到原來的序列中

具體實現:

1.申請一個數組count,大小為待排序數組array的范圍 M

2.遍歷待排序數組array,把數字對應的count數組的下標內容進行++

3.遍歷計數數組count 寫回到待排序數組array,此時需要注意寫的次數和元素值要一樣

4. 最后數組array中存儲的就是有序的序列

    public static void countSort(int[] array) {//求數組的最大值 和 最小值  O(N)int minVal = array[0];int maxVal = array[0];for (int i = 1; i < array.length; i++) {if(array[i] < minVal) {minVal = array[i];}if(array[i] > maxVal) {maxVal = array[i];}}//確定計數數組的 長度int len = maxVal - minVal + 1;int[] count = new int[len];//遍歷array數組 把數據出現的次數存儲到計數數組當中   O(N)for (int i = 0; i < array.length; i++) {count[array[i]-minVal]++;}//計數數組已經存放了每個數據出現的次數//遍歷計數數組 把實際的數據寫回array數組  O(M) M表示數據范圍int index = 0;for (int i = 0; i < count.length; i++) {while (count[i] > 0) {//這里需要重寫寫回array 意味著得從array的0位置開始寫array[index] = i+minVal;index++;count[i]--;}}}

計數排序的特性總結
1.計數排序是非基于比較的排序

2.?計數排序在數據范圍集中時,效率很高,但是適用范圍及場景有限;計數排序的場景:指定范圍內的數據

3. 時間復雜度:O(MAX(N,M))? ?M表示數據范圍
4. 空間復雜度:O(M)
5. 穩定性:穩定;上述代碼是不穩定的寫法

15、其他排序

15.1、基數排序

基數排序(Radix Sort)是一種非比較型的排序算法,它通過逐位比較元素的每一位(從最低位到最高位)來實現排序。基數排序的核心思想是將整數按位數切割成不同的數字,然后按每個位數分別進行排序。基數排序的時間復雜度為 O(d*(n+r)),其中 n 為元素個數,d 是最大數字的位數,r 為基數(桶的個數)

時間復雜度分析:

分配依次將每個數放到對應的桶中 O(n)

收集依次將每個桶里的元素拿出來 O(r)? (桶里的元素是用鏈表連接的)

每輪:分配+收集 O(n+r)

如果最大的數字有d位,就需要排d輪

所以時間復雜度為:O(d*(n+r))

15.2、桶排序

算法思想:劃分多個范圍相同的區間,每個子區間自排序,最后合并

計數排序、基數排序、桶排序 都是非基于比較的排序

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76676.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76676.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76676.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

虎躍辦公AI:重構智能辦公的「模型交響樂團」

虎躍辦公AI&#xff1a;重構智能辦公的「模型交響樂團」 ——當全球40大模型在辦公場景中奏響協奏曲 在某科創園區的會議室里&#xff0c;市場總監李薇正用AI生成產品發布會方案&#xff0c;設計團隊同步調校著AI渲染的3D主視覺&#xff0c;法務AI自動掃描著合同風險條款——這…

JdbcTemplate基本使用

JdbcTemplate概述 它是spring框架中提供的一個對象&#xff0c;是對原始繁瑣的JdbcAPI對象的簡單封裝。spring框架為我們提供了很多的操作模板類。例如:操作關系型數據的JdbcTemplate和MbernateTemplate&#xff0c;操作nosql數據庫的RedisTemplate&#xff0c;操作消息隊列的…

vue+leaflet 區域劃分_反向遮罩層

leaflet 區域劃分_遮罩層 geojson在線生成器網址:(https://datav.aliyun.com/portal/school/atlas/area_selector) 點擊前往阿里云geojson生成器 效果圖: 實現下面效果,只需要把addSateLayer函數的調用取消掉就好了. //添加遮罩層代碼function addMask() {var latlngs;var fe…

ESP32開發之ubuntu環境搭建

1. 在Ubuntu官網下載Ubuntu server 20.04版本https://releases.ubuntu.com/20.04.6/ 2. 在vmware下安裝Ubuntu 3. 改Ubuntu靜態IP $ sudo vi /etc/netplan/00-installer-config.yaml# This is the network config written by ‘subiquity’ network: renderer: networkd eth…

HTTP 1.1 比 HTTP1.0 多了什么?(詳盡版)

相較于HTTP 1.0&#xff0c;1.1 版本增加了以上特性&#xff1a; 1. 新增了連接管理即 keepalive&#xff0c;允許持久連接。 定義&#xff1a; Keepalive允許客戶端和服務器在完成一次請求-響應后&#xff0c;保持連接處于打開狀態&#xff0c;以便后續請求復用同一連接&am…

【深度學習】PyTorch實現VGG16模型及網絡層數學原理

一、Demo概述 代碼已附在文末 1.1 代碼功能 ? 實現VGG16網絡結構? 在CIFAR10數據集上訓練分類模型 1.2 環境配置 詳見【深度學習】Windows系統Anaconda CUDA cuDNN Pytorch環境配置 二、各網絡層概念 2.1 卷積層&#xff08;nn.Conv2d&#xff09; nn.Conv2d(in_cha…

解決RecyclerView在調用smoothScrollToPosition后最后一個item底部超出屏幕的問題

要解決RecyclerView在調用smoothScrollToPosition后最后一個item底部超出屏幕的問題&#xff0c;可以使用自定義的LinearSmoothScroller&#xff0c;使其底部對齊屏幕。步驟如下&#xff1a; 創建自定義的SmoothScroller類&#xff1a; 繼承LinearSmoothScroller并重寫getVerti…

k8s親和力和非親和力

在 Kubernetes 中&#xff0c;親和力&#xff08;Affinity&#xff09;和非親和力&#xff08;Anti-Affinity&#xff09;是用于控制 Pod 調度策略的機制&#xff0c;它們可以幫助優化資源利用率、提高應用性能和可用性。以下是親和力和非親和力的詳細解釋&#xff1a; 親和力…

開發一款游戲需要哪些崗位角色參與?

常見分類 1. 游戲策劃&#xff08;Game Designer&#xff09; 核心職責&#xff1a;設計游戲的玩法、規則、內容和整體體驗。 具體工作&#xff1a; 系統設計&#xff1a;設計游戲的戰斗、經濟、成長、社交等核心系統。 數值設計&#xff1a;平衡角色屬性、裝備數值、經濟系…

Asp.NET Core WebApi 創建帶鑒權機制的Api

構建一個包含 JWT&#xff08;JSON Web Token&#xff09;鑒權的 Web API 是一種常見的做法&#xff0c;用于保護 API 端點并驗證用戶身份。以下是一個基于 ASP.NET Core 的完整示例&#xff0c;展示如何實現 JWT 鑒權。 1. 創建 ASP.NET Core Web API 項目 使用 .NET CLI 或 …

Jenkins 發送釘釘消息

這里不介紹 Jenkins 的安裝&#xff0c;可以網上找到很多安裝教程&#xff0c;重點介紹如何集成釘釘消息。 需要提前準備釘釘機器人的 webhook 地址。&#xff08;網上找下&#xff0c;很多教程&#xff09; 下面開始配置釘釘機器人&#xff0c;登錄 Jenkins&#xff0c;下載 …

CentOS中離線安裝DockerCompos并用其部署Rabbitmq(使用離線導入導出docker鏡像方式)

場景 DockerDockerCompose實現部署jenkins,并實現jenkinsfile打包SpringBootVue流水線項目過程詳解、踩坑記錄(附鏡像資源、離線包資源下載)&#xff1a; DockerDockerCompose實現部署jenkins,并實現jenkinsfile打包SpringBootVue流水線項目過程詳解、踩坑記錄(附鏡像資源、離…

stm32week11

stm32學習 八.stm32基礎 2.stm32內核和芯片 F1系統架構&#xff1a;4個主動單元和4個被動單元 AHB是內核高性能總線&#xff0c;APB是外圍總線 總線矩陣將總線和各個主動被動單元連到一起 ICode總線直接連接Flash接口&#xff0c;不需要經過總線矩陣 AHB&#xff1a;72MHz&am…

貪心算法:部分背包問題深度解析

簡介&#xff1a; 該Java代碼基于貪心算法實現了分數背包問題的求解&#xff0c;核心通過單位價值降序排序和分階段裝入策略實現最優解。首先對Product數組執行雙重循環冒泡排序&#xff0c;按wm(價值/重量比)從高到低重新排列物品&#xff1b;隨后分兩階段裝入&#xff1a;循環…

13. Langchain異步處理:提升應用性能的關鍵技巧

引言&#xff1a;從"順序等待"到"并行加速" 2025年某電商平臺引入LangChain異步處理后&#xff0c;大促期間訂單處理能力提升5倍&#xff0c;系統響應延遲降低70%。本文將基于LangChain的異步架構&#xff0c;詳解如何通過并行執行流式處理&#xff0c;讓…

ros2-rviz2控制unity仿真的6關節機械臂,探索從仿真到實際應用的過程

文章目錄 前言&#xff08;Introduction&#xff09;搭建開發環境&#xff08;Setup Development Environment&#xff09;在window中安裝Unity&#xff08;Install Unity in window&#xff09;創建Docker容器&#xff0c;并安裝相關軟件&#xff08;Create Docker containers…

計算機組成原理筆記(十四)——3.4指令類型

一臺計算機的指令系統可以有上百條指令&#xff0c;這些指令按其功能可以分成幾種類型&#xff0c;下面分別介紹。 3.4.1數據傳送類指令 一、核心概念與功能定位 數據傳送類指令是計算機指令系統中最基礎的指令類型&#xff0c;負責在 寄存器、主存、I/O設備 之間高效復制數…

各開源協議一覽

在 GitHub 上&#xff0c;開源項目通常會使用一些常見的開源協議來定義項目的使用、修改和分發規則。以下是目前 GitHub 上最常見的幾種開源協議及其差異和示例說明&#xff1a; TL;DR 協議寬松程度是否強制開源專利保護適用場景MIT最寬松否無希望代碼被廣泛使用Apache 2.0寬松…

51c自動駕駛~合集17

我自己的原文哦~ https://blog.51cto.com/whaosoft/13793157 #匯聚感知、定位、規劃控制的自動駕駛系統 自動駕駛技術在應用到車輛上之后可以通過提高吞吐量來緩解道路擁堵&#xff0c;通過消除人為錯誤來提高道路安全性&#xff0c;并減輕駕駛員的駕駛負擔&#xff0c;從…

小程序開發指南

小程序開發指南 目錄 1. 小程序開發概述 1.1 什么是小程序1.2 小程序的優勢1.3 小程序的發展歷程 2. 開發準備工作 2.1 選擇開發平臺2.2 開發環境搭建2.3 開發模式選擇 3. 小程序開發流程 3.1 項目規劃3.2 界面設計3.3 代碼開發3.4 基本開發示例3.5 數據存儲3.6 網絡請求3.7 …