前言
本文我們將以java代碼實(shí)現(xiàn)十大經(jīng)典排序算法,包括冒泡排序、選擇排序、插入排序、希爾排序、歸并排序、快速排序、堆排序、計(jì)數(shù)排序、桶排序、基數(shù)排序。
排序算法相關(guān)的時(shí)間復(fù)雜度和空間復(fù)雜度

1、冒泡排序(Bubble Sort)
算法描述
- 比較相鄰的元素。如果第一個(gè)比第二個(gè)大,就交換它們兩個(gè);
- 對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對,這樣在最后的元素應(yīng)該會(huì)是最大的數(shù);
- 針對所有的元素重復(fù)以上的步驟,除了最后一個(gè);
- 重復(fù)步驟1~3,直到排序完成。
代碼實(shí)現(xiàn)
public void sort(int[] nums) {
for(int i = 0;i<nums.length -1;i++){
for (int j = 0; j < nums.length - 1-i; j++) {
int c;
if (nums[j] > nums[j + 1]) {
c = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = c;
}
}
}
}
這里有個(gè)問題,冒泡排序的時(shí)間復(fù)雜度最好是n,但上述代碼不管如何時(shí)間復(fù)雜度都是n2,所以有了以下改進(jìn)算法。
public void bubbleSort(int arr[]) {
boolean didSwap;
for(int i = 0, len = arr.length; i < len - 1; i++) {
didSwap = false;
for(int j = 0; j < len - i - 1; j++) {
if(arr[j + 1] < arr[j]) {
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
didSwap = true;
}
}
if(didSwap == false)
return;
}
}
2、選擇排序(Selection Sort)
算法描述
選擇排序一種簡單直觀的排序算法。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續(xù)尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
代碼實(shí)現(xiàn)
public int[] sort(int[] nums) {
int len = nums.length;
int temp;
for(int i = 0;i<len;i++){
for(int j =i;j<len;j++){
if(nums[i]>nums[j]){
temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
}
3、插入排序(Insertion Sort)
算法描述
- 從第一個(gè)元素開始,該元素可以認(rèn)為已經(jīng)被排序;
- 取下一元素,在已經(jīng)排序的元素序列中從后向前掃描;
- 如果該元素(已排序)大于新元素,將該元素移到下一位置;
- 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置;
- 將新元素插入到該位置后;
- 重復(fù)步驟2~5。
代碼實(shí)現(xiàn)
public void sort(int[] nums) {
int len = nums.length;
int preIndex,current;
for(int i = 0;i<len;i++){
preIndex = i - 1;
current = nums[i];
while(preIndex >= 0 && nums[preIndex] > current){
nums[preIndex + 1] = nums[preIndex];
preIndex--;
}
nums[preIndex + 1] = current;
}
}
4、希爾排序(Shell Sort)
算法排序
希爾排序(Shell's Sort)是插入排序)的一種又稱“縮小增量排序”,是直接插入排序算法的一種更高效的改進(jìn)版本。希爾排序是非穩(wěn)定排序算法。
- 選擇一個(gè)增量序列t1,t2,…,tk,其中ti>tj,tk=1;
- 按增量序列個(gè)數(shù)k,對序列進(jìn)行k 趟排序;
- 每趟排序,根據(jù)對應(yīng)的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進(jìn)行直接插入排序。僅增量因子為1 時(shí),整個(gè)序列作為一個(gè)表來處理,表長度即為整個(gè)序列的長度。
代碼實(shí)現(xiàn)
public void sort(int[] nums) {
int len = nums.length;
for (int gap = (int) Math.floor(len / 2); gap > 0; gap = (int) Math.floor(gap / 2)) {
// 注意:這里和動(dòng)圖演示的不一樣,動(dòng)圖是分組執(zhí)行,實(shí)際操作是多個(gè)分組交替執(zhí)行
for (int i = gap; i < len; i++) {
int j = i;
int current = nums[i];
while (j - gap >= 0 && current < nums[j - gap]) {
nums[j] = nums[j - gap];
j = j - gap;
}
nums[j] = current;
}
}
}
5、歸并排序(Merge Sort)
算法描述
歸并排序(MERGE-SORT)是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個(gè)非常典型的應(yīng)用。將已有序的子序列合并,得到完全有序的序列;即先使每個(gè)子序列有序,再使子序列段間有序。若將兩個(gè)有序表合并成一個(gè)有序表,稱為二路歸并。歸并排序是一種穩(wěn)定的排序方法。實(shí)際上就是用的遞歸的思想。
- 把長度為n的輸入序列分成兩個(gè)長度為n/2的子序列;
- 對這兩個(gè)子序列分別采用歸并排序;
- 將兩個(gè)排序好的子序列合并成一個(gè)最終的排序序列。
代碼實(shí)現(xiàn)
public int[] sort(int[] nums) {
int len = nums.length;
if(len<2){
return nums;
}
int middle = (int) Math.floor(len/2);
int[] left = new int[middle],right = new int[len-middle];
System.arraycopy(nums, 0, left, 0, middle);
System.arraycopy(nums, middle, right, 0, len-middle);
return merge(sort(left),sort(right));
}
private int[] merge(int[] left, int[] right) {
// TODO Auto-generated method stub
int[] result = new int[left.length+right.length];
int i = 0, j = 0, k = 0;
while ((left.length - j) > 0 && (right.length - k) > 0) {
if (left[j] <= right[k]) {
result[i] = left[j];
j++;
} else {
result[i] = right[k];
k++;
}
i++;
}
while (j < left.length){
System.arraycopy(left, j, result, i, left.length-j);
j++;
i++;
}
while (k < right.length){
System.arraycopy(right, k, result, i, right.length-k);
k++;
i++;
}
return result;
}
6、快速排序(Quick Sort)
算法描述
快速排序(Quicksort)是對冒泡排序的一種改進(jìn)。
- 首先選擇一個(gè)分界值,通過該分界值將數(shù)組分成左右兩部分。
- 重新對數(shù)組進(jìn)行排序,比分界值小的集中到數(shù)組左邊,比分界值大的位于數(shù)組右邊。
- 然后左右兩邊數(shù)組再次進(jìn)行快速排序。
- 最后重復(fù)上述步驟,實(shí)現(xiàn)遞歸。
代碼實(shí)現(xiàn)
public void sort(int[] arr,int low, int high) {
int i, j ,temp,snap;
if(low > high){
return;
}
i = low;
j = high;
// 基準(zhǔn)位置
temp = arr[low];
while (i<j) {
while (temp<=arr[j] && i<j) {
j--;
}
while (temp>=arr[i] && i<j) {
i++;
}
if (i<j) {
// 交換滿足條件的 i j位置的值
snap = arr[i];
arr[i] = arr[j];
arr[j] = snap;
}
}
// 交換基準(zhǔn)位置的值
arr[low] = arr[i];
arr[i] = temp;
// 遞歸調(diào)用, 左右
sort(arr,low,j-1);
sort(arr,j+1,high);
}
7、堆排序(Heap Sort)
算法描述
堆排序是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。堆是一個(gè)近似完全二叉樹的結(jié)構(gòu),并同時(shí)滿足堆積的性質(zhì):即子結(jié)點(diǎn)的鍵值或索引總是小于(或者大于)它的父節(jié)點(diǎn)。
- 將初始待排序關(guān)鍵字序列(R1,R2….Rn)構(gòu)建成大頂堆
- 將堆頂元素R[1]與最后一個(gè)元素R[n]交換。
- 由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對當(dāng)前無序區(qū)(R1,R2,……Rn-1)調(diào)整為新堆,然后再次將R[1]與無序區(qū)最后一個(gè)元素交換,得到新的無序區(qū)(R1,R2….Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過程直到有序區(qū)的元素個(gè)數(shù)為n-1,則整個(gè)排序過程完成。
代碼實(shí)現(xiàn)
public static void sort(int[] array) {
if (array == null || array.length <= 1) {
return;
}
buildMaxHeap(array);
int temp;
for(int arg:array){
System.out.print(arg+" ");
}
for (int i = array.length - 1; i >= 1; i--) {
temp = array[0];
array[0] = array[i];
array[i] = temp;
maxHeap(array, i, 0);
System.out.println(" ");
System.out.println("-------"+i+"-------");
for(int arg:array){
System.out.print(arg+" ");
}
}
}
private static void buildMaxHeap(int[] array) {
if (array == null || array.length <= 1) {
return;
}
int half = array.length / 2;
for (int i = half; i >= 0; i--) {
maxHeap(array, array.length, i);
}
}
private static void maxHeap(int[] array, int heapSize, int index) {
int left = index * 2 + 1;
int right = index * 2 + 2;
int largest = index;
if (left < heapSize && array[left] > array[index]) {
largest = left;
}
if (right < heapSize && array[right] > array[largest]) {
largest = right;
}
if (index != largest) {
int temp;
temp = array[index];
array[index] = array[largest];
array[largest] = temp;
maxHeap(array, heapSize, largest);
}
}
8、計(jì)數(shù)排序(Counting Sort)
算法描述
計(jì)數(shù)排序是一個(gè)非基于比較的排序算法,犧牲空間換取時(shí)間。
- 得到數(shù)組的最大和最小元素。
- 統(tǒng)計(jì)出每個(gè)數(shù)據(jù)為i的值出現(xiàn)的次數(shù),將次數(shù)存入數(shù)組C中。
- 以數(shù)組C為模板重新填充目標(biāo)數(shù)組。
代碼實(shí)現(xiàn)
public void sort(int[] nums) {
int min = nums[0],max = nums[0];
for(int num: nums){
max = Math.max(max, num);
min = Math.min(min, num);
}
int[] counts = new int[max - min+1];
for(int num: nums){
counts[num - min]++;
}
int i = 0,j = 0;
for(int count:counts){
while (count > 0) {
nums[i] = j + min;
count --;
i ++;
}
j++;
}
}
9、桶排序(Bucket Sort)
算法描述
將數(shù)組分到有限數(shù)量的桶子里。每個(gè)桶子再個(gè)別排序(有可能再使用別的排序算法)。
- 設(shè)置一個(gè)定量的鏈表當(dāng)作空桶。
- 遍歷輸入數(shù)據(jù),并且把數(shù)據(jù)一個(gè)一個(gè)放到對應(yīng)的桶里去。
- 對每個(gè)不是空的桶進(jìn)行排序。
- 從不是空的桶里把排好序的數(shù)據(jù)拼接起來。
代碼實(shí)現(xiàn)
public void sort(int[] nums) {
int min = nums[0],max = nums[0];
for(int num: nums){
max = Math.max(max, num);
min = Math.min(min, num);
}
int bucketBottom = (int) Math.floor((float)min/10);
int bucketTop = (int) Math.ceil((float)max/10);
ArrayList<ArrayList<Integer>> bucketsList = new ArrayList<ArrayList<Integer>>();
for(int i = 0;i<(bucketTop-bucketBottom);i++){
bucketsList.add(new ArrayList<Integer>());
}
for(int num: nums){
int index = getBucketIndex(num);
insertBucket(bucketsList.get(index),num);
}
int index = 0;
for (ArrayList<Integer> list : bucketsList) {
for(int data: list){
nums[index++] = data;
}
}
}
// 將數(shù)據(jù)放入具體的桶內(nèi)
private void insertBucket(ArrayList<Integer> arrayList, int num) {
boolean insertFlag = true;
ListIterator<Integer> it = arrayList.listIterator();
while (it.hasNext()) {
if (num <= it.next()) {
it.previous(); // 把迭代器的位置偏移回上一個(gè)位置
it.add(num); // 把數(shù)據(jù)插入到迭代器的當(dāng)前位置
insertFlag = false;
break;
}
}
// 否則把數(shù)據(jù)插入到鏈表末端
if (insertFlag) {
arrayList.add(num);
}
}
// 判斷放入哪個(gè)桶內(nèi)的方法
private int getBucketIndex(int data){
return (int) Math.floor(data/10);
}
10、基數(shù)排序(Radix Sort)
算法描述
基數(shù)排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。
- 取得數(shù)組中的最大數(shù),并取得位數(shù)。
- 使用鏈表對按照當(dāng)前位數(shù)大小存儲(chǔ)數(shù)據(jù),然后重新寫入數(shù)組。
- 根據(jù)位數(shù)的不同重復(fù)步驟2。
代碼實(shí)現(xiàn)
public void sort(int[] nums) {
int max = 0;
int exp = 1;
for (int num:nums){
max = Math.max(max,num);
}
while(max/Math.pow(10,exp) > 1){
exp++;
}
//存儲(chǔ)待排元素
ArrayList<ArrayList<Integer>> radixList = new ArrayList<ArrayList<Integer>>();
for(int j = 0;j<10;j++){
ArrayList<Integer> list = new ArrayList<Integer>();
radixList.add(list);
}
for(int i = 0;i<=exp;i++){
for(int num:nums){
int positionValue = getPositionValue(num, i);
radixList.get(positionValue).add(num);
}
int index = 0;
for(ArrayList<Integer> list:radixList){
for(int value: list){
nums[index] = value;
index++;
}
list.clear();
}
}
}
// 獲取當(dāng)前位數(shù)對應(yīng)的值
private int getPositionValue(int num,int exp){
return (int) (num % Math.pow(10, exp)/Math.pow(10, exp-1));
}