冒泡排序算法
特色數組
不斷比較兩個相鄰的元素,將值較大的元素交換到右邊,若是遇到相等的值就不進行交換。優化
大/小元素通過不斷的交換浮到頂端。ui
思路spa
以[10,1,35,61,89,36,55]爲例code
1)第一輪排序:blog
1.首先比較第一個值10和第二個值1,大小逆序了,因此交換.獲得[1,10,35,61,89,36,55]排序
2.再比較當前第二個值10和第三個值35,不須要交換。仍爲[1,10,35,61,89,36,55]遞歸
3.直到比較89與36,逆序,交換,獲得[1,10,35,61,36,89,55]it
4.比較89與55,逆序,交換,獲得[1,10,35,61,36,55,89].此時比較到最後兩個數,這一輪排序完成,最後一個數必定是數組中最大的數。所以比較第二趟的時候,最後一個數不須要參與比較。
2)第二輪排序:
1.首先比較第一個值1和第二個值10,不須要交換.獲得[1,10,35,61,36,55,89]
2.再比較當前第二個值10和第三個值35,不須要交換。仍爲[1,10,35,61,36,55,89]
3.直到比較61與36,逆序,交換,獲得[1,10,35,36,61,55,89]
4.比較61與55,逆序,交換,獲得[1,10,35,36,55,61,89].此時比較到最後兩個數,這一輪排序完成,最後兩個數必定是數組中最大的兩個數。所以比較第三趟的時候,最後兩個個數不須要參與比較。
3)第三輪排序:
以此比較相鄰的數字,都不須要交換。
4)每一輪排序的次數都在減小。直到沒有能夠交換的元素,排序就完成了。
複雜度
冒泡排序,每一輪比較都比上一輪,少比較一次。必定程度上減小了算法量。由於每一輪排序都會找出一個較大值。
N個數字要排序完成,總共須要進行N-1輪排序。每i輪的排序次數爲(N-i)次。(因爲第i輪時,已有i-1個數固定在最大值部分)。
當數據正序時,只須要一輪比較就能夠完成排序。最好的時間複雜度爲O(n)
當數據反序時,須要進行n-1輪排序,每輪排序都須要n-i次比較,而且每次比較都必須移動記錄三次來實現交換位置的效果,所以最壞的時間複雜度爲O(n^2).比較次數=[n*(n-1)]/2=O(n^2)、記錄移動次數=[3n*(n-1)]/2=O(n^2)
整體來講,冒泡排序的時間複雜度爲O(n^2).
實現
private static void bubbleSort(int[] array){
//要遍歷的次數 for(int i=0;i<array.length-1;i++){
//依次比較相鄰的兩個數。遍歷依次事後,最大的i個數已經放在前i個位置。所以須要比較的數字-i for(int j=0;j<array.length-i-1;j++){
//優化2:把最後一次交換的位置給len,從而減小內循環的次數。for(int j=0;j<len;j++){
//比較相鄰的數字,若是逆序,則交換 if(array[j]>array[j+1]){ int t=array[j]; array[j]=array[j+1]; array[j+1]=t; //flag=0;//優化1:設置一個標誌位
//tempPosition=j;//優化2:記錄交換的位置 } }
//優化1:判斷flag是否更改了值,若是沒有通過交換的過程,那麼flag!=0.就已經有序了
//優化2:len=tempPosition } }
優化
1.因爲思路部分,不管排序是否已經完成,都須要進行n-1次排序。
那麼咱們能夠設置一個標誌位,來表示當前數據是否已經有序(當前第i輪排序是否有交換)。減小了當數據已經有序後,不必的排序。
2.冒泡排序中還存在,第 i 趟排的第 i 小或者大的元素已經在第 i 位上了,甚至可能第 i-1 位也已經歸位了 的狀況。所以再一次循環時,有許多不產生交換動做的 比較,這些都是不少餘的。
一樣能夠設置一個標誌位,記錄當前第i輪排序所交換的最後一個位置的下標。在進行第i+1輪排序時,只要內循環到該下標就能夠。由於後面位置的數據在上一輪排序就沒有交換位置了,這一次也不會換位置了。
快速排序
特色
分治。
從數列中取出一個基準數;而後進行分區操做,將比這個數大的全放在它的右邊,小於或等於這個數的全放在它的左邊;而後對左右分區不斷重複分區操做,直到各區間中只有一個數。
思路
依舊以[10,1,35,61,89,36,55]爲例。
1)取第一個數10爲基準數.
1.初始時,i=0,j=6,X=a[i]=10.
因爲已將a[0]中的數保存到X,至關於在數組中a[0]的位置挖了個坑。能夠將其它數據填充過來。
2.從j開始向前找一個比X小或者等於X的數,當j=1時,符合條件。所以將a[1]挖出,再填到上一個坑a[0]中。a[0]=a[1];i++;這樣a[0]坑就被填滿了。此時i=1,j=1。i==j,將基準數填入a[i]中。此時數組爲[1,10,35,61,89,36,55]。
3.能夠看出此時a[1]前面的數字都小於它,a[1]後面的數字都大於它。所以再對a[0]和a[2...6]兩個子區間重複上述步驟。
2)對後面的區間[1,10,||35,61,89,36,55],取第一個數35爲基準數。
1.初始時,i=2,j=6,X=a[i]=35.
將a[2]數字挖出來,留一個坑。
2.從j開始往前找一個比X更小或等於X的數,那麼直到找到j=2,才獲得該數。有i==j。將基準數填入a[i]。此時數組仍爲[1,10,35,61,89,36,55]。
3)對後面的區間[1,10,35,||61,89,36,55],取第一個數61爲基準數。
1.初始時,i=3,j=6,X=a[i]=61.
將a[3]數字挖出來,留一個坑。
2.從j開始往前找一個比X更小或等於X的數,找到j=6時,a[6]=55。將a[6]挖出,填入a[3]坑中。a[3]=a[6];i++;
此時i=4,j=6,X=a[i]=61.
3.從i開始日後找一個比X更大的數,找到i=4時,a[4]=89。將a[4]挖出,填入a[6]坑中。a[6]=a[5];j--;
此時i=4,j=5,X=a[i]=61.
4.從j開始往前找一個比X更小或等於X的數,找到j=5時,a[5]=36。將a[5]挖出,填入a[4]坑中。a[4]=a[5];i++;
此時i=j=5。將基準數填入到a[5]坑中,數組爲[1,10,35,||55,36,||61,||89].
能夠看出此時a[5]前面的數字都小於它,後面的數字都大於它。
4)接下來應該就是劃分[55,26]這個區間。
複雜度
1.快速排序最優的狀況是:每一次取到的元素都恰好平分整個數組。此時時間複雜度爲T[n]=2T[n/2]+f(n);T[n/2]爲平分後的子數組的時間複雜度,f[n]爲平分整個數組所花的時間。O(nlogn)
2. 最差的狀況就是每一次取到的元素就是數組中最小/最大的,這種狀況其實就是冒泡排序了(每一次都排好一個元素的順序)。此時時間複雜度爲O(n^2)
3.平均時間複雜度爲O(nlogn)
實現
private static void quickSort(int[] array, int left, int right){ if(left<right){ //設置i,j和基準值 int i=left,j=right,x=array[left]; while(i<j){ //從右到左找到第一個小於或等於x的值 while(i<j && array[j]>x) j--; if(i<j){ array[i]=array[j];//將array[j]的值填到array[i]的坑中,array[j]造成新的坑 i++; } //從左到右找到第一個大於x的值 while(i<j && array[j]<=x) i++; if(i<j){ array[j]=array[i];//將array[i]的值填到上一步留出的array[j]的坑中,array[i]造成新的坑 j--; } } array[j]=x;//退出時,i==j,將x填入坑中 quickSort(array,left,i-1);//遞歸左半部分 quickSort(array,i+1,right); //遞歸右半部分 } }