版權信息:
原文鏈接:[必須知道的八大種排序算法【java實現】](http://www.jianshu.com/p/8c915179fd02)
---
## 一、冒泡排序
冒泡排序是一種簡單的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
冒泡排序的**示例**:

冒泡排序的**算法實現**如下:【排序后,數組從小到大排列】
~~~
/*
* 冒泡排序
* 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
* 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元素應該會是最大的數。
* 針對所有的元素重復以上的步驟,除了最后一個。
* 持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
* @param numbers 需要排序的整型數組
*/
public static void bubbleSort(int[] numbers)
{
int temp = 0;
int size = numbers.length;
for(int i = 0 ; i < size-1; i ++)
{
for(int j = 0 ;j < size-1-i ; j++)
{
if(numbers[j] > numbers[j+1]) //交換兩數位置
{
temp = numbers[j];
numbers[j] = numbers[j+1];
numbers[j+1] = temp;
}
}
}
}
~~~
* * *
## 二、快速排序
**快速排序的基本思想**:
通過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分關鍵字小,則分別對這兩部分繼續進行排序,直到整個序列有序。
快速排序的**示例**:
(a)一趟排序的過程:

(b)排序的全過程:

把整個序列看做一個數組,把第零個位置看做中軸,和最后一個比,如果比它小交換,比它大不做任何處理;交換了以后再和小的那端比,比它小不交換,比他大交換。這樣循環往復,一趟排序完成,左邊就是比中軸小的,右邊就是比中軸大的,然后再用分治法,分別對這兩個獨立的數組進行排序。
代碼實現如下:
1.查找中軸(最低位作為中軸)所在位置:
~~~
/**
* 查找出中軸(默認是最低位low)的在numbers數組排序后所在位置
*
* @param numbers 帶查找數組
* @param low 開始位置
* @param high 結束位置
* @return 中軸所在位置
*/
public static int getMiddle(int[] numbers, int low,int high)
{
int temp = numbers[low]; //數組的第一個作為中軸
while(low < high)
{
while(low < high && numbers[high] > temp)
{
high--;
}
numbers[low] = numbers[high];//比中軸小的記錄移到低端
while(low < high && numbers[low] < temp)
{
low++;
}
numbers[high] = numbers[low] ; //比中軸大的記錄移到高端
}
numbers[low] = temp ; //中軸記錄到尾
return low ; // 返回中軸的位置
}
~~~
2、 遞歸形式的分治排序算法:
~~~
/**
*
* @param numbers 帶排序數組
* @param low 開始位置
* @param high 結束位置
*/
public static void quickSort(int[] numbers,int low,int high)
{
if(low < high)
{
int middle = getMiddle(numbers,low,high); //將numbers數組進行一分為二
quickSort(numbers, low, middle-1); //對低字段表進行遞歸排序
quickSort(numbers, middle+1, high); //對高字段表進行遞歸排序
}
}
~~~
3、快速排序提供方法調用:
~~~
/**
* 快速排序
* @param numbers 帶排序數組
*/
public static void quick(int[] numbers)
{
if(numbers.length > 0) //查看數組是否為空
{
quickSort(numbers, 0, numbers.length-1);
}
}
~~~
**分析:**
快速排序是通常被認為在**同數量級(O(nlog2n))**的排序方法中平均性能最好的。但若初始序列按關鍵碼有序或基本有序時,快排序反而蛻化為冒泡排序。為改進之,通常以“三者取中法”來選取基準記錄,即將排序區間的兩個端點與中點三個記錄關鍵碼居中的調整為支點記錄。快速排序是一個不穩定的排序方法。
## 三、方法測試
打印函數:
~~~
public static void printArr(int[] numbers)
{
for(int i = 0 ; i < numbers.length ; i ++ )
{
System.out.print(numbers[i] + ",");
}
System.out.println("");
}
~~~
測試:
~~~
public static void main(String[] args)
{
int[] numbers = {10,20,15,0,6,7,2,1,-5,55};
System.out.print("排序前:");
printArr(numbers);
bubbleSort(numbers);
System.out.print("冒泡排序后:");
printArr(numbers);
quick(numbers);
System.out.print("快速排序后:");
printArr(numbers);
}
~~~
結果:
~~~
排序前:10,20,15,0,6,7,2,1,-5,55,
冒泡排序后:-5,0,1,2,6,7,10,15,20,55,
快速排序后:-5,0,1,2,6,7,10,15,20,55,
~~~
## 四、選擇排序
**1、基本思想**:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;然后在剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最后一個數比較為止。
**2、實例**:

**3、算法實現**:
~~~
/**
* 選擇排序算法
* 在未排序序列中找到最小元素,存放到排序序列的起始位置
* 再從剩余未排序元素中繼續尋找最小元素,然后放到排序序列末尾。
* 以此類推,直到所有元素均排序完畢。
* @param numbers
*/
public static void selectSort(int[] numbers)
{
int size = numbers.length; //數組長度
int temp = 0 ; //中間變量
for(int i = 0 ; i < size ; i++)
{
int k = i; //待確定的位置
//選擇出應該在第i個位置的數
for(int j = size -1 ; j > i ; j--)
{
if(numbers[j] < numbers[k])
{
k = j;
}
}
//交換兩個數
temp = numbers[i];
numbers[i] = numbers[k];
numbers[k] = temp;
}
}
~~~
## 五、插入排序
**1、基本思想**:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置(從后向前找到合適位置后),直到全部插入排序完為止。
**2、實例**:

**3、算法實現**:
~~~
/**
* 插入排序
*
* 從第一個元素開始,該元素可以認為已經被排序
* 取出下一個元素,在已經排序的元素序列中從后向前掃描
* 如果該元素(已排序)大于新元素,將該元素移到下一位置
* 重復步驟3,直到找到已排序的元素小于或者等于新元素的位置
* 將新元素插入到該位置中
* 重復步驟2
* @param numbers 待排序數組
*/
public static void insertSort(int[] numbers)
{
int size = numbers.length;
int temp = 0 ;
int j = 0;
for(int i = 0 ; i < size ; i++)
{
temp = numbers[i];
//假如temp比前面的值小,則將前面的值后移
for(j = i ; j > 0 && temp < numbers[j-1] ; j --)
{
numbers[j] = numbers[j-1];
}
numbers[j] = temp;
}
}
~~~
**4、效率**:
時間復雜度:O(n^2).
## 六、希爾算法
**1、基本思想:**
先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。
**2、操作方法:**
`1、選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2、按增量序列個數k,對序列進行k 趟排序;
3、每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。`
希爾排序的示例:

**3、****算法實現:**
~~~
/**希爾排序的原理:根據需求,如果你想要結果從大到小排列,它會首先將數組進行分組,然后將較大值移到前面,較小值
* 移到后面,最后將整個數組進行插入排序,這樣比起一開始就用插入排序減少了數據交換和移動的次數,可以說希爾排序是加強
* 版的插入排序
* 拿數組5, 2, 8, 9, 1, 3,4來說,數組長度為7,當increment為3時,數組分為兩個序列
* 5,2,8和9,1,3,4,第一次排序,9和5比較,1和2比較,3和8比較,4和比其下標值小increment的數組值相比較
* 此例子是按照從大到小排列,所以大的會排在前面,第一次排序后數組為9, 2, 8, 5, 1, 3,4
* 第一次后increment的值變為3/2=1,此時對數組進行插入排序,
*實現數組從大到小排
*/
public static void shellSort(int[] data)
{
int j = 0;
int temp = 0;
//每次將步長縮短為原來的一半
for (int increment = data.length / 2; increment > 0; increment /= 2)
{
for (int i = increment; i < data.length; i++)
{
temp = data[i];
for (j = i; j >= increment; j -= increment)
{
if(temp > data[j - increment])//如想從小到大排只需修改這里
{
data[j] = data[j - increment];
}
else
{
break;
}
}
data[j] = temp;
}
}
}
~~~
**4、效率**:
時間復雜度:O(n^2).
## 七、 歸并排序算法
**基本思想:**
歸并(Merge)排序法是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合并為整體有序序列。
歸并排序示例:

**合并方法:**
設r[i…n]由兩個有序子表r[i…m]和r[m+1…n]組成,兩個子表長度分別為n-i +1、n-m。
~~~
1、j=m+1;k=i;i=i; //置兩個子表的起始下標及輔助數組的起始下標
2、若i>m 或j>n,轉⑷ //其中一個子表已合并完,比較選取結束
3、//選取r[i]和r[j]較小的存入輔助數組rf
如果r[i]<r[j],rf[k]=r[i]; i++; k++; 轉⑵
否則,rf[k]=r[j]; j++; k++; 轉⑵
4、//將尚未處理完的子表中元素存入rf
如果i<=m,將r[i…m]存入rf[k…n] //前一子表非空
如果j<=n , 將r[j…n] 存入rf[k…n] //后一子表非空
5、合并結束。
~~~
**算法實現:**
~~~
/**
* 歸并排序
* 簡介:將兩個(或兩個以上)有序表合并成一個新的有序表 即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合并為整體有序序列
* 時間復雜度為O(nlogn)
* 穩定排序方式
* @param nums 待排序數組
* @return 輸出有序數組
*/
public static int[] sort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左邊
sort(nums, low, mid);
// 右邊
sort(nums, mid + 1, high);
// 左右歸并
merge(nums, low, mid, high);
}
return nums;
}
/**
* 將數組中low到high位置的數進行排序
* @param nums 待排序數組
* @param low 待排的開始位置
* @param mid 待排中間位置
* @param high 待排結束位置
*/
public static void merge(int[] nums, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low;// 左指針
int j = mid + 1;// 右指針
int k = 0;
// 把較小的數先移到新數組中
while (i <= mid && j <= high) {
if (nums[i] < nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
}
}
// 把左邊剩余的數移入數組
while (i <= mid) {
temp[k++] = nums[i++];
}
// 把右邊邊剩余的數移入數組
while (j <= high) {
temp[k++] = nums[j++];
}
// 把新數組中的數覆蓋nums數組
for (int k2 = 0; k2 < temp.length; k2++) {
nums[k2 + low] = temp[k2];
}
}
~~~
## 八、堆排序算法
**1、基本思想:**
堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。
堆的定義下:具有n個元素的序列 (h1,h2,...,hn),當且僅當滿足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)時稱之為堆。在這里只討論滿足前者條件的堆。由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項(大頂堆)。完全二 叉樹可以很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。
思想:初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,使之成為一個 堆,這時堆的根節點的數最大。然后將根節點與堆的最后一個節點交換。然后對前面(n-1)個數重新調整使之成為堆。依此類推,直到只有兩個節點的堆,并對 它們作交換,最后得到有n個節點的有序序列。從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反復調用滲透函數實現排序的函數。
**2、實例:**
初始序列:46,79,56,38,40,84
建堆:

