常見的排序方法

目錄

1. 插入排序

2. 希爾排序

3. 選擇排序

4. 堆排序

5. 冒泡排序

6. 快速排序

1. 快速排序的實現

1. 思路(以從小到大排序為例)

2. 選取基準元素的方法(Hoare)

3. 選取基準元素的方法(挖坑法)

2. 快速排序的優化

3. 快速排序的非遞歸實現

7. 歸并排序

1. 歸并排序的實現

2. 歸并排序的非遞歸實現

3. 海量數據的排序問題

8. 計數排序


1. 插入排序

時間復雜度:O(n^2)

空間復雜度:O(1)

穩定性:穩定

特點:數據越有序,排序越快

思路(以從小到大排序為例):

指針 i 標記新拿到的數據,指針 j 標記已有的排好序的數據,初始情況為 i = 1,?j = i - 1;

i 向后遍歷,如果拿到的數據比 j 指向的數據大,直接將 i 指向的數據放在 j + 1 的位置;

注意:第一次挪 j 位置的數據會覆蓋掉原來 i 位置的數據,因此需要定義一個變量,提前保存好 i 位置的數據;

如果 i 指向的數據小于等于 j 指向的數據,將 j 指向的數據放在 j + 1 位置即可;

如果 i 指向的數據比所有的 j 維護的數據都小,那么 j 會減小至負數,將 i 位置數據放到 0 位置即可;

    public static void insertSort(int[] array){int n = array.length;for(int i = 1; i < n; i++){int j = i - 1;int tmp = array[i];for( ; j >= 0; j--){if(tmp < array[j]){array[j + 1] = array[j];}else{break;}}array[j + 1] = tmp;}}

2. 希爾排序

時間復雜度:O(n^1.3 ~ n^1.5)

空間復雜度:O(1)

穩定性:不穩定

思路(以從小到大排序為例):

先將數據分成若干組,每組元素間隔距離為?gap,每組元素以插入排序的方式進行排序;

排序完成后,每組元素間隔距離為 gap / 2,再以插入排序的方式進行排序,直至 gap = 1,整體完成排序;

    public static void shellSort(int[] array){int gap = array.length;while(gap > 0){gap /= 2;shell(array, gap);}}private static void shell(int[] array, int gap){int n = array.length;for(int i = gap; i < n; i++){int tmp = array[i];int j = i - gap;for(; j >= 0; j -= gap){if(array[j] > tmp){array[j + gap] = array[j];}else{break;}}array[j + gap] = tmp;}}

3. 選擇排序

時間復雜度:O(n^2)

空間復雜度:O(1)

穩定性:不穩定

思路(以從小到大排序為例):

定義 i 指針遍歷數組,定義 j = i + 1,找最小值,找到最小值后,將下標 j 存在 minIndex 里面;

交換 i 位置和 minIndex 位置元素,直到 i 遍歷數組完成;

    public static void selectSort(int[] array){int n = array.length;for(int i = 0; i < n; i++){int minIndex = i;for(int j = i + 1; j < n; j++){if(array[j] < array[minIndex]){minIndex = j;}}swap(array, i, minIndex);}}private static void swap(int[] array, int i, int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}

4. 堆排序

時間復雜度:O(n* logn)

空間復雜度:O(1)

穩定性:不穩定

思路(以從小到大排序為例):

建立一個大根堆,從后往前遍歷每一棵子樹,如果根節點值小于左右孩子節點的最大值,根節點值和最大值交換,重新指向交換過的孩子節點并向下調整;

