算法练习day5——190322(快排、建堆、调整堆)
1.快速排序
1.1 非經典:
- 每次選取數組的最后一個元素為基準,進行排序;
- 將大于基準的排后邊,小于基準的排前面,等于基準的在中間(類似于荷蘭國旗問題);
- 直至進行排序的左邊界=右邊界。
運行結果:
1.2 經典的:
第一次排序之前:
第一次排序之后:
第二次進行排序的范圍:
即:每次排序只搞定一個數x。(可能小于等于x之中還有多個等于x的)。
而上面1.1的方法,排完序后:
第二次排序的范圍:
減少了比較的次數,提高了效率。
1.3 代碼中的partation
剛開始時,less和more的位置:
一次排序之后:
交換x和大于區域的第一個位置的元素:
可以相對減少比較次數。
1.4 改進:隨機快排(最常用的)
1.4.1 快排的缺陷
可能使得一部分區域沒數據,基準值選的太偏。
比如:
此時選擇7作為基準,一次排序后,只搞定了一個元素。花費是:
N個元素的花費為:,即。
1.4.2 最好的情況
大于小于區域長度相當——類似于歸并排序的時間復雜度。
1.4.3 改進quickSort()
public static void quickSort(int[] arr, int l, int r) {if (l < r) {swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//獲取隨機的元素為基準int[] p = partition(arr, l, r);quickSort(arr, l, p[0] - 1);quickSort(arr, p[1] + 1, r);} }每次不是選擇最后位置,而是選擇隨機位置的數進行劃分。
交換這個隨機位置的數和最后位置元素的原因:便于代碼復用。
落在每個位置上的概率相等,最終的時間復雜度應用概率表示,結果:。
最常用的,代碼簡潔——常數項很低。
- mergeSort:需準備一個數組,并且要數組拷貝;空間復雜度是
- quickSort:一個while搞定;空間復雜度:;
- 隨機快排的時間復雜度是那個;
- 記住等與區域的左右邊界,原因:
- 當左半部分排完后,需要知道右半部分從哪開始;
- 浪費空間的地方是在劃分點。總共是劃分次(概率計算)
- 最壞是次;
- 最好是次。
最少也得用3個位置存放斷點。
1.5 算法在設計時繞開樣本本身數據狀況的方法
1.隨機,打亂數據狀況;
2.哈希,也是打亂。
1.6 其他
工程上用的是快排的非遞歸版本。
因為遞歸的準備代價高:壓棧信息多,大量都是無用信息。
遞歸層數多時系統棧也會報錯。
應該為非遞歸。
2.堆排序
堆:是完全二叉樹(要么是一個滿二叉樹,要么僅有的節點序號和滿二叉樹中的一一對應)。
并不存在實際的二叉樹,都是數組結構,只是在數組結構中定義了一個規則。
2.1 堆的分類
大根堆:任何一個子樹的最大值都是頭部;
小根堆:任何一個子樹的最小值都是頭部;
2.2 數組變成大根堆:
數組中的一個子數組也可想成完全二叉樹。
原始數組:
1、首先,假設需要變換的數組的范圍僅是0位置的元素:
(腦海中的:)
2、接著是0~1位置的元素:
同時需要比較1和它父節點元素的大小,若大于就得交換。
父節點位置的確定:(當前元素的下標-1)/2
(1-1)/2=0;
所以1位置的元素和0位置的進行比較:1<2,不用交換
3、接著假設需要轉換的是0~2位置的元素:
比較2位置的元素和(2-1)/2=0(向下取整)位置的元素:3>2,需要交換:
交換后,比較被交換位置0的元素和它父節點位置(0-1)/2=0的元素,相等,不滿足大于的條件,不交換。
4、0~3位置的元素:
比較3位置的元素和(3-1)/2=1位置的元素:6>1,需要交換:
再接著比較6和它父節點0的元素:6>3,繼續交換:
5、0~4位置的元素:
0小于它父節點位置(4-1)/2=1的元素3,所以不用交換。
6、0~5位置的元素:
比較4和它父節點位置(5-1)/4=2的元素:4>2,需要交換:
交換后,比較被交換節點位置2的元素和它父節點位置0的元素:4<6不需要交換。
5已經是數組的最大下標,大頂堆轉換結束。
此部分的實現代碼:
package Sort;public class HeapSort {public static void main(String[] args) {int[] array= {2,1,3,6,0,4};heapSort(array);for(int i=0;i<array.length;i++)System.out.print(array[i]+" ");}public static void heapSort(int[] arr) {for(int i=0;i<arr.length;i++) {heapInsert(arr,i);//對0~1、0~2...進行建堆}}public static void heapInsert(int[] arr,int i) {while(arr[i]>arr[(i-1)/2]) {swap(arr,i,(i-1)/2);i=(i-1)/2;//變為自己的父節點,繼續向上比較}}public static void swap(int[] arr,int i,int j) {int temp=arr[i];arr[i]=arr[j];arr[j]=temp;} }運行結果:
2.2.1 建立大根堆的時間復雜度
一個節點進來時,只需要和它到父節點、祖宗節點進行比較。
比較次數就為目前數高:
加入節點i時,面0~i-1的大頂堆已經建好,所以它比的就是0~i-1所形成的樹高。
所以總共是:
2.3 heapify
當堆中有元素發生變化,進行調整的過程。
- 找到它的左右兩個孩子;
- 孩子中較大的和它進行交換;
值變化之前:
將6變為1:
不滿足大根堆的特性了,需要進行調整。
首先找到變化位置0的左右孩子:0*2+1=1,0*2+2=2。
比較,選擇其中較大的和自己交換:5>4,所以1和5進行交換:
再找被調整位置1的左右孩子:1*2+1=3,1*2+2=4。
比較,找到其中較大的:5>3,所以1和5進行交換:
此時1位置4的左右孩子越界,計算停止。
大根堆調整完畢。
代碼實現:
package Sort;public class HeapSort {public static void main(String[] args) {int[] array= {2,1,3,6,0,4};heapSort(array);for(int i=0;i<array.length;i++)System.out.print(array[i]+" ");System.out.println();array[0]=1;heapify(array,0,array.length);//傳入更改的位置for(int i=0;i<array.length;i++)System.out.print(array[i]+" ");}public static void heapSort(int[] arr) {for(int i=0;i<arr.length;i++) {heapInsert(arr,i);//對0~1、0~2...進行建堆}}public static void heapInsert(int[] arr,int i) {while(arr[i]>arr[(i-1)/2]) {swap(arr,i,(i-1)/2);i=(i-1)/2;//變為自己的父節點,繼續向上比較}}public static void heapify(int[] arr,int i,int heapsize) {int left=i*2+1;while(left<heapsize) {if(left+1<heapsize) {int largest=max(arr,i,left,left+1);if(largest==arr[i])//它已經是最大的了break;else if(largest==arr[left]) {swap(arr,i,left);i=left;}else {swap(arr,i,left+1);i=left+1;}left=2*i+1;}else {if(arr[i]<arr[left]) {swap(arr,i,left);i=left;left=i*2+1;}elsebreak;}}}public static void swap(int[] arr,int i,int j) {int temp=arr[i];arr[i]=arr[j];arr[j]=temp;}public static int max(int[] arr,int i,int j,int k) {int num=arr[i]>arr[j]?arr[i]:arr[j];return num>arr[k]?num:arr[k];} }運行結果:
更簡短的代碼:
public static void heapify(int[] arr, int index, int size) {int left = index * 2 + 1;while (left < size) {//左孩子不越界//僅當右孩子不越界,并且右孩子大于左孩子時,largest=右孩子下標,//否則largest=左孩子下標int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//largest處的元素再和index處的元素進行比較,largest為三者中最大值的下標largest = arr[largest] > arr[index] ? largest : index;//如果三者中最大值是index處的,結束循環if (largest == index) {break;}//largset!=index時才能執行到此處//交換index和三者中最大值的位置swap(arr, largest, index);//改變需要對比的節點的下標以及它左孩子的下標index = largest;left = index * 2 + 1;} }?
總結
以上是生活随笔為你收集整理的算法练习day5——190322(快排、建堆、调整堆)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法练习day1——190318(二分查
- 下一篇: 算法练习day6——190323(求中位