各個排序演算法應用:求取陣列中第K大的數( LeetCode 215. Kth Largest Element in an Array )
阿新 • • 發佈:2018-12-22
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4]
and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6]
and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
以下為各個演算法相應的程式碼
int findKthLargest(int* nums, int numsSize, int k) { int i,j; int t; int max; /* 1、冒泡法******************************************************************************************** //通過從左到右不斷交換相鄰逆序的相鄰元素,在一輪的交換之後,可以讓未排序的元素上浮到最右側,是的右側是已排序的。在一輪交換中,如果沒有發生交換,就說明陣列已經是有序的,此時可以直接退出。 for(i=0;i<numsSize-1;i++) for(j=1;j<numsSize-i;j++) { if(nums[j]>nums[j-1]){ int t=nums[j]; nums[j]=nums[j-1]; nums[j-1]=t; } } return nums[k-1]; */ /* 2、插入排序****************************************************************************************** //插入排序從左到右進行,每次都將當前元素插入到左側已經排序的陣列中,使得插入之後左部陣列依然有序。第 j 元素是通過不斷向左比較並交換來實現插入過程:當第 j 元素小於第 j - 1 元素,就將它們的位置交換,然後令 j 指標向左移動一個位置,不斷進行以上操作。 for(i=0;i<numsSize-1;i++){ for(j=i+1;j>0;j--){ if(nums[j]>nums[j-1]){ t=nums[j]; nums[j]=nums[j-1]; nums[j-1]=t; } else{ break; } } } return nums[k-1]; */ /* 3、選擇排序,每次選擇最大的排在第k個位置**************************************************************** //選擇出陣列中的最小元素,將它與陣列的第一個元素交換位置。再從剩下的元素中選擇出最小的元素,將它與陣列的第二個元素交換位置。不斷進行這樣的操作,直到將整個陣列排序。 for(i=0;i<k;i++) { t=i; max=nums[i]; for(j=i+1;j<numsSize;j++){ if(nums[j]>max){ t=j; max=nums[j]; } } int a=nums[i]; nums[i]=max; nums[t]=a; } return nums[k-1]; */ /* 4、歸併排序***************************************************************************************** if(numsSize==1) return nums[0]; sort_2(nums,0,numsSize-1); return nums[k-1]; */ /* 5、希爾排序***************************************************************************************** //對於大規模的陣列,插入排序很慢,因為它只能交換相鄰的元素,每次只能將逆序數量減少 1。 //希爾排序的出現就是為了改進插入排序的這種侷限性,它通過交換不相鄰的元素,每次可以將逆序數量減少大於 1。 //希爾排序使用插入排序對間隔 h 的序列進行排序。通過不斷減小 h,最後令 h=1,就可以使得整個陣列是有序的。 int gap; //跳躍的步數 for(gap=numsSize/2;gap>=1;gap=gap/2){ for(i=0;i<numsSize-gap;i++){ for(j=i+gap;j-gap>=0;j=j-gap){ //對gap內的陣列進行插入排序 if(nums[j]>nums[j-gap]){ int aaa=nums[j]; nums[j]=nums[j-gap]; nums[j-gap]=aaa; } } } } return nums[k-1]; */ /* 6、快速排序************************************************************************************** sort(nums,0,numsSize-1); return nums[k-1]; */ /* 7、基於切分的快速選擇排序************************************************************************ //時間複雜度O(n) //該演算法是線性級別的,因為每次正好將陣列二分,那麼比較的總次數為 (N+N/2+N/4+..),直到找到第 k 個元素,這個和顯然小於 2N。 int low=0; int high=numsSize-1; while(high>low){ int q; q=splite_num(nums,low,high); //獲得切分的下標,並排好序 if(q==k-1){ break; } if(q>k-1){ high=q-1; } else{ low=q+1; } } return nums[k-1]; */ /* 8、堆排序******************************************************************************************** // 參考 https://www.cnblogs.com/chengxiao/p/6129630.html //1.構建大頂堆 for(int i=numsSize/2-1;i>=0;i--){ //從第一個非葉子結點從下至上,從右至左調整結構 adjustHeap(nums,i,numsSize); } //2.調整堆結構+交換堆頂元素與末尾元素 for(int j=numsSize-1;j>0;j--){ swap(nums,0,j);//將堆頂元素與末尾元素進行交換 adjustHeap(nums,0,j);//重新對堆進行調整 } return nums[numsSize-k]; */ //******************************************************************************************** } ################################################################################################# void adjustHeap(int *arr,int i,int length){ //1.構建大頂堆 int temp = arr[i];//先取出當前元素i for(int k=i*2+1;k<length;k=k*2+1){//從i結點的左子結點開始,也就是2i+1處開始 if(k+1<length && arr[k]<arr[k+1]){//如果左子結點小於右子結點,k指向右子結點 k++; } if(arr[k] >temp){//如果子節點大於父節點,將子節點值賦給父節點(不用進行交換) arr[i] = arr[k]; i = k; }else{ break; } } arr[i] = temp;//將temp值放到最終的位置 } /** * 交換元素 * @param arr * @param a * @param b */ void swap(int *arr,int a ,int b){ //交換元素 int temp=arr[a]; arr[a] = arr[b]; arr[b] = temp; } ############################################################################################### void sort(int *nums,int low,int high){ //快速排序 //•歸併排序將陣列分為兩個子陣列分別排序,並將有序的子陣列歸併使得整個陣列排序; //•快速排序通過一個切分元素將陣列分為兩個子陣列,左子陣列小於等於切分元素,右子陣列大於等於切分元素,將這兩個子陣列排序也就將整個陣列排序了。 if(high<=low) return; int q; q=splite_num(nums,low,high); //獲得切分的下標,並排好序 sort(nums,low,q); sort(nums,q+1,high); } int splite_num(int *nums,int low,int high){ //獲得切分的下標,並排好序 //取 a[low] 作為切分元素,然後從陣列的左端向右掃描直到找到第一個大於等於它的元素,再從陣列的右端向左掃描找到第一個小於等於它的元素,交換這兩個元素,並不斷進行這個過程,就可以保證左指標 i 的左側元素都不大於切分元素,右指標 j 的右側元素都不小於切分元素。當兩個指標相遇時,將切分元素 a[low] 和 a[j] 交換位置。 int a=nums[low]; int i=low+1; int j=high; while(1){ while(i<high && nums[i]>=a) //搜尋nums[i]<a,當 nums[i]==a的時候,可以分配到另一個數組裡,保證公平分配 { i++; } while(j>low && nums[j]<=a) //搜尋nums[j]>a,當 nums[i]==a的時候,可以分配到另一個數組裡,保證公平分配 { j--; } if(i>=j){ break; } else{ int ttt=nums[i]; nums[i]=nums[j]; nums[j]=ttt; i++; j--; } } int ttt=nums[low]; nums[low]=nums[j]; nums[j]=ttt; return j; } ############################################################################################## int sort_2(int *nums,int low,int high){ //歸併排序 // //歸併排序的思想是將陣列分成兩部分,分別進行排序,然後歸併起來。自頂向下歸併排序 if(high<=low) return; int middle=low+(high-low)/2; sort_2(nums,low,middle); sort_2(nums,middle+1,high); merge(nums,low,middle,high); //合併兩個降序的陣列 } void merge(int *nums,int low,int middle,int high){ //合併兩個降序的陣列 int i,j; i=low; j=middle+1; int k=0; int b[high-low+1]; while((i<=middle)&&(j<=high)&&(k<high-low+1)){ if(nums[i]<nums[j]){ b[k]=nums[j]; j++; } else{ b[k]=nums[i]; i++; } k++; } if(i>middle && j<=high){ for(;j<=high;j++){ b[k]=nums[j]; k++; } } if(j>high && i<=middle){ for(;i<=middle;i++){ b[k]=nums[i]; k++; } } for(i=low;i<=high;i++) nums[i]=b[i-low]; } #############################################################################################