交換堆頂元素和最后一個元素,并將堆頂元素向下調整;

    private static void swap(int[] array, int i, int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}public static void heapSort(int[] array){// 建一個大根堆int n = array.length;int parent = (n - 1 - 1) / 2;for( ; parent >= 0; parent--){shiftDown(array, parent, n);}// 排序int end = n - 1;while(end > 0){swap(array, 0, end);shiftDown(array, 0, end--);}}private static void shiftDown(int[] array, int parent, int size){int child = parent * 2 + 1;while(child < size){if(child + 1 < size && array[child] < array[child + 1]){child++;}if(array[child] > array[parent]){swap(array, child, parent);parent = child;child = parent * 2 + 1;}else{break;}}}

5. 冒泡排序

時間復雜度:O(n*2)

空間復雜度:O(1)

穩定性:穩定

思路(以從小到大排序為例):

定義指針 i 標記排序的趟數,如果數組長度為 n,排 n - 1 趟即可;

定義指針 j 標記要比較的元素,和 j + 1 位置的元素做比較,如果 array[j] > array[j + 1],交換 j 位置和 j + 1 位置的元素;

優化:如果一趟排序下來沒有發生交換,表示已經有序了,跳出循環即可;

    public static void bubbleSort(int[] array){int n = array.length;for(int i = 0; i < n - 1; i++){boolean flag = false;for(int j = 0; j < n - 1 - i; j++){if(array[j] > array[j + 1]){swap(array, j, j + 1);flag = true;}}if(!flag) break;}}private static void swap(int[] array, int i, int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}

6. 快速排序

時間復雜度:

快排算法的時間復雜度取決于分割數組的方式(基準元素的選取),因此分割方式不同,時間復雜度不同;

如果基準元素每次都正好選取了要排序部分的中位數,那么時間復雜度應為 O(n*logn);

如果基準元素每次都正好選取了要排序部分的最小值或者最大值,那么時間復雜度應為 O(n^2);

空間復雜度:O(logn)

穩定性:不穩定

1. 快速排序的實現

1. 思路(以從小到大排序為例)

采用數組分塊的思想,選取一個基準元素,將數組分成兩塊,此時基準元素已經有序了,分別處理基準元素左右兩遍的區間;

左邊部分再選取基準元素,在 [left, pivot - 1] 中遞歸;

右邊部分再選取基準元素,在 [pivot + 1, right] 中遞歸;

2. 選取基準元素的方法(Hoare)

left 指向待排序的區間的左端點,right 指向待排序的區間的右端點;

選取左端點指向的元素為基準元素;

如果 right 指針指向的元素大于等于基準元素,就向左移動;

如果 left 指針指向的元素小于等于基準元素,就向右移動;

此時 right 指向的是比基準元素小的值,left 指向的是比基準元素大的值,交換 left 和 right 指向的元素;

直到 left 指針和 right 指針相遇,此時指向的位置,左邊的元素都小于等于基準元素,右邊的元素都大于等于基準元素;

    public static void quickSort(int[] array){int n = array.length;quick(array, 0, n - 1);}public static void quick(int[] array, int left, int right){if(left >= right) return;int pivot = partition(array, left, right);quick(array, left, pivot - 1);quick(array, pivot + 1, right);}private static int partition(int[] array, int left, int right){int tmp = array[left];int pos = left;while(left < right){while(left < right && array[right] >= tmp) right--;while(left < right && array[left] <= tmp) left++;swap(array, left, right);}swap(array, pos, left);return left;}private static void swap(int[] array, int i, int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}

要點 1:為什么選取基準元素,移動 left 和 right 指針的時候要取等號?

防止 left 和 right 指針指向的元素都恰好等于基準元素的值,都等于會陷入死循環;

要點 2:為什么要先移動 right 指針,而不是先移動 left 指針?

如果先移動 left,當 left 和 right 指針相遇的時候,指向的元素的值會大于基準元素,如果交換基準元素和該元素,就會把比基準元素大的值交換到左邊去,不符合排序的思想;

3. 選取基準元素的方法(挖坑法)

left 指向待排序的區間的左端點,right 指向待排序的區間的右端點;

選取左端點指向的元素為基準元素;

如果 right 指針指向的元素大于等于基準元素,就向左移動;

此時 right 指向的是比基準元素小的值,把 right 指向的元素放到?left 指向的位置;

如果 left 指針指向的元素小于等于基準元素,就向右移動;

此時 left 指向的是比基準元素大的值,把 left 指向的元素放到 right 指向的位置;

直到 left 指針和 right 指針相遇,把基準元素放到此時指向的位置,左邊的元素都小于等于基準元素,右邊的元素都大于等于基準元素,此時基準元素已經有序了;

    private static int partition2(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;}

2. 快速排序的優化

使用快排為了避免出現選取基準元素選取到區間中的最大值或者最小值的情況,因此可以結合三數取中法進行優化;

思路:

定義 left 指向區間的左端點,right 指向區間的右端點,mid 指向區間的中點;

找出 left,right,mid 指向的元素中第二大的元素,返回下標;

    private static int midOfThree(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 {if(array[mid] < array[right]) return right;else if(array[mid] > array[left]) return left;else return mid;}}

結合三數取中法優化:

記錄三數取中法返回的下標 index,index 指向的元素和 left 指向的元素進行交換,保證數組分塊時,每次選取 left 指向的元素都不是區間內的最大值和最小值,使分塊形成的二叉樹盡可能接近完全二叉樹;

    public static void quick(int[] array, int left, int right){if(left >= right) return;int index = midOfThree(array, left, right);swap(array, index, left);int pivot = partition(array, left, right);quick(array, left, pivot - 1);quick(array, pivot + 1, right);}

3. 快速排序的非遞歸實現

快速排序可以借助遞歸實現,遞的過程就是調用方法在棧上開辟棧幀的過程,歸的過程就是方法返回銷毀棧幀的過程,因此遞歸通常可以借助棧來轉化為非遞歸。

思路:

遞歸是找基準元素將數組分塊,再分別處理左邊區間和右邊區間;

因此可以將區間的左右端點加入到棧中,每次彈出一組端點,分別為區間的右端點和左端點;

調用數組分塊的方法,將這段區間分為兩塊,再重新將兩塊區間的端點加入到棧中;

直至彈出棧中所有的數對,排序就完成了;

    // 快排的非遞歸版本public static void quickSortNor(int[] array){int n = array.length;Stack<Integer> stack = new Stack<>();stack.push(0);stack.push(n - 1);while(!stack.isEmpty()){int right = stack.pop();int left = stack.pop();int pivot = partition2(array, left, right);if(left + 1 < pivot){stack.push(left);stack.push(pivot - 1);}if(pivot + 1 < right){stack.push(pivot + 1);stack.push(right);}}}

7. 歸并排序

時間復雜度:O(n*logn)

空間復雜度:O(n)

穩定性:穩定

1. 歸并排序的實現

思路:

給定一段待排序的區間,left 指向區間左端點,right 指向區間右端點,mid 指向區間左中點;

先對左邊區間進行排序 [left, mid],再對右邊區間進行排序 [mid + 1, right];

合并兩個有序區間;

    public static void mergeSort(int[] array){mergeSortFunc(array, 0, array.length - 1);}private static void mergeSortFunc(int[] array, int left, int right) {if(left >= right) return;int mid = (right + left) / 2;mergeSortFunc(array, left, mid);mergeSortFunc(array, mid + 1, right);merge(array, left, mid, right);}private static void merge(int[] array, int left, int mid, int right){int i = left;int j = mid + 1;int k = 0;int[] tmp = new int[right - left + 1];while(i <= mid && j <= right){if(array[i] <= array[j]){tmp[k++] = array[i++];}else{tmp[k++] = array[j++];}}while(i <= mid) tmp[k++] = array[i++];while(j <= right) tmp[k++] = array[j++];for(int l = left; l <= right; l++){array[l] = tmp[l - left];}}

2. 歸并排序的非遞歸實現

思路:

定義變量 gap 表示每次排序的子區間的長度,從左向右對子區間進行排序;

left 用于表示子區間的左端點,mid 表示子區間的右端點,mid + 1 表示下一個子區間的左端點,right 表示下一個子區間的右端點,分別計算 left,mid,right 的值;

調用 merge() 方法合并兩個有序區間;

gap 增大為原來的 2 倍,繼續重復上述步驟,直到 gap 增大到大于等于整個數組的長度;

    public static void mergeSortNor(int[] array){int n = array.length;int gap = 1;while(gap < n){for(int i = 0; i < n; i += 2 * gap){int left = i;int mid = i + gap - 1;int right = mid + gap;if(mid >= n) mid = n - 1;if(right >= n) right = n - 1;merge(array, left, mid, right);}gap *= 2;}}

3. 海量數據的排序問題

外部排序:排序過程需要在磁盤外部進行的排序;

前提:內存只有 1G,需要排序 100G 的數據;

因為內存只有 1G,無法將所有數據全部放下,所以需要外部排序,而歸并排序是最常用的外部排序;

實現步驟:

1. 先把數據分割成 200 份,每份 512M;

2. 對每一份 512M 的數據進行排序,因為內存可以放得下,因此選用哪種排序方式均可;

3. 進行 2 路歸并,同時讀取兩個文件中的數據,每次讀 1 個數據,進行比較排序,并寫入到一個新文件中;

4. 得到新文件再按照上述方式進行 2 路歸并,最終形成一個新的數據有序的文件;

8. 計數排序

時間復雜度:O(N+數據范圍)

空間復雜度:O(數據范圍)

穩定性:穩定

思路(下面代碼是不穩定的排序):

遍歷一遍數組,找出最小值 minVal 和最大值 maxVal;

建立一個計數數組 count,大小為 maxVal - minVal + 1;

遍歷一遍數組,統計每個數字出現的次數,minVal 放到下標為 0 的位置,依次類推;

遍歷一遍 count 數組,如果 count[i] 不等于 0,依次將數字放到原數組中;

    public static void countSort(int[] array){int n = array.length;int minVal = array[0];int maxVal = array[0];for(int x : array){if(minVal > x) minVal = x;if(maxVal < x) maxVal = x;}int[] count = new int[maxVal - minVal + 1];for(int x : array){count[x - minVal]++;}int pos = 0;for(int i = 0; i < count.length; i++){while(count[i]-- > 0){array[pos++] = i + minVal;}}}

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

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

相關文章

【matlab定位例程】基于AOA和TDOA混合的定位方法,背景為三維空間,自適應錨點數量,附下載鏈接

文章目錄 代碼概述代碼功能概述核心算法原理AOA定位模型TDOA定位迭代算法混合定位策略關鍵技術創新 運行結果4個錨點的情況40個錨點的情況 MATLAB源代碼 代碼概述 代碼功能概述 本代碼實現了一種三維空間中的混合定位算法&#xff0c;結合到達角&#xff08; A O A AOA AOA&a…

專題:2025醫療AI應用研究報告|附200+份報告PDF匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p42748 本報告匯總解讀聚焦醫療行業人工智能應用的前沿動態與市場機遇&#xff0c;以數據驅動視角剖析技術演進與商業落地的關鍵路徑。從GenAI在醫療領域的爆發式增長&#xff0c;到細分場景的成熟度矩陣&#xff0c;再到運營成本壓力…

推薦一個前端基于vue3.x,vite7.x,后端基于springboot3.4.x的完全開源的前后端分離的中后臺管理系統基礎項目(純凈版)

XHan Admin 簡介 &#x1f389;&#x1f389; XHan Admin 是一個開箱即用的開源中后臺管理系統基礎解決方案&#xff0c; 項目為前后端分離架構。采用最新的技術棧全新構建&#xff0c;純凈的項目代碼&#xff0c;沒有歷史包袱。 前端使用最新發布的 vite7.0 版本構建&#xf…

MySQL誤刪數據急救指南:基于Binlog日志的實戰恢復詳解

背景 數據誤刪是一個比較嚴重的場景 1.典型誤操作場景 場景1&#xff1a;DELETE FROM orders WHERE status0 → 漏寫AND create_time>‘2025-06-20’ 場景2&#xff1a;DROP TABLE customer → 誤執行于生產環境 認識 binlog 1.binlog 的核心作用 記錄所有 DDL/DML 操…

高效數據采集方案:快速部署與應用 AnyCrawl 網頁爬蟲工具實操指南

以下是對 AnyCrawl 的簡單介紹&#xff1a; AnyCrawl 提供高性能網頁數據爬取&#xff0c;其功能專為 LLM 集成和數據處理而設計支持利用搜索引擎直接查詢獲取結果內容&#xff0c;類似 searxng提供開發者友好的API&#xff0c;支持動態內容抓取&#xff0c;并輸出結構化數據&…

vue3可以分頁、搜索的select

下載 npm i v-selectpage基本使用 import { SelectPageList } from v-selectpage;<SelectPageListlanguage"zh-chs"key-prop"id"label-prop"name"fetch-data"fetchData" />const fetchData (data,callback) > {const { sea…

C# 入門學習教程 (一)

文章目錄 一、解決方案與項目1. Solution 與 project 二、類與名稱空間1.類與名稱空間2.類庫的引用1. DLL引用&#xff08;黑盒引用&#xff0c;無源代碼&#xff09;2. Nuget 引用3. 項目引用&#xff08;白盒引用&#xff0c;有源代碼&#xff09; 3.依賴關系 三、類&#xf…

76、單元測試-參數化測試

76、單元測試-參數化測試 參數化測試是一種單元測試技術&#xff0c;通過將測試數據與測試邏輯分離&#xff0c;使用不同的輸入參數多次運行相同的測試用例&#xff0c;從而提高測試效率和代碼復用性。 #### 基本原理 - **數據驅動測試**&#xff1a;將測試數據參數化&#xf…

SQL學習筆記3

SQL常用函數 1、字符串函數 函數調用的語法&#xff1a;select 函數&#xff08;參數); 常用的字符串函數有&#xff1a; 拼接字符串&#xff0c;將幾個字符串拼到一起&#xff1a;concat (s1,s2,……); select concat(你好,hello); update mytable set wherefo concat(中…

Golang 面向對象編程,如何實現 封裝、繼承、多態

Go語言雖然不是純粹的面向對象語言&#xff0c;但它通過結構體(struct)、接口(interface)和方法(method)提供了面向對象編程的能力。下面我將通過具體示例展示Go中如何實現類、封裝、繼承、多態以及構造函數等概念。 1. 類與封裝 在Go中&#xff0c;使用結構體(struct)來定義…

為什么android要使用Binder機制

1.linux中大多數標準 IPC 場景&#xff08;如管道、消息隊列、ioctl 等&#xff09;的進程間通信機制 ------------------ ------------------ ------------------ | 用戶進程 A | | 內核空間 | | 用戶進程 B | | (User Spa…

OpenCV CUDA模塊設備層-----雙曲余弦函數cosh()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 該函數用于計算四維浮點向量&#xff08;float4類型&#xff09;的雙曲余弦值&#xff0c;作用于CUDA設備端。雙曲余弦函數定義為cosh(x) (e? …

48頁PPT | 企業數字化轉型關鍵方法論:實踐路徑、案例和落地評估框架

目錄 一、什么是企業數據化轉型&#xff1f; 二、為什么要進行數據化轉型&#xff1f; 1. 市場復雜性與不確定性上升 2. 內部流程效率與協同難題突出 3. 數字資產沉淀不足&#xff0c;智能化基礎薄弱 三、數據化流程管理&#xff1a;從“業務流程”到“數據流程”的對齊 …

VTK中的形態學處理

VTK圖像處理代碼解析:閾值化與形態學開閉運算 這段代碼展示了使用VTK進行醫學圖像處理的兩個關鍵步驟:閾值分割和形態學開閉運算。下面我將詳細解析每個部分的功能和實現原理。 處理前 處理后 1. 閾值分割部分 (vtkImageThreshold) vtkSmartPointer<vtkImageThresho…

xlsx.utils.sheet_to_json() 方法詳解

sheet_to_json() 是 SheetJS/xlsx 庫中最常用的方法之一&#xff0c;用于將 Excel 工作表&#xff08;Worksheet&#xff09;轉換為 JSON 格式數據。下面我將全面講解它的用法、參數配置和實際應用場景。 基本語法 javascript 復制 下載 const jsonData XLSX.utils.sheet…

〔從零搭建〕BI可視化平臺部署指南

&#x1f525;&#x1f525; AllData大數據產品是可定義數據中臺&#xff0c;以數據平臺為底座&#xff0c;以數據中臺為橋梁&#xff0c;以機器學習平臺為中層框架&#xff0c;以大模型應用為上游產品&#xff0c;提供全鏈路數字化解決方案。 ?杭州奧零數據科技官網&#xf…

合規型區塊鏈RWA系統解決方案報告——機構資產數字化的終極武器

&#xff08;跨境金融科技解決方案白皮書&#xff09; 一、直擊機構客戶四大痛點 痛點傳統方案缺陷我們的破局點?? 跨境資產流動性差結算周期30天&#xff0c;摩擦成本超8%?? 724h全球實時交易&#xff08;速度提升90%&#xff09;?? 合規成本飆升KYC/AML人工審核占成本…

探索阿里云容器:解鎖云原生應用的無限可能

引言&#xff1a;容器時代的開啟 在數字化浪潮洶涌澎湃的當下&#xff0c;云計算已成為企業創新與發展的關鍵驅動力。從早期的基礎設施即服務&#xff08;IaaS&#xff09;&#xff0c;到如今蓬勃發展的平臺即服務&#xff08;PaaS&#xff09;和軟件即服務&#xff08;SaaS&a…

spring-ai 1.0.0 (1)模型調用能力

聽說1.0是一個非常好用的版本&#xff0c;最后還是扛不住聽說的壓力&#xff0c;為了落實自己懸浮心理&#xff0c;自己還是著手實踐一下了。 第一步pom集成&#xff1a; 參考spring-projects/spring-ai | DeepWiki維基以及官方文檔入門 &#xff1a;&#xff1a; Spring AI …

數據分享:汽車行業-汽車屬性數據集

說明&#xff1a;如需數據可以直接到文章最后關注獲取。 1.數據背景 Automobile數據集源自于對汽車市場深入研究的需求&#xff0c;旨在為汽車行業提供一個全面且詳細的資源&#xff0c;以便更好地理解影響汽車價格及性能的各種因素。該數據集最初由卡內基梅隆大學&#x…