Java數(shù)據(jù)結構之基于比較的排序算法基本原理及具體實現(xiàn)
基于比較的排序算法基本原理及Java實現(xiàn)
1. 七大基于比較的排序-總覽
1.1常見基于比較的排序分類
1.2時間復雜度,空間復雜度以及穩(wěn)定性。
- 穩(wěn)定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
- 不穩(wěn)定:如果a原本在b的前面,而a=b,排序之后 a 可能會出現(xiàn)在 b 的后面。
2.直接插入排序
2.1 直接插入排序的基本思想
直接插入排序的基本思想,就是每次選取一個待排序元素,按照一定規(guī)定插入到前面已經排好序的一組元素的適當位置。當每個元素都插入完畢,整個序列已經有序。
當插入第i(i >= 1)時,前面的V[0],V[1],……,V[i-1]已經排好序。這時,用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之前的序列 已經有序 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 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最壞情況:數(shù)組整體逆序O(n^2);
- 時間復雜度最好情況:數(shù)組已經有序O(n^2);
- 空間復雜度:O(1);
- 穩(wěn)定性:穩(wěn)定;
3. 希爾排序
3.1 算法思想
希爾排序是特殊的插入排序,直接插入排序每次插入前的遍歷步長為1,而希爾排序是將待排序列分為若干個子序列,對這些子序列分別進行直接插入排序,當每個子序列長度為1時,再進行一次直接插入排序時,結果一定是有序的。常見的劃分子序列的方法有:初始步長(兩個子序列相應元素相差的距離)為要排的數(shù)的一半,之后每執(zhí)行一次步長折半。
3.2 圖片演示
3.3 代碼示例
public static void hell(int[] arr,int gap){//當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 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最好:O(n);
- 時間復雜度最壞:O(n^1.5);
- 時間復雜度平均:O(n^1.3);
- 空間復雜度: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 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最好:O(n);
- 時間復雜度最壞:O(n^2;
- 時間復雜度平均:O(n^2);
- 空間復雜度:O(1);
- 穩(wěn)定性:不穩(wěn)定;
5.堆排序
5.1 算法思想
堆
堆是具有以下性質的完全二叉樹:每個結點的值都大于或等于其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小于或等于其左右孩子結點的值,稱為小頂堆。如下圖:
- 將初始待排序關鍵字序列(R1,R2….Rn)構建成大頂堆,此堆為初始的無序區(qū);
- 將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(qū)(R1,R2,……Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2…n-1]<=R[n];
- 由于交換后新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(qū)(R1,R2,……Rn-1)調整為新堆,然后再次將R[1]與無序區(qū)最后一個元素交換,得到新的無序區(qū)(R1,R2….Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復此過程直到有序區(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é)點,然后向下調整根節(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){//向下調整操作 int parent=root; int child=parent*2+1; while(child<len){ if(child+1<len&&arr[child]<arr[child+1]){//待調整節(jié)點右子樹大于左子樹 child++; } if(arr[child]>arr[parent]){//由于是大根堆,如果兒子節(jié)點值大于父親節(jié)點就交換 int temp=arr[parent]; arr[parent]=arr[child]; arr[child]=temp; parent=child;//繼續(xù)向下調整,防止調整后不滿足大根堆的條件 child=parent*2+1; }else break; } }
5.4 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最好:O(nlogn);
- 時間復雜度最壞:O(nlogn);
- 時間復雜度平均:O(nlogn);
- 空間復雜度: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ù)放在后面,重復步驟,直至全部排序完成
- 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;//一個標記進行優(yōu)化 for(int j=0;j<arr.length-1-i;j++){//內層循環(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)走完沒有交換,說明已經有序,直接break } }
6.4 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最好:O(n);
- 時間復雜度最壞:O(n^2);
- 時間復雜度平均:O(n^2);
- 空間復雜度:O(1);
- 穩(wěn)定性:穩(wěn)定
7.快速排序(十分重要)
7.1 算法思想
我們從數(shù)組中選擇一個元素,我們把這個元素稱之為中軸元素吧,然后把數(shù)組中所有小于中軸元素的元素放在其左邊,所有大于或等于中軸元素的元素放在其右邊,顯然,此時中軸元素所處的位置的是有序的。也就是說,我們無需再移動中軸元素的位置。
從中軸元素那里開始把大的數(shù)組切割成兩個小的數(shù)組(兩個數(shù)組都不包含中軸元素),接著我們通過遞歸的方式,讓中軸元素左邊的數(shù)組和右邊的數(shù)組也重復同樣的操作,直到數(shù)組的大小為1,此時每個元素都處于有序的位置。
7.2 動畫演示
7.3 代碼示例
//該代碼參考acwing創(chuàng)始人yxc的快排模板 public static void quickSort(int[] arr,int l,int r){ if(l>=r) return;//每次判斷傳進來左右下標 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 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最好:O(nlog2n);
- 時間復雜度最壞:O(n^2);
- 時間復雜度平均:O(nlog2n);
- 空間復雜度: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左側數(shù)組的頭和尾 int e1=mid; int s2=mid+1;//mid右側數(shù)組的頭和尾 int e2=high; int[]temp=new int[high-low+1];//定義一個數(shù)字,用來合并他們 int k=0;//指示數(shù)組下標 while(s1<=e1&&s2<=e2){//選出兩側數(shù)組最小元素 插入temp數(shù)組 if(arr[s1]<=arr[s2]) temp[k++]=arr[s1++]; else temp[k++]=arr[s2++]; } while(s1<=e1) temp[k++]=arr[s1++];//如果一側數(shù)組插入完畢,另一側還有剩余,則全部插入 while(s2<=e2) temp[k++]=arr[s2++]; for(int i=0;i<temp.length;i++){//返還到arr數(shù)組 arr[i+low]=temp[i]; } } }
8.4 時間復雜度,空間復雜度以及穩(wěn)定性
- 時間復雜度最好:O(nlog2n);
- 時間復雜度最壞:O(nlog2n);
- 時間復雜度平均:O(nlog2n);
- 空間復雜度:O(n);
到此這篇關于Java數(shù)據(jù)結構之基于比較的排序算法基本原理及具體實現(xiàn)的文章就介紹到這了,更多相關Java 排序算法內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java object wait notify notifyAll代碼解析
這篇文章主要介紹了Java object wait notify notifyAll代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-11-11mybatis攔截器實現(xiàn)數(shù)據(jù)權限項目實踐
本文主要介紹了mybatis攔截器實現(xiàn)數(shù)據(jù)權限項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06Java中的BlockingQueue阻塞隊列原理以及實現(xiàn)詳解
這篇文章主要介紹了Java中的BlockingQueue阻塞隊列原理以及實現(xiàn)詳解,在最常見的使用到這個阻塞隊列的地方,就是我們耳熟能詳?shù)木€程池里面了,作為我們線程池的一大最大參與者,也是AQS的一個具體實現(xiàn),需要的朋友可以參考下2023-12-12maven項目中<scope>provided</scope>的作用及說明
這篇文章主要介紹了maven項目中<scope>provided</scope>的作用及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12