九大排序算法Java实现
之前學習數據結構與算法時花了三天時間整理九大排序算法,并采用Java語言來實現,今天第一次寫博客,剛好可以把這些東西從總結的文檔中拿出來與大家分享一下,同時作為自己以后的備忘錄。 ??
1.排序算法時間復雜度、穩定性分類:
2.排序算法問題描述與實現
2.1冒泡排序(交換排序-穩定)
【問題描述】對于一個int數組,請編寫一個冒泡排序算法,對數組元素排序。?
問題分析:冒泡排序,顧名思義,從前往后遍歷,每次遍歷在末尾固定一個最大值。
易錯點:每次內層循環結束都會在末尾確定一個元素的位置,因此內層循環的判斷條件要減去外層的循環次數i
public int[] BobbleSort(int[] array){?
???????? for(int i=0;i<array.length-1;i++){ ?
??????????? for(int j=0;j<array.length-1-i;j++){ ?//內層循環,注意array.length-1-i,一定要-i
??????????????? swap(array,j,j+1);//交換數據元素的方法
???????? ?? }
???????? }
???????? return array;
????? }
????? public void swap(int[] array,int i,int j){//交換數據元素的方法
???????? if(array[i]>array[j]){
??????????? int temp=array[i];
??????????? ?array[i]=array[j];
??????????? ?array[j]=temp;
???????? }
????? }
冒泡排序的改進:加一個布爾型的flag,當某一趟冒泡排序沒有元素交換時,則冒泡結束,元素已經有序,可以有效的減少冒泡次數。
?
2.2 選擇排序(不穩定)
【問題描述】對于一個int數組,請編寫一個選擇排序算法,對數組元素排序。??
問題分析:選擇排序,從前往后遍歷,每次遍歷從頭開始依次選擇最小的來排序。
注意點:
【初始升序】:交換0次,時間復雜度為o(n); 【初始降序】:交換n-1次,時間復雜度為o(n^2)。
【特點】:交換移動數據次數少,比較次數多。
?? public int[] choose(int[] array){
????? for(int i=0;i<array.length-1;i++){
???????? int min=i;
???????? for (int j = i+1; j < array.length; j++) {
??????????? if(array[min]>array[j])
??????????????? min=j;
???????? }
???????? if(i!=min){
??????????? swap(array,i,min);
???????? }
????? }
????? return array;
?? }
??public?void?swap(int[] array,int?i,int?j){
??????if(array[i]>array[j]){
?????????int?temp=array[i];
???????? ?array[i]=array[j];
???????? ?array[j]=temp;
????? }
?? }
?
2.3 直接插入排序(穩定的 )
【問題描述】對于一個int數組,請編寫一個插入排序算法,對數組元素排序。??
問題分析:插入排序,從前往后遍歷,每次遍歷確定一個元素的位置,下一個元素從后往前依次與排序好的元素比較,類似于摸撲克牌。
注意點:假定n是數組的長度,首先假設第一個元素被放置在正確的位置上,這樣僅需從1-n-1范圍內對剩余元素進行排序。對于每次遍歷,從0-i-1范圍內21.的元素已經被排好序,每次遍歷的任務是:通過掃描前面已排序的子列表,將位置i處的元素定位到從0到i的子列表之內的正確的位置上。將arr[i]復制為一個名為target的臨時元素。向下掃描列表,比較這個目標值target與arr[i-1]、arr[i-2]的大小,依次類推。這個比較過程在小于或等于目標值的第一個元素(arr[j])處停止,或者在列表開始處停止(j=0)。在arr[i]小于前面任何已排序元素時,后一個條件(j=0)為真,因此,這個元素會占用新排序子列表的第一個位置。在掃描期間,大于目標值target的每個元素都會向右滑動一個位置(arr[j]=arr[j-1])。一旦確定了正確位置j,目標值target(即原始的arr[i])就會被復制到這個位置。與選擇排序不同的是,插入排序將數據向右滑動,并且不會執行交換。
?? public int[] insert1(int[] array){
????? for(int i=1;i<array.length;i++){
???????? int j=i;
???????? int temp=array[i];
???????? while(j>0&&array[j-1]>temp){
??????????? array[j]=array[j-1];//元素右移
??????????? j--;
???????? }
???????? array[j]=temp;
????? }
????? return array;
?? }
?
2.4?希爾排序(2個for循環+最后一個while,不穩定)
問題描述:對于一個int數組,請編寫一個希爾排序算法,對數組元素排序。??
問題分析:希爾排序是改進的插入排序,算法先將要排序的一組數按某個增量d(n/2,n為要排序數的個數)分成若干組,每組中記錄的下標相差d.對每組中全部元素進行直接插入排序,然后再用一個較小的增量(d/2)對它進行分組,在每組中再進行直接插入排序。當增量減到1時,進行直接插入排序后,排序完成。希爾排序法(縮小增量法) 屬于插入類排序,是將整個無序列分割成若干小的子序列分別進行插入排序的方法。
- public int[] hillInsert(int[] array){
- ??? for (int d = array.length>>1; d>=1; d=d>>1) {
- ?????? for(int i=d;i<array.length;i++){
- ????????? int j=i;
- ????????? int temp=array[i];
- ????????? while(j>=d&&array[j-d]>temp){
- ???????????? array[j]=array[j-d];
- ???????????? j-=d;
- ????????? }
- ????????? array[j]=temp;
- ?????? }
- ??? }
- ??? return array;
- }
2.5.堆排序(不穩定)
?問題描述:對于一個int數組,請編寫一個堆排序算法,對數組元素排序。??
問題分析:堆數據結構是一種數組對象,它可以被視為一科完全二叉樹結構(完全二叉樹是由滿二叉樹而引出來的。對于深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹)。它的特點是父節點的值大于(小于)兩個子節點的值(分別稱為大頂堆和小頂堆)。它常用于管理算法執行過程中的信息,應用場景包括堆排序,優先隊列等。
完全二叉樹性質:如果i>1,則雙親是結點[i/2]。也就是說下標i與2i和2i+1是雙親子女關系。?當排序對象為數組時,下標從0開始,所以下標 i 與下標 2i+1和2i+2是雙親子女關系。
算法思路:
- 堆排序:(大根堆)
- ①將存放在array[0,...,n-1]中的n個元素建成初始堆;
- ②將堆頂元素與堆底元素進行交換,則序列的最大值即已放到正確的位置;
- ③但此時堆被破壞,將堆頂元素向下調整使其繼續保持大根堆的性質,再重復第②③步,直到堆中僅剩下一個元素為止。
- 堆排序算法的性能分析:
- 空間復雜度:o(1);
- 時間復雜度:建堆:o(n),每次調整o(log n),故最好、最壞、平均情況下:o(n*logn);
- 穩定性:不穩定
- public static int[] heapMax(int[] array){ ?//建立大根堆
- ??? for(int i=(array.length-2)/2;i>=0;i--){
- ?????? buildHeap(array,i,array.length);
- ??? }
- ??? return array;
- }
- private static void buildHeap(int[] array, int k, int length) { ?//建堆方法
- ??? // TODO Auto-generated method stub
- ??? int temp=array[k];
- ??? for(int i=2*k+1;i<length-1;i=2*i+1){
- ?????? if(array[i]<array[i+1]){
- ????????? i++;
- ?????? }
- ?????? if(temp>=array[i])
- ????????? break;
- ?????? else{
- ????????? array[k]=array[i];
- ????????? k=i;
- ?????? }
- ??? }?
- ?????? array[k]=temp;
- }
- public static int[] heapSortArray(int[] array){ //進行堆排序
- ??? ?array=heapMax(array);??
- ??? for (int i =array.length-1; i >0; i--) {
- ?????? int temp=array[0];???
- ?????? array[0]=array[i];
- ?????? array[i]=temp;
- ?????? buildHeap(array,0,i);
- ??? }
- ??? return array;
- }
?
2.6 歸并排序(穩定)
問題描述:對于一個int數組,請編寫一個歸并排序算法,對數組元素排序。?
問題分析:歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。首先考慮下如何將將二個有序數列合并。這個非常簡單,只要從比較二個數列的第一個數,誰小就先取誰,取了后就在對應數列中刪除這個數。然后再進行比較,如果有數列為空,那直接將另一個數列的數據依次取出即可。可以看出合并有序數列的效率是比較高的,可以達到O(n)。
解決了上面的合并有序數列問題,再來看歸并排序,其的基本思路就是將數組分成二組A,B,如果這二組組內的數據都是有序的,那么就可以很方便的將這二組數據進行排序。如何讓這二組組內數據有序了?
可以將A,B組各自再分成二組。依次類推,當分出來的小組只有一個數據時,可以認為這個小組組內已經達到了有序,然后再合并相鄰的二個小組就可以了。這樣通過先遞歸的分解數列,再合并數列就完成了歸并排序。
?? public int[] mergeSort(int[] A, int n) {
?? ?????? //歸并排序,遞歸做法,分而治之
?? ??????? mSort(A,0,n-1);
?? ??????? return A;
?? ??? }
?
?? ??? private void mSort(int[] A,int left,int right){
??? //分而治之,遞歸常用的思想,跳出遞歸的條件
??? if(left>=right){
??????? return;
??? }
??? //中點
??? int mid = (left+right)/2;
??? //有點類似后序遍歷!
??? mSort(A,left,mid);
??? mSort(A,mid+1,right);
??? merge(A,left,mid,right);
}
//將左右倆組的按序子序列排列成按序序列
private void merge(int[] A,int left,int mid,int rightEnd){
??? //充當tem數組的下標
??? int record = left;
??? //最后復制數組時使用
??? int record2 = left;
??? //右子序列的開始下標
??? int m =mid+1;
??? int[] tem = new int[A.length];
??? //只要left>mid或是m>rightEnd,就跳出循環
??? while(left<=mid&&m<=rightEnd){
??????? if(A[left]<=A[m]){
??????????? tem[record++]=A[left++];
??????? }else{
??????????? tem[record++]=A[m++];
??????? }
??? }
??? while(left<=mid){
??????? tem[record++]=A[left++];
??? }
??? while(m<=rightEnd){
??????? tem[record++]=A[m++];
??? }
??? //復制數組
??? for( ;record2<=rightEnd;record2++){
??????? A[record2] = tem[record2];
??? }
}
?
2.7 快速排序算法(不穩定)
問題描述:對于一個int數組,請編寫一個快速排序算法,對數組元素排序。?
問題分析:快速排序(Quicksort)是對冒泡排序的一種改進,使用分治法(Divide and conquer)策略來把一個序列(list)分為兩個子序列(sub-lists)。
從數列中挑出一個元素,稱為”樞軸”(pivot)。重新排序數列,所有元素比樞軸值小的擺放在基準前面,所有元素比樞軸值大的擺在樞軸的后面(相同的數可以到任一邊)。
在這個分區結束之后,該樞軸就處于數列的中間位置。這個稱為分區(partition)操作。遞歸地(recursive)把小于樞軸值元素的子數列和大于樞軸值元素的子數列排序。
- public static final void quickSort(int[] array, intstart, intend) {
- ???? ??// i相當于助手1的位置,j相當于助手2的位置
- ???? ??int i = start, j = end;
- ???? ??int pivot = array[i]; // 取第1個元素為基準元素
- ???? ??int emptyIndex = i; // 表示空位的位置索引,默認為被取出的基準元素的位置
- ???? ??// 如果需要排序的元素個數不止1個,就進入快速排序(只要i和j不同,就表明至少有2個數組元素需要排序)
- ???? ??while (i < j) {
- ???? ????// 助手2開始從右向左一個個地查找小于基準元素的元素
- ???? ????while (i < j && pivot <= array[j])
- ???? ??????j--;
- ???? ????if (i < j) {
- ???? ??????// 如果助手2在遇到助手1之前就找到了對應的元素,就將該元素給助手1的"空位",j成了空位
- ???? ??????array[emptyIndex] = array[emptyIndex = j];
- ???? ????}
- ???? ?????
- ???? ????// 助手1開始從左向右一個個地查找大于基準元素的元素
- ???? ????while (i < j && array[i] <= pivot)
- ???? ??????i++;
- ???? ????if (i < j) {
- ???? ??????// 如果助手1在遇到助手2之前就找到了對應的元素,就將該元素給助手2的"空位",i成了空位
- ???? ??????array[emptyIndex] = array[emptyIndex = i];
- ???? ????}
- ???? ??}???
- ???? ??// 助手1和助手2相遇后會停止循環,將最初取出的基準值給最后的空位
- ???? ??array[emptyIndex] = pivot;
- ???? ???
- ???? ??// =====本輪快速排序完成=====
- ???? ??// 如果分割點i左側有2個以上的元素,遞歸調用繼續對其進行快速排序
- ???? ??if (i - start > 1) {
- ???? ????quickSort(array, 0, i - 1);
- ???? ??}
- ???? ??// 如果分割點j右側有2個以上的元素,遞歸調用繼續對其進行快速排序
- ???? ??if (end - j > 1) {
- ???? ????quickSort(array, j + 1, end);
- ???? ??}
- ???? }
?
算法優化:選取基準軸點時采用三數取中法:
public class Quick {
?? public void sort(int[] array){
????? int start=0;
????? int end=array.length-1;
????? quickSort(array,start,end);
?? }
?? public void quickSort(int[] array, int start, int end) {
????? // TODO Auto-generated method stub
????? int i=start;
????? int j=end;
????? int emptyIndex=start;
????? int pivot=middle3(array,start,end);
????? while(i<j){
???????? while(i<j&&array[j]>=pivot){
??????????? j--;
???????? }
???????? if(i<j)
??????????? array[emptyIndex]=array[emptyIndex=j];
???????? while(i<j&&array[i]<=pivot){
??????????? i++;
???????? }
???????? if(i<j)
??????????? array[emptyIndex]=array[emptyIndex=i];
????? }
????? array[emptyIndex]=pivot;
?????
????? if(i-start>1)
???????? quickSort(array,start,i-1);
????? if(end-j>1)
???????? quickSort(array,j+1,end);
?? }
?
2.8 計數排序算法(穩定,也是桶排序)
問題描述:對于一個int數組,請編寫一個計數排序算法,對數組元素排序。?
問題分析:
1. 提前必須是已知待排序的關鍵字為整型且范圍已知。?
2. 時間復雜度為O(n+k),n指的是桶的個數,k指的是待排序數組的長度,不是基于比較的排序算法,因此效率非常之高。?
3. 穩定性好,這個是計數排序非常重要的特性,可以用在后面介紹的基數排序中。?
4. 但需要一些輔助數組,如C[0..k],因此待排序的關鍵字范圍0~k不宜過大。
? ??public int[] countingSort(int[] A, int n) {
??????? if(A==null ||n<2){
??????????? return A;
??????? }
??????? //找出桶的范圍,即通過要排序的數組的最大最小值來確定桶范圍
??????? int min=A[0];
??????? int max=A[0];
??????? for(int i=0;i<n;i++){
??????????? min=Math.min(A[i],min);
??????????? max=Math.max(A[i],max);
??????? }
??????? //確定桶數組,桶的下標即為需排序數組的值,桶的值為序排序數同一組值出現的次數
??????? int[] arr = new int[max-min+1];
??????? //往桶里分配元素
??????? for(int i=0;i<n;i++){
??????????? arr[A[i]-min]++;
??????? }
??????? //從桶中取出元素
??????? int index=0;
??????? for(int i=0;i<arr.length;i++){
??????????? while(arr[i]-->0){
??????????????? A[index++]=i+min;
??????????? }
??????? }
??????? return A;
??? }
}
?
2.9 基數排序算法(穩定)
問題描述:對于一個int數組,請編寫一個基數排序算法,對數組元素排序。?
問題分析:
基數排序(Radix sort)是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然后按每個位數分別比較。由于整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是只能使用于整數。將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后,數列就變成一個有序序列。
鍵字范圍0~k不宜過大。
public void sort(int[] array){
????? int max=0;
????? int d=0;// 位數
????? for (int i = 0; i < array.length; i++) {
???????? max=Math.max(max, array[i]);
????? }
????? while(max>0){
???????? max/=10;
???????? d++;
????? }
????? int t=0;
????? int m=1;
????? int n=1;
????? int[][] temp=new int[10][array.length];
????? int[] order=new int[10];
????? while(m<=d){
???????? for (int i = 0; i < array.length; i++) {
??????????????? int k=((array[i]/n)%10);
??????????????? temp[k][order[k]++]=array[i];
??????????? }
????????
???????? for (int i = 0; i < 10; i++) {
??????????? if(order[i]!=0){
??????????????? for (int j = 0; j <order[i]; j++) {
?????????????????? array[t++]=temp[i][j];
??????????????? }
??????????????? order[i]=0;
??????????? }
???????? }
???????? t=0;
???????? n=n*10;
???????? m++;
????? }
?? }
采用鏈表的方式來實現基數排序:?
//基數排序開始
public static void radixSort(int[] array){
int max=array[0];
for (int i = 0; i < array.length; i++) {
max=Math.max(max, array[i]);
}
int time=0;
while(max!=0){
time++;
max=max/10;
}
List<List<Integer>> list=new ArrayList<List<Integer>>();
for(int i=0;i<10;i++){
List<Integer> item=new ArrayList<Integer>();
list.add(item);
}
for (int i = 0; i < time; i++) {
for (int j = 0; j < array.length; j++) {
int index=array[j]%(int)Math.pow(10, i+1);
index/=(int)Math.pow(10, i);
list.get(index).add(array[j]);
}
int count=0;
for (List<Integer> a : list) {
for (int m : a) {
if(m!=0){
array[count++]=m;
}
}
a.clear();
}
}
}
//基數排序結束
?
轉載于:https://www.cnblogs.com/jetHu/p/6445060.html
總結
以上是生活随笔為你收集整理的九大排序算法Java实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第七章 心得体会
- 下一篇: karatsuba乘法