常用排序算法
瞭解數據結構,對於我們開發者而言是非常必要的,而排序算法更是我們必須要了解和掌握的知識,在這裏我總結了八種常用的排序算法以及自己學習排序算法的一些心得,希望對讀者們有一定的幫助。(注:代碼實現部分我是用java代碼展示的)。廢話不多說,下面就一起學習吧。
一、交換排序
交換排序中主要有:
1、冒泡排序;
2、快熟排序
1、冒泡排序
動態排序圖如下:
冒泡排序是我們接觸開發的時候最先了解的一種排序算法,這種排序的思想就是從元素的起始位置開始,倆倆相鄰的兩組元素做對比,如果是升序排序,那麼前者比後者大的話就交換順序,直到元素變成有序爲止,這種排序的規則有點像沸騰的水中的水泡從下向上的過程,因此我們叫這種排序算法爲冒泡排序,排序算法是一種穩定的算法,它的代碼實現也比較簡單,就是兩個for循環實現,接下來我們來看代碼:
/**
* 冒泡排序
* @param array
*/
private static void bubbleSort(int[] array){
if (array == null){
return;
}
//第一個循環控制比較的輪數
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 temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
2、快速排序
它的同態排序圖如下:
快速排序的排序思想就是隨機選擇我們要排序元素中的一個元素,一般我們默認選擇起始位置的那個元素,排序規則就是:我們將選擇的這個元素作爲一個基準數,如果比這個元素大的,我們就往右邊放,比基準數小的,就往左邊放,當將所有元素遍歷完之後,就分爲了兩個區,將左邊的數和右邊的數按照排序規則繼續遞歸排序,就得到了一個有序的數組
代碼實現如下:
private static void quickSort(int[] arry, int start, int end) {
if (start >= end) {
return;
}
//通常將數組的第一個元素作爲標準數
int startNumber = arry[start];
//記錄排序的下標
int low = start;
int high = end;
//循環找比標準數大的數,比標準數小的數
while (low < high) {
while (low < high && startNumber <= arry[high]) {
high--;
}
//使用右邊的數替換左邊的數
arry[low] = arry[high];
//如果左邊的數比標準數小
while (low < high && arry[low] <= startNumber) {
low++;
}
arry[high] = arry[low];
}
//把標準數賦值給低位所在的元素
arry[high] = startNumber;
//遞歸所有小的數字
quickSort(arry, start, low);
//遞歸所有大的數字
quickSort(arry, low + 1, end);
}
二、插入排序
插入排序分爲:
1、直接插入排序;
2、希爾排序
1、直接插入排序
插入排序動態展示圖:
插入排序的算法原理很好理解,就是我們認定前面的元素都已經是有序的元素,後面的元素跟相鄰前面的元素比較,如果比前者小,那麼向前插入,並繼續和前一個比較,直到找到正確的位置爲止。這種排序和冒泡排序有相似之處,只不過冒泡是向後面比較,插入是向前面比較。
代碼實現如下:
private static void insertSort(int[] arra) {
//直接插入排序默認前面的值已經是排好序的
for (int i = 1; i <arra.length ; i++) {
//存儲當前遍歷的數字
int temp = arra[i];
//如果前面的一個數比當前的小則進入排序
if (arra[i]<arra[i-1]){
int j=0;
//循環排序
for (j = i-1; j <i&&temp<arra[j] ; j--) {
arra[j+1] = arra[j];
}
//將臨時變量的值還給被覆蓋的值
arra[j+1] = temp;
}
}
}
2、希爾排序
動態展示圖:
希爾排序其實是對我們的直接插入排序做了一點改進,在直接插入排序的思想上加了一個步長概念,先按照元素數量/2作爲步長做一次插入排序,下一次的循環又按照上一次的步長/2再進行插入排序,直到步長爲0結束排序。
代碼實現:
private static void shellSort(int[] arra) {
//希爾排序,遍歷所有步長,直到步長爲0爲止
for (int d = arra.length/2;d>0; d/=2) {
//遍歷所有元素
for (int i = d; i <arra.length ; i++) {
//按照步長遍歷本組中的所有元素
for (int j = i-d; j >=0 ; j-=d) {
//如果當前元素大於加步長後的那個元素
if(arra[j]>arra[j+d]){
int temp = arra[j];
arra[j] = arra[j+d];
arra[j+d] = temp;
}
}
}
}
}
三、選擇排序
選擇排序分爲:
1、簡單選擇排序
2、堆排序
1、簡單選擇排序
動態展示圖:
選擇排序的算法思想比較簡單,就是遍歷元素,將最小的取出後,繼續遍歷選擇最小的,直到取完元素爲止,依次取出的元素便是排序後的元素
代碼實現:
private static void simpleChooseSort(int[] arra) {
//簡單選擇排序
for (int i = 0; i <arra.length; i++) {
int minIndex = i;
//遍歷後面的元素,扎到最小的那個元素的下標
for (int j = i+1; j <arra.length ; j++) {
//循環記錄數組中最小的元素
if (arra[minIndex]>arra[j]){
minIndex = j;
}
}
//賦值最小的元素Heapsort
if (arra[minIndex] != arra[i]){
int tmp = arra[i];
arra[i] = arra[minIndex];
arra[minIndex] = tmp;
}
}
}
1、堆排序
動態展示圖:
堆排序的算法思想就是將我們的元素先轉換成大頂堆,(如果對大頂堆不熟悉,可以找度娘學習一波),轉換成大頂堆之後,取第一個元素(即最大的元素),繼續將取出元素後的二叉樹轉成大頂堆,重複以上邏輯,直到元素取完爲止,我們可以獲取一個降序的元素,反遍歷一次就得到升序的元素
代碼實現如下:
public class Heapsort {
public static void main(String[] args) {
int[] arra = new int[]{2, 7, 4, 10, 4, 8, 5};
System.out.println(Arrays.toString(arra));
//開始位置爲最後一個非葉子節點
int start = (arra.length-1)/2;
//結束位置,數組的長度減一
for (int i = start; i >=0 ; i--) {
maxHeap(arra,arra.length,i);
}
//轉成大頂堆之後,將大頂堆第一個元素放進數組的最後一個位置,再將前面的數又轉換成大頂堆
for (int i = arra.length -1; i >0; i--) {
int temp = arra[0];
arra[0] = arra[i];
arra[i] = temp;
maxHeap(arra,i,0);
}
System.out.println(Arrays.toString(arra));
}
public static void maxHeap(int[] arra, int size, int index) {
//通過索引找到兩個節點
//左子節點
int leftNode = index * 2 + 1;
//右子節點
int rightNode = index * 2 + 2;
int max = index;
//和兩個子節點對比,找出最大的節點
if (leftNode<size&&arra[leftNode] > arra[max]) {
max = leftNode;
}
if (rightNode<size&&arra[rightNode] > arra[max]) {
max = rightNode;
}
if (max != index) {
int temp = arra[index];
arra[index] = arra[max];
arra[max] = temp;
//交換位置後破快了之前的順序,需要重新排序
maxHeap(arra,size,max);
}
}
}
四 、歸併排序
動態排序圖:
歸併排序採用了遞歸的思想:將元素拆分爲最小的兩個數組,將兩個數組歸併排序(即升序合併到一個數組中),然後合併後的數組繼續跟另外一個合併的數組繼續比較合併,遞歸操作,直到排序完成
代碼實現如下:
import java.util.Arrays;
/**
* 歸併排序
*/
public class MergeSort {
public static void main(String[] args) {
int[] arra = new int[]{212,37,54,10,4,688,5,87,89,23,43,51};
System.out.println(Arrays.toString(arra));
mergeSort(arra,0,arra.length-1);
System.out.println(Arrays.toString(arra));
}
private static void mergeSort(int[] arra,int low,int heigh) {
if (low>=heigh){
return;
}
int middel = (low+heigh)/2;
//遞歸排序左邊的數組
mergeSort(arra,low,middel);
//遞歸排序右邊的數組
mergeSort(arra,middel+1,heigh);
//合併數組
merge(arra,low,middel,heigh);
}
/**
*
* @param arra
* @param low
* @param middle
* @param heigh
*/
private static void merge(int[] arra,int low,int middle,int heigh) {
//存放臨時數組
int[] temp = new int[heigh-low+1];
//記錄數組的下標
int indexLow = low;
//記錄右邊數組需要遍歷的下標
int indexHeight = middle+1;
//記錄臨時數組的下標
int tempIndex = 0;
while (indexLow<=middle&&indexHeight<=heigh){
//第一個數據更小
if (arra[indexLow]<=arra[indexHeight]){
//把小的數組放入臨時數組中
temp[tempIndex] = arra[indexLow];
indexLow++;
}else {
temp[tempIndex] = arra[indexHeight];
indexHeight++;
}
tempIndex++;
}
//處理多餘的數
while (indexLow<=middle){
temp[tempIndex] = arra[indexLow];
tempIndex++;
indexLow++;
}
while (indexHeight<=heigh){
temp[tempIndex] = arra[indexHeight];
indexHeight++;
tempIndex++;
}
//將臨時的數組存入原數組
for (int i = 0; i < temp.length; i++) {
arra[i+low] = temp[i];
}
}
}
五、基數排序
動態圖如下:
基數排序根據元素的位數進行排序,申請0-9的隊列空間,第一次按照個數遍歷,將元素各位相同的數有序的放在對應標記的隊列中,第一次遍歷結束後,從0-9的隊列中按照存入的順序依次取出,第二次按照十位遍歷,執行相同的邏輯,直到遍歷到最高位,循環結束。
代碼實現如下:
1、數組版本
private static void radxiSort(int[] arra) {
//存數組中最大的數
int max = Integer.MIN_VALUE;
for (int arr:arra) {
if (arr>max){
max = arr;
}
}
//計算最大的數字是幾位數
int maxLength = String.valueOf(max).length();
//根據最大的長度決定比較次數
int bucket[][] = new int[10][arra.length];
//記錄存放的個數
int[] counts = new int[10];
for (int i = 0,n=1; i < maxLength; i++,n*=10) {
//把每一個數字求餘
for (int j = 0; j < arra.length; j++) {
int remainder = arra[j]/n%10;
bucket[remainder][counts[remainder]] = arra[j];
//記錄數據
counts[remainder]++;
}
//記錄取元素的下標
int index = 0;
//把數字取出來
for (int j = 0; j <counts.length; j++) {
if (counts[j]!=0){
//循環取出元素
for (int k = 0; k < counts[j]; k++) {
arra[index] = bucket[j][k];
index++;
}
}
counts[j] = 0;
}
}
}
2、隊列版本
private static void radxiSort(int[] arra) {
//存數組中最大的數
int max = Integer.MIN_VALUE;
for (int arr:arra) {
if (arr>max){
max = arr;
}
}
//計算最大的數字是幾位數
int maxLength = String.valueOf(max).length();
Queue<Integer>[] queue = new Queue[10];
for (int i = 0; i < 10; i++) {
queue[i] = new ArrayDeque();
}
//記錄存放的個數
int[] counts = new int[10];
for (int i = 0,n=1; i < maxLength; i++,n*=10) {
//把每一個數字求餘
for (int j = 0; j < arra.length; j++) {
int remainder = arra[j]/n%10;
queue[remainder].add(arra[j]);
counts[remainder]++;
}
//將隊列中的數按照順序放入原來的數組中去
int index = 0;
for (Queue<Integer> queue1:queue) {
while (!queue1.isEmpty()){
arra[index++] = queue1.poll();
}
}
}
}