交換,從堆中踢出最大數:

依次類推:最后堆中剩余的最后兩個結點交換,踢出一個,排序完成。
**3.算法實現:**
~~~
public class HeapSort {
public static void main(String[] args) {
int[] a={49,38,65,97,76,13,27,49,78,34,12,64};
int arrayLength=a.length;
//循環建堆
for(int i=0;i<arrayLength-1;i++){
//建堆
buildMaxHeap(a,arrayLength-1-i);
//交換堆頂和最后一個元素
swap(a,0,arrayLength-1-i);
System.out.println(Arrays.toString(a));
}
}
//對data數組從0到lastIndex建大頂堆
public static void buildMaxHeap(int[] data, int lastIndex){
//從lastIndex處節點(最后一個節點)的父節點開始
for(int i=(lastIndex-1)/2;i>=0;i--){
//k保存正在判斷的節點
int k=i;
//如果當前k節點的子節點存在
while(k*2+1<=lastIndex){
//k節點的左子節點的索引
int biggerIndex=2*k+1;
//如果biggerIndex小于lastIndex,即biggerIndex+1代表的k節點的右子節點存在
if(biggerIndex<lastIndex){
//若果右子節點的值較大
if(data[biggerIndex]<data[biggerIndex+1]){
//biggerIndex總是記錄較大子節點的索引
biggerIndex++;
}
}
//如果k節點的值小于其較大的子節點的值
if(data[k]<data[biggerIndex]){
//交換他們
swap(data,k,biggerIndex);
//將biggerIndex賦予k,開始while循環的下一次循環,重新保證k節點的值大于其左右子節點的值
k=biggerIndex;
}else{
break;
}
}
}
}
//交換
private static void swap(int[] data, int i, int j) {
int tmp=data[i];
data[i]=data[j];
data[j]=tmp;
}
}
~~~
## 九、各種算法的時間復雜度

