Java數(shù)據(jù)結(jié)構(gòu)之基于比較的排序算法基本原理及具體實現(xiàn)
基于比較的排序算法基本原理及Java實現(xiàn)
1. 七大基于比較的排序-總覽
1.1常見基于比較的排序分類

1.2時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性。
- 穩(wěn)定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
- 不穩(wěn)定:如果a原本在b的前面,而a=b,排序之后 a 可能會出現(xiàn)在 b 的后面。
2.直接插入排序
2.1 直接插入排序的基本思想
直接插入排序的基本思想,就是每次選取一個待排序元素,按照一定規(guī)定插入到前面已經(jīng)排好序的一組元素的適當(dāng)位置。當(dāng)每個元素都插入完畢,整個序列已經(jīng)有序。
當(dāng)插入第i(i >= 1)時,前面的V[0],V[1],……,V[i-1]已經(jīng)排好序。這時,用V[I]的排序碼與V[i-1],V[i-2],…的排序碼順序進行比較,找到插入位置即將V[i]插入,原來位置上的元素向后順移。
2.2 直接插入排序動畫演示

2.3 代碼示例
public static void insertSort(int[] arr){
for(int i=1;i<arr.length;i++){//從第二個元素開始,遍歷整個數(shù)組
int j=i-1;//i之前的序列 已經(jīng)有序
int temp=arr[i];
for(;j>=0;j--){//此循環(huán)用于尋找插入位置
if(arr[j]>temp){//此時逐個向后移動元素
arr[j+1]=arr[j];
}else break;//找到插入位置 直接break
}
arr[j+1]=temp;//直接插入
}
}
2.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最壞情況:數(shù)組整體逆序O(n^2);
- 時間復(fù)雜度最好情況:數(shù)組已經(jīng)有序O(n^2);
- 空間復(fù)雜度:O(1);
- 穩(wěn)定性:穩(wěn)定;
3. 希爾排序
3.1 算法思想
希爾排序是特殊的插入排序,直接插入排序每次插入前的遍歷步長為1,而希爾排序是將待排序列分為若干個子序列,對這些子序列分別進行直接插入排序,當(dāng)每個子序列長度為1時,再進行一次直接插入排序時,結(jié)果一定是有序的。常見的劃分子序列的方法有:初始步長(兩個子序列相應(yīng)元素相差的距離)為要排的數(shù)的一半,之后每執(zhí)行一次步長折半。
3.2 圖片演示

3.3 代碼示例
public static void hell(int[] arr,int gap){//當(dāng)gap=1時 其實就是直接插入排序
for(int i=gap;i<arr.length;i++){
int j=i-gap;
int temp=arr[i];
for(;j>=0;j-=gap){
if(arr[j]>temp){
arr[j+gap]=arr[j];
}else break;
}
arr[j+gap]=temp;
}
}
public static void hellSort(int[] arr){
int gap=arr.length;
while(gap>1){
gap=gap/2+1;
hell(arr,gap);
}
}
3.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最好:O(n);
- 時間復(fù)雜度最壞:O(n^1.5);
- 時間復(fù)雜度平均:O(n^1.3);
- 空間復(fù)雜度:O(1);
- 穩(wěn)定性:不穩(wěn)定;
4.選擇排序
4.1 算法思想
選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工作原理:首先在未排序序列中找到最?。ù螅┰?,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續(xù)尋找最?。ù螅┰?,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
4.2 動畫演示

