排序分類.png
性能比較
對比圖.png
O(nlogn)效率優于O(n^2)
簡單算法:冒泡、選擇、直接插入
改進算法:希爾、堆、快速、歸并
不穩定的排序----快些選隊(快速排序、希爾排序、選擇排序、堆排序)
冒泡排序
public static void BubbleSort(int[] num) {
int size =num.length;
boolean flag = false;
for(int i = 0; i< size-1; i++) {
for(int j = 0; j< size-1-i; j++) {
if (num[j] > num[j+1]) {
int temp = num[j];
num[j] = num[j+1];
num[j+1] = temp;
flag = true;
}
}
if (flag == false) {
break; //遍歷一遍數組后發現無元素交換時說明此時數組已有序
}
}
}
快速排序(冒泡排序的升級,同屬于交換排序類)
通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。
適用于數據量較大的情況
原始算法
public static void quickSort(int[] num,int low, int high) {
if (low < high) {
int middle = getMiddle(num, low, high); //將數組一分為二
quickSort(num, low, middle-1); //對低字段進行遞歸排序
quickSort(num, middle+1, high); //對高字段進行遞歸排序
}
}
public static int getMiddle(int[] num, int low, int high) {
int temp = num[low]; //優化前:選取第一個字段作為基準值
while(low < high) {
while(low < high && num[high] > temp) { //從后往前找到一個比基準值小的數
high--;
}
swap(num, low, high);
while(low <high && num[low] < temp) { //從前往后找一個比基準值大的數
low++;
}
swap(num, low, high);
}
return low; //返回中軸所在位置
}
public static void swap(int[] num, int low, int high) {
int temp = num[low];
num[low] = num[high];
num[high] = temp;
}
優化算法
采用三數取中方式選取基準值
采用替換方式而不是交換方式進行操作
public static int getMiddle(int[] num, int low, int high) {
//快速排序優化:三數取中方式選取基準值
int m = low + (high - low) /2; //獲取數組中間值下標
if (num[low] > num[high]) {
swap(num, low, high); //保證左端小于右端
}
if (num[m] > num[high]) {
swap(num, m, high); //保證中間小于右端
}
if (num[m] > num[low]) {
swap(num, m, low); //保證左端小于中間
}
int temp = num[low]; //優化后:選取數組第一個數,中間的數以及最后一個數中的中間數最為基準值
while(low < high) { //循環結束后low和high指向同一元素,該元素的位置就是基準元素的位置
while(low < high && num[high] > temp) { //從后往前找到一個比基準值小的數
high--;
}
num[low] = num[high]; //采用替換而不是交換方式進行操作
while(low <high && num[low] < temp) { //從前往后找一個比基準值大的數
low++;
}
num[high] = num[low];
}
num[low] = temp; //將保存的基準值替換到相應位置
return low; //返回中軸所在位置
}
選擇排序
思想:在待排序的數中,選出一個最小的數與第一個位置的數交換,然后在剩下的數中找到第二小的數與第二個位置的數交換,如此反復。
public static void selectSort(int[] num) {
for(int i = 0; i < num.length; i++) {
int min = i;
for(int j = i+1; j < num.length; j++){
if (num[j] < num[min]) {
min = j;
}
}
if (i != min) {
swap(num,i,min);
}
}
}
優化思路:每次遍歷都找出一個最大值以及最小值
堆排序(選擇排序的升級)
思路:構造一個最大堆,將根結點的值與最后一個葉節點交換,然后排除該葉節點后再次構造最大堆。
for(int i=0; i<num.length-1; i++) {
//循環建立最大堆
buildMaxHeap(num, num.length-1-i);
//交換根節點和最大堆最后一個葉節點
swap(num, 0, num.length-1-i);
}
public static void buildMaxHeap(int[] num, int lastIndex) {
//從lastIndex節點的父節點開始循環
for(int i = (lastIndex-1) /2; i>=0; i--) {
//保存正在判斷的節點
int temp = i;
//當前節點的子節點存在時
while( temp*2+1 <= lastIndex) {
//當前節點的左節點
int biggerIndex = 2*temp+1;
if (biggerIndex < lastIndex) {
//biggerIndex指向當前節點左右孩子節點中較大值的索引
if (num[biggerIndex] < num[biggerIndex+1]) {
biggerIndex++;
}
}
//當前節點小于子女節點中較大值時
if (num[temp] < num[biggerIndex]) {
//交換位置
swap(num,temp,biggerIndex);
//開啟while的下一次循環,保證temp節點的值大于左右孩子節點的值
temp = biggerIndex;
}else {
break;
}
}
}
}
直接插入排序
思路:將一個記錄插入到已經排好序的有序表中,從而得到一個新的、記錄數加1的有序表。
public static void insertSort(int[] num) {
int i,j;
for( i = 0; i< num.length; i++) {
int temp = num[i];
for( j = i; j > 0 && temp < num[j-1]; j-- ) {
//temp小于前面的值,則前面的數右移
num[j] = num[j-1];
}
num[j] = temp;
}
}
希爾排序(直接插入排序升級版)
思路:將待排序的數組按一定步長進行分組,并在每個分組中應用直接插入排序算法,然后縮短步長再次進行分組,直到步長為0為止。
public static void shellSort(int[] num) {
//設置步長
int step = num.length/2;
while(step >= 1) {
for(int i = step; i< num.length; i+=step) {
int temp = num[i];
int j;
//直接插入排序算法
for(j=i; j>0 && num[j-step] > temp; j-=step) {
num[j] = num[j-step];
}
num[j] = temp;
}
//縮小步長直到step為0
step = step/2;
}
}
歸并排序
思路:假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度為1,然后兩兩歸并,得到n/2(向上取整)個長度為2或1的有序序列,再兩兩歸并......,如此反復,直至得到一個長度為n的有序序列為止,這種排序稱為2路歸并排序。
/**
* @param num 待排序數組
* @param n 排序元素個數
*/
public static void mergerSort(int[] num, int n) {
sort(num, 0, n-1);
}
public static void sort(int[] num, int left, int right) {
if (left < right) {
int middle = (right + left)/2;
sort(num, left, middle);
sort(num, middle+1, right);
merge(num, left, middle, right);
}
}
public static void merge(int[] num, int left, int mid, int right) {
//計算數組大小
int n = right-left+1;
//創建臨時數組保存數據
int[] tempArr = new int[n];
//臨時數組下標
int t = 0;
int r = mid+1;
int l = left;
while(l <= mid && r <= right) {
if (num[l] < num[r]) {
tempArr[t++] = num[l++];
}else {
tempArr[t++] = num[r++];
}
}
//剩余元素加入臨時數組
while(l <= mid) {
tempArr[t++] = num[l++];
}
while(r <= right) {
tempArr[t++] = num[r++];
}
//將臨時數組的值復制到原數組中
for(int i = 0; i < t ; i++) {
num[left+i] = tempArr[i];
}
}