作者:shadow000902
鏈接:http://www.jianshu.com/p/8c915179fd02
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
- 0-發現
- AndroidInterview-Q-A
- Android能讓你少走彎路的干貨整理
- LearningNotes
- temp
- temp11
- 部分地址
- 0-待辦任務
- 待補充列表
- 0-未分類
- AndroidView事件分發與滑動沖突處理
- Spannable
- 事件分發機制詳解
- 1-Java
- 1-Java-01基礎
- 未歸檔
- 你應該知道的JDK知識
- 集合框架
- 1-Java-04合集
- Java之旅0
- Java之旅
- JAVA之旅01
- JAVA之旅02
- JAVA之旅03
- JAVA之旅04
- JAVA之旅05
- JAVA之旅06
- JAVA之旅07
- JAVA之旅08
- JAVA之旅09
- java之旅1
- JAVA之旅10
- JAVA之旅11
- JAVA之旅12
- JAVA之旅13
- JAVA之旅14
- JAVA之旅15
- JAVA之旅16
- JAVA之旅17
- JAVA之旅18
- JAVA之旅19
- java之旅2
- JAVA之旅20
- JAVA之旅21
- JAVA之旅22
- JAVA之旅23
- JAVA之旅24
- JAVA之旅25
- JAVA之旅26
- JAVA之旅27
- JAVA之旅28
- JAVA之旅29
- java之旅3
- JAVA之旅30
- JAVA之旅31
- JAVA之旅32
- JAVA之旅33
- JAVA之旅34
- JAVA之旅35
- 1-Java-05辨析
- HashMapArrayMap
- Java8新特性
- Java8接口默認方法
- 圖解HashMap(1)
- 圖解HashMap(2)
- 2-Android
- 2-Android-1-基礎
- View繪制流程
- 事件分發
- AndroidView的事件分發機制和滑動沖突解決
- 自定義View基礎
- 1-安卓自定義View基礎-坐標系
- 2-安卓自定義View基礎-角度弧度
- 3-安卓自定義View基礎-顏色
- 自定義View進階
- 1-安卓自定義View進階-分類和流程
- 10-安卓自定義View進階-Matrix詳解
- 11-安卓自定義View進階-MatrixCamera
- 12-安卓自定義View進階-事件分發機制原理
- 13-安卓自定義View進階-事件分發機制詳解
- 14-安卓自定義View進階-MotionEvent詳解
- 15-安卓自定義View進階-特殊形狀控件事件處理方案
- 16-安卓自定義View進階-多點觸控詳解
- 17-安卓自定義View進階-手勢檢測GestureDetector
- 2-安卓自定義View進階-繪制基本圖形
- 3-安卓自定義View進階-畫布操作
- 4-安卓自定義View進階-圖片文字
- 5-安卓自定義View進階-Path基本操作
- 6-安卓自定義View進階-貝塞爾曲線
- 7-安卓自定義View進階-Path完結篇偽
- 8-安卓自定義View進階-Path玩出花樣PathMeasure
- 9-安卓自定義View進階-Matrix原理
- 通用類介紹
- Application
- 2-Android-2-使用
- 2-Android-02控件
- ViewGroup
- ConstraintLayout
- CoordinatorLayout
- 2-Android-03三方使用
- Dagger2
- Dagger2圖文完全教程
- Dagger2最清晰的使用教程
- Dagger2讓你愛不釋手-終結篇
- Dagger2讓你愛不釋手-重點概念講解、融合篇
- dagger2讓你愛不釋手:基礎依賴注入框架篇
- 閱讀筆記
- Glide
- Google推薦的圖片加載庫Glide:最新版使用指南(含新特性)
- rxjava
- 這可能是最好的RxJava2.x入門教程完結版
- 這可能是最好的RxJava2.x入門教程(一)
- 這可能是最好的RxJava2.x入門教程(三)
- 這可能是最好的RxJava2.x入門教程(二)
- 這可能是最好的RxJava2.x入門教程(五)
- 這可能是最好的RxJava2.x入門教程(四)
- 2-Android-3-優化
- 優化概況
- 各種優化
- Android端秒開優化
- apk大小優化
- 內存分析
- 混淆
- 2-Android-4-工具
- adb命令
- 一鍵分析Android的BugReport
- 版本控制
- git
- git章節簡述
- 2-Android-5-源碼
- HandlerThread 源碼分析
- IntentService的使用和源碼分析
- 2-Android-9-辨析
- LRU算法
- 什么是Bitmap
- 常見圖片壓縮方式
- 3-Kotlin
- Kotlin使用筆記1-草稿
- Kotlin使用筆記2
- kotlin特性草稿
- Kotlin草稿-Delegation
- Kotlin草稿-Field
- Kotlin草稿-object
- 4-JavaScript
- 5-Python
- 6-Other
- Git
- Gradle
- Android中ProGuard配置和總結
- gradle使用筆記
- Nexus私服搭建
- 編譯提速最佳實踐
- 7-設計模式與架構
- 組件化
- 組件化探索(OKR)
- 1-參考列表
- 2-1-組件化概述
- 2-2-gradle配置
- 2-3-代碼編寫
- 2-4-常見問題
- 2-9-值得一讀
- 8-數據結構與算法
- 0臨時文件
- 漢諾塔
- 8-數據-1數據結構
- HashMap
- HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比較
- 遲到一年HashMap解讀
- 8-數據-2算法
- 1個就夠了
- Java常用排序算法(必須掌握的8大排序算法)
- 常用排序算法總結(性能+代碼)
- 必須知道的八大種排序算法(java實現)
- 9-職業
- 閱讀
- 書單
- 面試
- 面試-01-java
- Java面試題全集駱昊(上)
- Java面試題全集駱昊(下)
- Java面試題全集駱昊(中)
- 面試-02-android
- 40道Android面試題
- 面試-03-開源源碼
- Android圖片加載框架最全解析(二),從源碼的角度理解Glide的執行流程
- 面試-07-設計模式
- 面試-08-算法
- 面試-09-其他
- SUMMARY
- 版權說明
- temp111