最近在看算法導論,一開始就講了許多關於各種排序的問題,(原諒我之前只會STL模板庫里的sort函數),正好oj上有一個簡單排序題,如圖:
題意就是將序列排序然后找第k個數就行了,先隨便交一發過了之后我覺得我應該學一些別的算法,於是這兩天看懂了歸並算法然后進行了實現。
歸並排序
其實質就是分治,首先考慮下如何將左右兩個有序數列合並。這個非常簡單,只要從比較這兩個數列的第一個數,誰小就先將他放入要排序的數列中。然后再進行比較,如果有數列為空,那直接將另一個數列的數據依次取出即可。
這個排序其實非常簡單,讓我們從小規模來看,當一個數組只有一個數的時候,顯然他是有序的,當有兩個數的時候,把這兩個數分成兩組,這兩組各自都是有序的,然后將其合並,以此類推,通過不斷地遞歸和合並,我們就能實現排序的目的了
合並的過程:
void merge(int arr[],int l,int mid,int r) //我們將整個數組分為左部和右部,l為左部數組的起始點,mid作為兩個數組的分割點,r為右部數組的結尾點,即兩個數組分別為L[l....mid],R[mid+1.....r],這兩個數組都是有序的
{ int llen=mid-l+1,rlen=r-mid; //兩個數組的長度 int x[llen],y[rlen]; //新開辟兩個的數組用來存放未排序的數據 for(int i=0;i<llen;i++) { x[i]=arr[l+i]; } for(int i=0;i<rlen;i++) { y[i]=arr[mid+1+i]; } int j=0,k=0; for(int i=l;i<=r;i++) { if(x[j]<=y[k]&&j<llen&&k<rlen) //誰小誰在前 { arr[i]=x[j++]; } else if(x[j]>y[k]&&j<llen&&k<rlen) { arr[i]=y[k++]; } else if(j>=llen) //誰先放完另一個數組接着依次放入 { arr[i]=y[k++]; } else if(k>=rlen) { arr[i]=x[j++]; } } }
ps:只聲明一個數組也可以實現;
遞歸的函數:
變為子問題排序。
void mergesort(int arr[],int l,int r) { if(l<r) { int mid=(l+r)/2; mergesort(arr,l,mid); //分治,就是大數組從中間分成大小差不多的數組進行子問題的解決 mergesort(arr,mid+1,r); merge(arr,l,mid,r); //合並 } }
以下是該題代碼:
#include<bits/stdc++.h> using namespace std; int a[10000005]; typedef long long ll; void merge(int arr[],int l,int mid,int r) { int llen=mid-l+1,rlen=r-mid; int x[llen],y[rlen]; for(int i=0;i<llen;i++) { x[i]=arr[l+i]; } for(int i=0;i<rlen;i++) { y[i]=arr[mid+1+i]; } int j=0,k=0; for(int i=l;i<=r;i++) { if(x[j]<=y[k]&&j<llen&&k<rlen) { arr[i]=x[j++]; } else if(x[j]>y[k]&&j<llen&&k<rlen) { arr[i]=y[k++]; } else if(j>=llen) { arr[i]=y[k++]; } else if(k>=rlen) { arr[i]=x[j++]; } } } void mergesort(int arr[],int l,int r) { if(l<r) { int mid=(l+r)/2; mergesort(arr,l,mid); mergesort(arr,mid+1,r); merge(arr,l,mid,r); } } int main() { int n,k; cin>>n>>k; for(int i=0;i<n;i++) { cin>>a[i]; } mergesort(a,0,n-1); /*for(int i=0;i<n;i++) { cout<<a[i]<<" "; }*/ cout<<a[k-1]<<endl; return 0; }
但是歸並排序有一些缺陷,雖然和STL里的sort函數的復雜度均為O(nlogn),但是內存方面歸並排序要占的很多,由此可見歸並排序也並不是理想的排序方法,尤其是數據多的時候。之后會更新堆排序(前提是我先學會...