排序大雜燴

七個排序

這裏介紹七個排序算法

選擇排序

選擇排序應該是最簡單的排序之一
簡單來講,找到剩餘區間內的最小值而後放在一個有序區間的末尾
(不用糾結剩餘區間什麼意思,模擬一遍後你就會知道數組

  • 模擬一下
    假設給整個區間排序
    區間內數爲
    4,3,6,9,8
    先找整個區間中的最小值
    爲3,放在第一位,交換3和4
    而後找除了3之外的數的最小值(也就是剩下未排序的元素,第2個數到第5個數即爲剩餘區間),最小值爲4,4就在第二位不用換
    而後繼續找最小值(最小值在第3個數到第5個數之間)爲6,放在原位
    繼續找最小值(最小值在第4個數到第5個數之間)爲8,交換8和9
    最終有序區間[3,4,6,8,9]

算法實現

  • code
for(int i=1;i<len;++i){
	int maxi=i; 
	for(int j=i+1;j<=len;++j)//剩餘區間
		if(s[j]<s[maxi]) maxi=j;//記錄區間中最小值下標
	swap(s[maxi],s[i]);
}
  • 複雜度分析

插入排序

插入排序是經過「插入」來排序函數

經過將一個數插入到一個有序區間的某個位置,從而保證插入後區間有序
設爲插入的數爲b(插入後區間長度+1
則b在區間知足的條件爲 \(a[i]<b<a[i+1]\)spa

  • 證實
    由於\(a[1]<a[2]<....<a[n-1]<a[n]\)
    又由於a序列始終有序,插入後不破壞其性質
    因此插入後\(a[1]<a[2]<...<a[i-1]<b<a[i]<..<a[n+1]\)

舉個簡單例子指針

  • 現有有個區間1,2,6
    要插入一個數字4
    這個很顯然都知道插入到2和6之間就能保證整個區間有序

其實模擬一遍就很容易懂code

  • 假設給整個區間排序
    區間內數爲
    4,3,6,9,8
    設此時4爲有序區間(一個數固然構成一個有序區間
    找下一個數 3
    與4相比,要比4小,爲保證區間有序,因此3須要插入到4的前面即有序區間爲[3,4]
    再下一個數 6
    逐個與有序區間內的數相比插入到4後面,有序區間爲[3,4,6]
    再下一個數 9
    重複剛剛的步驟 有序區間[3,4,6,9]
    以此類推 最終有序區間[3,4,6,8,9]

算法實現

  • code
for(int i=1;i<len;++i){
      cin>>s;
      for(int j=i-1;j>=1 && a[j]>s;--j) a[j+1]=a[j];//從後往前找
      a[j+1]=s;//此時j爲第一個小於s的數的下標
}

複雜度

選擇排序

選擇排序應該是最簡單的排序之一
簡單來講,找到剩餘區間內的最小值而後放在一個有序區間的末尾
(不用糾結剩餘區間什麼意思,模擬一遍後你就會知道blog

  • 模擬一下
    區間內數爲
    4,3,6,9,8
    先找整個區間中的最小值
    爲3,放在第一位,交換3和4
    而後找除了3之外的數的最小值(也就是剩下未排序的元素,第2個數到第5個數即爲剩餘區間),最小值爲4,4就在第二位不用換
    而後繼續找最小值(最小值在第3個數到第5個數之間)爲6,放在原位
    繼續找最小值(最小值在第4個數到第5個數之間)爲8,交換8和9
    最終有序區間[3,4,6,8,9]

算法實現

  • code
for(int i=1;i<len;++i){
	int maxi=i; 
	for(int j=i+1;j<=len;++j)//剩餘區間
		if(s[j]<s[maxi]) maxi=j;//記錄區間中最小值下標
	swap(s[maxi],s[i]);
}

複雜度

冒泡排序

冒泡排序思想很簡單,實現也很簡單排序

咱們知道數組下標是有序的,只要咱們在數組中,以這個數的爲下標的位置的數標記一下就能夠,從1開始找,找到有標記位置就將輸出遞歸

  • 模擬一下
    區間內數爲
    4,3,6,9,8
    那麼數組t={0,0,0,1,1,0,1,0,1,1}
    對應下標 0,1,2,3,4,5,6,7,8,9
    從t[0]開始到t[9],

算法實現

  • code
bool t[maxn];
for(int i=1;i<=len;++i){
	int num;cin>>num;
	t[num]=1; 
}
  • 複雜度

堆排序

堆排序通常能夠直接用優先隊列實現(這裏不展開講,有興趣同窗能夠到去看其餘blog隊列

#include<queue>//須要用到這個頭文件
priority_queue<>

快速排序

快速排序實際上是用到了分治的思想

將一個區間分爲兩個區間,左區間小中間這個數,右區間大於中間這個數
逐次遞歸分解愈來愈小的區間,最後使整個區間有序

  • 老規矩模擬一遍
    區間內數爲
    4,9,5,2,8
    咱們能夠找到中間數5,
    把全部小於它的數放在左邊,大於它的數放右邊則區間爲
    4,2,5,9,8
    而後再看左區間,取中間數4,操做後區間則爲2,4
    再看右區間,取中間數9,操做後區間則爲8,9
    整個區間就爲2,4,5,8,9
    (若是還不理解,能夠本身多舉幾組數模擬一下

算法實現

  • code
void sort(int l,int r){ 
	int mid=s[(l+r)>>1];//位運算等價於(l+r)/2;
	int i=l,j=r;
	while(i<=j){//這幾步就是使左右區間的數分別知足小於和大於mid 
		while(s[i]<mid) ++i;//i必定不大於 (l+r)/2
		while(s[j]>mid) --j;//j必定不小於 (l+r)/2
		//思考下爲何不大於等於 
		if(i<=j){
			swap(s[i],s[j]);
			i++;j--; 
		}
	} 
	//此時區間劃分爲[l,j] 和[i,r] 
	if(l<j) sort(l,j); 
	if(i<r) sort(i,r);
}

通常不手寫由於stl有

#include<algorithm>
bool cmp(int a,int b){//int 可根據數組類型改成double或其餘(甚至能夠是結構體
  return a<b;//數組按從小到大排序
  return a>b;//數組按從大到小排序
}
sort(a+1,a+1+n,cmp)//a爲數組,a+1爲須要排序的數組的起始位置,a+1+n爲末位置,cmp爲自定義函數
  • 複雜度

歸併排序

這裏主要講二路歸併(還有多路歸併

二路歸併一樣也有分治的思想

將兩個有序的區間,合爲一個有序的區間,和前面講過的同樣,一個數即爲一個有序區間,

  • 模擬。。。(又來了
    假設給整個區間排序 區間內數爲
    4,9,5,2,8
    劃爲兩個區間 4,9,5和 2,8
    再繼續劃分 4,9 和 5和 2和8
    繼續劃分 4和 9 和 5和 2和8
    不能再劃分就中止
    而後開始合併
    怎麼合併了?
    4和9本來屬於一個[4,9]區間,又要合併回去,且保證有序
    比較4和9,4小於9,先放入區間[4],而後9又放入[4,9]
    好而後此時 [4,9]區間有序了,[5]區間也有序了,這兩個又同屬於本來的[4,9,5]
    合併回去,比較4和5,4比5小能夠直接放入(由於此時4是左區間最小數,5是右區間最小數,因此兩個區間合併後的最小數必定在這兩個數之間)目前區間[4]
    (但能不能把5直接放入後面,不能,因左區間第2個數可能小於5),而後把5與9比較,9就比5大,因此放入5,區間[4,5],最後放入9,q區間[4,5,9]
    2和8同理合爲區間[2,8];
    而後區間[4,5,9]與區間[2,8]合爲[2,4,5,8,9]

算法實現

  • code
int m[maxn];//原序列 
int temp[maxn];//存有序區間 
void marge_sort(int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	marge_sort(l,mid);//左區間 
	marge_sort(mid+1,r);//右區間 
	int i=l,j=mid+1,p=l;//i爲左區間左端點,j爲右區間左端點 p爲合成區間的末端指針 
	while(i<=mid && j<=r){
		if(m[i]<m[j]) temp[p++]=m[i++];//兩個序列相比較,小的放 
		else temp[p++]=m[j++]; 
	}
	while(i<=mid) temp[p++]=m[i++];//右區間已經放完,而左區間也是有序的則,直接放入有區間後 
	while(j<=r) temp[p++]=m[j++];//同理,直接放入 
	for(i=l;i<=r;++i) m[i]=temp[i];//把有序序列放置給m 
}

還有不少排序算法沒有講到 若是有不清楚的地方能夠發評論私信提問 講的不行還望海涵

相關文章
相關標籤/搜索