4.3 代碼示例
public static void selectSort(int[] arr){
for(int i=0;i<arr.length;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[j]<arr[i]){
int temp=arr[j];
arr[j]=arr[i];
arr[i]=temp;
}
}
}
}
4.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最好:O(n);
- 時間復(fù)雜度最壞:O(n^2;
- 時間復(fù)雜度平均:O(n^2);
- 空間復(fù)雜度:O(1);
- 穩(wěn)定性:不穩(wěn)定;
5.堆排序
5.1 算法思想
堆
堆是具有以下性質(zhì)的完全二叉樹:每個結(jié)點的值都大于或等于其左右孩子結(jié)點的值,稱為大頂堆;或者每個結(jié)點的值都小于或等于其左右孩子結(jié)點的值,稱為小頂堆。如下圖:
- 將初始待排序關(guān)鍵字序列(R1,R2….Rn)構(gòu)建成大頂堆,此堆為初始的無序區(qū);
- 將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(qū)(R1,R2,……Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2…n-1]<=R[n];
- 由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對當(dāng)前無序區(qū)(R1,R2,……Rn-1)調(diào)整為新堆,然后再次將R[1]與無序區(qū)最后一個元素交換,得到新的無序區(qū)(R1,R2….Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過程直到有序區(qū)的元素個數(shù)為n-1,則整個排序過程完成。

5.2 動畫演示

5.3 代碼示例
public static void heapSort(int[] arr){//排序方法
creatHeap(arr);
int end=arr.length-1;
//因為是大根堆,所以根節(jié)點值為最大值,根節(jié)點與最后一個節(jié)點值交換 輸出并刪除此時最后一個節(jié)點,然后向下調(diào)整根節(jié)點
while(end>0){
int temp=arr[0];
arr[0]=arr[end];
arr[end]=temp;
shiftDown(arr,0,end);
end--;
}
}
public static void creatHeap(int[] arr){//建堆
for(int parent=(arr.length-1-1)/2;parent>=0;parent--){
shiftDown(arr,parent,arr.length);
}
}
public static void shiftDown(int[]arr,int root,int len){//向下調(diào)整操作
int parent=root;
int child=parent*2+1;
while(child<len){
if(child+1<len&&arr[child]<arr[child+1]){//待調(diào)整節(jié)點右子樹大于左子樹
child++;
}
if(arr[child]>arr[parent]){//由于是大根堆,如果兒子節(jié)點值大于父親節(jié)點就交換
int temp=arr[parent];
arr[parent]=arr[child];
arr[child]=temp;
parent=child;//繼續(xù)向下調(diào)整,防止調(diào)整后不滿足大根堆的條件
child=parent*2+1;
}else break;
}
}
5.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最好:O(nlogn);
- 時間復(fù)雜度最壞:O(nlogn);
- 時間復(fù)雜度平均:O(nlogn);
- 空間復(fù)雜度:O(1);
- 穩(wěn)定性:不穩(wěn)定;
6.冒泡排序
6.1 算法思想
比較兩個相鄰的元素,將值大的元素交換到右邊
思路:依次比較相鄰的兩個數(shù),將比較小的數(shù)放在前面,比較大的數(shù)放在后面。
- 1.第一次比較:首先比較第一和第二個數(shù),將小數(shù)放在前面,將大數(shù)放在后面。
- 2.比較第2和第3個數(shù),將小數(shù) 放在前面,大數(shù)放在后面。
- 3.如此繼續(xù),知道比較到最后的兩個數(shù),將小數(shù)放在前面,大數(shù)放在后面,重復(fù)步驟,直至全部排序完成
- 4.在上面一趟比較完成后,最后一個數(shù)一定是數(shù)組中最大的一個數(shù),所以在比較第二趟的時候,最后一個數(shù)是不參加比較的。
- 5.在第二趟比較完成后,倒數(shù)第二個數(shù)也一定是數(shù)組中倒數(shù)第二大數(shù),所以在第三趟的比較中,最后兩個數(shù)是不參與比較的。
- 6.依次類推,每一趟比較次數(shù)減少依次
6.2 動畫演示

6.3 代碼示例
public static void bubbleSort(int[] arr){
for(int i=0;i<arr.length-1;i++){//外層循環(huán)控制趟數(shù)
boolean flag=false;//一個標(biāo)記進行優(yōu)化
for(int j=0;j<arr.length-1-i;j++){//內(nèi)層循環(huán)控制比較次數(shù)
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=true;
}
}
if(flag==false) break;//如果一趟循環(huán)走完沒有交換,說明已經(jīng)有序,直接break
}
}
6.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最好:O(n);
- 時間復(fù)雜度最壞:O(n^2);
- 時間復(fù)雜度平均:O(n^2);
- 空間復(fù)雜度:O(1);
- 穩(wěn)定性:穩(wěn)定
7.快速排序(十分重要)
7.1 算法思想
我們從數(shù)組中選擇一個元素,我們把這個元素稱之為中軸元素吧,然后把數(shù)組中所有小于中軸元素的元素放在其左邊,所有大于或等于中軸元素的元素放在其右邊,顯然,此時中軸元素所處的位置的是有序的。也就是說,我們無需再移動中軸元素的位置。
從中軸元素那里開始把大的數(shù)組切割成兩個小的數(shù)組(兩個數(shù)組都不包含中軸元素),接著我們通過遞歸的方式,讓中軸元素左邊的數(shù)組和右邊的數(shù)組也重復(fù)同樣的操作,直到數(shù)組的大小為1,此時每個元素都處于有序的位置。
7.2 動畫演示

7.3 代碼示例
//該代碼參考acwing創(chuàng)始人yxc的快排模板
public static void quickSort(int[] arr,int l,int r){
if(l>=r) return;//每次判斷傳進來左右下標(biāo)
int i=l-1,j=r+1;//因為在循環(huán)中,要先i++,j--所以i,j定義為兩邊界偏移1
int x=arr[(l+r)/2];//取x為數(shù)組中間位置數(shù)
while(i<j){
do i++;while(arr[i]<x);
do j--;while(arr[j]>x);
//此時i指向的數(shù)字大于中軸數(shù)字, j指向數(shù)字小于中軸數(shù)字 交換
if(i<j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
quickSort(arr,l,j);//退出循環(huán)是i==j 遞歸排序
quickSort(arr,j+1,r);
}
7.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最好:O(nlog2n);
- 時間復(fù)雜度最壞:O(n^2);
- 時間復(fù)雜度平均:O(nlog2n);
- 空間復(fù)雜度:O(nlog2n);
- 穩(wěn)定性:不穩(wěn)定;
8.歸并排序
8.1 算法思想
將一個大的無序數(shù)組有序,我們可以把大的數(shù)組分成兩個,然后對這兩個數(shù)組分別進行排序,之后在把這兩個數(shù)組合并成一個有序的數(shù)組。由于兩個小的數(shù)組都是有序的,所以在合并的時候是很快的。
通過遞歸的方式將大的數(shù)組一直分割,直到數(shù)組的大小為 1,此時只有一個元素,那么該數(shù)組就是有序的了,之后再把兩個數(shù)組大小為1的合并成一個大小為2的,再把兩個大小為2的合并成4的 …… 直到全部小的數(shù)組合并起來。
8.2 動畫演示

8.3 代碼示例
public static void mergerSort(int[]arr){
mergerSortInternal(arr,0,arr.length-1);
}
public static void mergerSortInternal(int[]arr,int l,int r) {//
if (l >= r) return;
int mid = (l + r) / 2;//把數(shù)組分為兩個
mergerSortInternal(arr, l, mid);//對左邊數(shù)組遞歸排序
mergerSortInternal(arr, mid + 1, r);//對右邊數(shù)組遞歸排序
merge(arr,l,mid,r);//排序完成,進行合并
}
public static void merge(int[]arr,int low,int mid,int high){
int s1=low;//mid左側(cè)數(shù)組的頭和尾
int e1=mid;
int s2=mid+1;//mid右側(cè)數(shù)組的頭和尾
int e2=high;
int[]temp=new int[high-low+1];//定義一個數(shù)字,用來合并他們
int k=0;//指示數(shù)組下標(biāo)
while(s1<=e1&&s2<=e2){//選出兩側(cè)數(shù)組最小元素 插入temp數(shù)組
if(arr[s1]<=arr[s2]) temp[k++]=arr[s1++];
else temp[k++]=arr[s2++];
}
while(s1<=e1) temp[k++]=arr[s1++];//如果一側(cè)數(shù)組插入完畢,另一側(cè)還有剩余,則全部插入
while(s2<=e2) temp[k++]=arr[s2++];
for(int i=0;i<temp.length;i++){//返還到arr數(shù)組
arr[i+low]=temp[i];
}
}
}
8.4 時間復(fù)雜度,空間復(fù)雜度以及穩(wěn)定性
- 時間復(fù)雜度最好:O(nlog2n);
- 時間復(fù)雜度最壞:O(nlog2n);
- 時間復(fù)雜度平均:O(nlog2n);
- 空間復(fù)雜度:O(n);
到此這篇關(guān)于Java數(shù)據(jù)結(jié)構(gòu)之基于比較的排序算法基本原理及具體實現(xiàn)的文章就介紹到這了,更多相關(guān)Java 排序算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?cloud如何實現(xiàn)FeignClient指定Zone調(diào)用
這篇文章主要介紹了Spring?cloud如何實現(xiàn)FeignClient指定Zone調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Java List轉(zhuǎn)換成String數(shù)組幾種實現(xiàn)方式詳解
這篇文章主要介紹了Java List轉(zhuǎn)換成String數(shù)組幾種實現(xiàn)方式詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12

