爲什麼要學習排序算法?
我相信很多人都會有這樣的疑問,明明有現成的API爲什麼我們還要學這些東西呢?首先我覺得很重要的一點就是學習這些東西有助於我們理解很多東西,比如從時間的複雜度和空間的複雜度去評估一個算法的好壞;但我覺得更重要的還是爲了培養思維上的一些東西,就如同我們學了那麼多年的數學但在實際生活中也許我們只用了其中的百分之一,那我們爲什麼還要學,我想更多的是爲了培養我們數學的思維吧。
排序算法梗概
基本排序總共有8種,5大類
交換排序 | 冒泡排序 | 每一輪排序都選出最大的一個並放到本輪參與排序的數字的最後 |
---|---|---|
快速排序 | 選一個數作爲基準,比他大的放右邊比他小的放左邊,然後大的和小的各作爲新的一組繼續按這種方式進行排序 | |
插入排序 | 直接插入排序 | 從第二個數開始與他前面所有的數進行比較,找到屬於自己的位置直接插入進去 |
希爾排序 | 按步長分組進行直接插入排序,每一輪步長都除以2,直到爲0結束 | |
選擇排序 | 簡單選擇排序 | 每一輪選出最小的數放在每一輪開始輪詢的位置 |
堆排序 | 將數組轉爲大頂堆或者小頂堆數組後交換首元素和末元素,每一輪數組長度減1 | |
歸併排序 | 歸併排序 | 將兩個有序的數組合併成一個新的有序的數組 |
基數排序 | 基數排序 | 每一輪都是比較各個位置上的數,從個位開始一直到最高位 |
冒泡排序
/**
* 冒泡排序法:
* 每一輪都都選出最大的一個,已經比較過的在下一輪不進行比較
* 每一輪的比較邏輯是兩兩比較,如果前一個數大於後一個數就交換位置
*/
public void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int val = array[j];
array[j] = array[j + 1];
array[j + 1] = val;
}
}
}
}
快速排序
/**
* 快速排序:
* 1.選第一個數作爲基準,比他大的放右邊比他小的放左邊
* 2.放的概念是直接覆蓋,覆蓋了哪邊哪邊的指針移動直到下一次覆蓋,交換指針開始移動,直到高位和低位的指針重合
* 3.然後小的作爲一組大的作爲新的一組繼續按照方式一進行排序
*/
public void quickSort(int[] array, int start, int end) {
if (start < end) {
//把數組中的第0個數作爲標準數
int stand = array[start];
//記錄需要排序的下標
int low = start;
int high = end;
//當低位和高位未重合時就需要一直比較
while (low < high) {
//右邊的數字比標準數字大,高位左移
while (low < high && stand < array[high]) {
high--;
}
array[low] = array[high];
//左邊的數字比標準數小
while (low < high && array[low] <= stand) {
low++;
}
array[high] = array[low];
}
//把標準數放到低位
array[low] = stand;
//處理所有小的數字
quickSort(array, 0, low);
//處理所有的大的數字
quickSort(array, low + 1, end);
}
}
直接插入排序
/**
* 直接插入排序:
* 從第二個數起與前面的所有數進行比較,直到找到適合自己的位置然後插入進去
*/
public void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
if (array[i] < array[i - 1]) {
int val = array[i];
int j;
for (j = i - 1; j >= 0 && val < array[j]; j--) {
array[j + 1] = array[j];
}
array[j + 1] = val;
}
}
}
希爾排序
/**
* 希爾排序:
* 將數組爲成兩個小組進行直接插入排序
* 分組的方式是以數組處以2得到一個分組的步長
* 每一輪排序都是在上一個步長的基礎上除以2,直到步長爲0
* 希爾排序比直接插入排序效率高
*/
public void shellSort(int[] array) {
//輪詢步長
for (int d = array.length / 2; d > 0; d /= 2) {
//遍歷所有元素
for (int i = d; i < array.length; i++) {
//遍歷本組所有的元素
for (int j = i - d; j >= 0; j -= d) {
//如果當前元素大於加上步長後的那個元素
if (array[j] > array[j + d]) {
int val = array[j];
array[j] = array[j + d];
array[j + d] = val;
}
}
}
}
}
簡單選擇排序
/**
* 簡單選擇排序:
* 每一輪選出數組中的最小的一個數把它更新在相應的位置
* 第N輪就更新第N位
*/
public void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int min = i;
for (int j = i + 1; j < array.length; j++) {
if (array[min] < array[j]) {
//記錄這一輪排序中最小元素的下標
min = j;
}
}
if (min != i) {
int val = array[i];
array[i] = array[min];
array[min] = val;
}
}
}
堆排序
/**
* 堆排序:
* 堆:數據結構中的一種,可以把他看作一個二叉樹的數組對象
* 根節點最大的叫大頂堆,根節點最小的叫小頂堆
* 升序排序:用大頂堆
* 降序排序:用小頂堆
*/
public void heapSort(int[] array) {
//從最後一個非葉子節點開始,即最後一個節點的父節點
int start = (array.length - 1) / 2;
//將數組轉換成大頂堆
for (int i = start; i >= 0; i--) {
maxheap(array, array.length, i);
}
System.out.println("第1次 " + Arrays.toString(array));
int n = 2;
//將數組轉換成大頂堆後需要交換第一個元素和最後一個元素的位置
for (int i = array.length - 1; i > 0; i--) {
int temp = array[0];
array[0] = array[i];
array[i] = temp;
//第一次的時候已經將整個數組轉換成了大頂堆,這裏交換了以後可能會影響之前已經排好的,所以從這個位置開始要繼續調整
maxheap(array, i, 0);
System.out.println("第" + (n++) + "次 " + Arrays.toString(array));
}
}
/**
* 將一個數組變成一個大頂堆
*
* @param array
* @param size
* @param index
*/
public void maxheap(int[] array, int size, int index) {
//左子節點
int left = 2 * index + 1;
//右子節點
int right = 2 * index + 2;
int max = index;
//和兩個子節點比較找出最大的那個節點
if (left < size && array[left] > array[max]) {
max = left;
}
if (right < size && array[right] > array[max]) {
max = right;
}
//如果子節點大則交換位置
if (max != index) {
int temp = array[index];
array[index] = array[max];
array[max] = temp;
//交換位置後可能會影響array[max]與他的子節點不能構成大頂堆的格式,所以那個位置要繼續調整
maxheap(array, size, max);
}
}
歸併排序
/**
* 歸併邏輯:
* 兩個有序的數組合併成一個有序的數組,注意是有序的
*/
public void merge(int[] array, int low, int middle, int high) {
int[] temp = new int[high - low + 1];
//用於記錄第一個數組中的下標
int i = low;
//用於記錄第二個數組中的下標
int j = middle + 1;
//用於記錄在臨時數組中存放的下標
int index = 0;
//遍歷兩個數組取出小的數字放入臨時數組
while (i <= middle && j <= high) {
if (array[i] <= array[j]) {
temp[index] = array[i];
i++;
} else {
temp[index] = array[j];
j++;
}
index++;
}
while (i <= middle) {
temp[index] = array[i];
i++;
index++;
}
while (j <= high) {
temp[index] = array[j];
j++;
index++;
}
//把臨時數組重新存入原數組
for (int k = 0; k < temp.length; k++) {
array[low + k] = temp[k];
}
}
/**
* 歸併排序
* 遞歸的方式
*/
public void mergeSort(int[] array, int low, int high) {
int middle = (low + high) / 2;
if (low < high) {
//處理左邊,爲了讓左邊有序
mergeSort(array, low, middle);
//處理右邊,爲了讓右邊有序
mergeSort(array, middle + 1, high);
//歸併
merge(array, low, middle, high);
}
}
基數排序
/**
* 基數排序:
* 每一次輪循都是比較某個位置上的數進行排序
* 第一次個位比第二次十位比,依次類推
*/
public void RedixSort(int[] array) {
//存數組中最大的數
int max = Integer.MIN_VALUE;
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
//最大的數字是幾位數
int maxLength = (max + "").length();
//用於臨時存儲每次比較時放在不同位置的數組
int[][] temp = new int[10][array.length];
//用於存放temp數組中每一行存放的數據的個數
int[] counts = new int[10];
//根據最大長度決定比較的次數
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
//把數字存進temp
for (int j = 0; j < array.length; j++) {
//取出每一位上的數並進行存儲和統計個數
int remainder = array[j] / n % 10;
temp[remainder][counts[remainder]] = array[j];
counts[remainder] = counts[remainder] + 1;
}
int index = 0;
//把數字從temp取出來並更新原數組,相當於按位數進行了一輪排序
for (int k = 0; k < counts.length; k++) {
if (counts[k] != 0) {
for (int l = 0; l < counts[k]; l++) {
array[index] = temp[k][l];
index++;
}
//一輪比較後清楚緩存數據
counts[k] = 0;
}
}
}
}
/**
* 基礎排序,隊列實現
*/
public void RedixQueueSort(int[] array) {
//存數組中最大的數
int max = Integer.MIN_VALUE;
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
//最大的數字是幾位數
int maxLength = (max + "").length();
//每一次放數據時放到的隊列
Queue<Integer>[] temp = new Queue[10];
for(int i=0;i<temp.length;i++){
temp[i] = new LinkedList<>();
}
//根據最大長度決定比較的次數
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
//把數字存進temp
for (int j = 0; j < array.length; j++) {
//取出每一位上的數並進行存儲和統計個數
int remainder = array[j] / n % 10;
temp[remainder].add(array[j]);
}
int index = 0;
//把數字從temp取出來並更新原數組,相當於按位數進行了一輪排序
for (int k = 0; k < temp.length; k++) {
while (!temp[k].isEmpty()) {
array[index] = temp[k].poll();
index++;
}
}
}
}
綜上就是基本的八種排序算法的java實現了,我們在學習的時候更多的應該是體會其思想而不是更多的把心思放在實現上