做者 | 三玖天下第一 html
來源 | cnblogs.com/hzoi-liujiahui/p/13256439.htmljava
一、冒泡排序c++
冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,若是它們的順序錯誤就把它們交換過來。走訪數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。這個算法的名字由來是由於越小的元素會經由交換慢慢「浮」到數列的頂端。程序員
1.1 算法描述
n個元素的序列,通過n-1趟選擇排序獲得有序結果。具體算法描述以下:web
初始狀態:無序區爲R[1..n],有序區爲空;面試
每一輪從無序區起點開始,相鄰元素兩兩比較,若是前面的比後面的元素大就交換,直到無序區最後。算法
針對全部的元素重複以上的步驟,每一輪冒泡操做無序區域元素減一,有序區元素減一;後端
重複執行n-1輪,序列變爲有序。數組
1.2 動圖演示
1.3 代碼實現微信
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Bubble_sort(int a[]){
for(int i=1;i<n;++i){//通過n-1輪的冒泡操做
for(int j=1;j<=n-i;++j)//沒操做一輪,待排序的少一個
if(a[j]>a[j+1])
std::swap(a[j],a[j+1]);
}
}
void Solve(){
Read();
Bubble_sort(a);
Print();
}
int main(){
Solve();
return 0;
}
2.選擇排序
選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工做原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。
2.1 算法描述
n個記錄的直接選擇排序可通過n-1趟直接選擇排序獲得有序結果。具體算法描述以下:
初始狀態:無序區爲R[1..n],有序區爲空;
第i趟排序(i=1,2,3…n-1)開始時,當前有序區和無序區分別爲R[1..i-1]和R(i..n)。該趟排序從當前無序區中-選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變爲記錄個數增長1個的新有序區和記錄個數減小1個的新無序區;
n-1趟結束,數組有序化了。
2.2 動圖演示
2.3 代碼實現
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Select_sort(int a[]){
int k=0;
for(int i=1;i<n;++i){//n-1輪,每一輪找到一個最小的
k=i;//k指向未排序的最小
for(int j=i+1;j<=n;++j)
if(a[k]>a[j])
k=j;
std::swap(a[i],a[k]);
}
}
void Solve(){
Read();
Select_sort(a);
Print();
}
int main(){
Solve();
return 0;
}
3. 插入排序
插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。
3.1 算法描述
從第一個元素開始,該元素能夠認爲已經被排序;
取出下一個元素,在已經排序的元素序列中從後向前掃描;
若是該元素(已排序)大於新元素,將該元素移到下一位置;
重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;
將新元素插入到該位置後;
重複步驟2~5。
3.2 動圖演示
3.3 代碼實現
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Insert_sort(int a[]){
for(int i=2;i<=n;++i){
for(int j=i;j>1 && a[j-1]>a[j];--j)
std::swap(a[j],a[j-1]);
}
}
void Solve(){
Read();
Insert_sort(a);
Print();
}
int main(){
Solve();
return 0;
}
4. Shell排序
1959年Shell發明,第一個突破 O(n2)
O ( n 2 ) 的排序算法,是簡單插入排序的改進版。它與插入排序的不一樣之處在於,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。
4.1 算法描述
選擇一個較大的增量,通常選gap=n/2,把下標爲i,i+gap,i+2*gap+...+i+n/gap*gap分爲一組,對同組的元素進行插入排序。
減少增量爲上一個增量的一半,繼續操做1。
重複以上操做,直到增量爲1,此時序列變爲有序。
4.2 動圖演示
4.3 代碼演示
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Shell_sort(){
for(int d=n/2;d>0;d>>=1){//增量選擇,逐漸縮小爲原來一半
for(int i=d+1;i<=n;++i){//對同組元素進行插入排序
for(int j=i;j-d>0 && a[j]<a[j-d];j-=d)
std::swap(a[j],a[j-d]);
}
}
}
void Solve(){
Read();
Shell_sort();
Print();
}
int main(){
Solve();
return 0;
}
4.4 時間效率
希爾排序中對於增量序列的選擇十分重要,直接影響到希爾排序的性能。咱們上面選擇的增量序列{n/2,(n/2)/2...1}(希爾增量),其最壞時間複雜度依然爲 O(n2)
O ( n 2 ) ,一些通過優化的增量序列如Hibbard通過複雜證實可以使得最壞時間複雜度爲 O(n3/2)O ( n 3 / 2 ) 。
5. 歸併排序
歸併排序是創建在歸併操做上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。
5.1 算法描述
把長度爲n的輸入序列分紅兩個長度爲n/2的子序列;
對這兩個子序列分別採用歸併排序;
將兩個排序好的子序列合併成一個最終的排序序列。
5.2 動圖演示
5.3 代碼實現
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Merge(int l,int mid,int r){//合併操做
int i=l,j=mid+1,k=0;//i指向前面區間第一個元素,j指向後面區間第一元素
int b[r-l+2]={0};//b存儲合併的序列
while(i<=mid && j<=r){//取兩個序列前面的較小者
if(a[i]<=a[j])b[++k]=a[i++];
else b[++k]=a[j++];
}//跳出循環兩個序列中有一個爲空
while(i<=mid)//若比較完以後,第一個有序區仍有剩餘
b[++k]=a[i++];
while(j<=r)//若比較完以後,第二個有序區仍有剩餘
b[++k]=a[j++];
for(i=l,k=1;i<=r;++i,++k)//把合併後的排好序的序列拷貝到數組a[l,r]
a[i]=b[k];
}
void Merge_sort(int l,int r){
if(l<r){//把區間分紅兩部分
int mid=l+(r-l)/2;
Merge_sort(l,mid);//遞歸左區間
Merge_sort(mid+1,r);//遞歸右區間
Merge(l,mid,r);//合併左右兩區間
}
}
void Solve(){
Read();
Merge_sort(1,n);
Print();
}
int main(){
Solve();
return 0;
}
5.4 時間效率
歸併排序是一種穩定的排序方法。和選擇排序同樣,歸併排序的性能不受輸入數據的影響,但表現比選擇排序好的多,由於始終都是 O(nlogn)
O ( n l o g n ) 的時間複雜度。代價是須要額外的內存空間。
6. 快速排序
快速排序的基本思想:經過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另外一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。
6.1 算法描述
從數列中挑出一個元素,稱爲 基準(pivot);
從新排序數列,全部元素比基準值小的擺放在基準前面,全部元素比基準值大的擺在基準的後面(相同的數能夠到任一邊)。在這個分區退出以後,該基準就處於數列的中間位置。這個稱爲分區(partition)操做;
遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
6.2 動圖演示
6.3 代碼實現
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Quick_sort(int l,int r){
if(l>=r)return;
int i=l,j=r,base=a[l];//選左邊界做爲基準
while(i<j){//做指針要小於右指針
while(i<j && a[j]>base)--j;//先遍歷右邊界
while(i<j && a[i]<base)++i;
if(i<j)std::swap(a[i],a[j]);
}//跳出循環時i==j,此位置爲基準最終位置
std::swap(a[l],a[i]);
Quick_sort(l,i-1);
Quick_sort(i+1,r);
}
void Solve(){
Read();
Quick_sort(1,n);
Print();
}
int main(){
Solve();
return 0;
}
7. 堆排序
堆排序 (Heapsort) 是指利用堆這種數據結構所設計的一種排序算法。
堆是一個近似徹底二叉樹的結構,並同時知足堆的性質:即子結點的鍵值或索引老是小於(或者大於)它的父結點。
堆排序能夠說是一種利用堆的概念來排序的選擇排序。分爲兩種方法:
大頂堆:每一個結點的值都大於或等於其子結點的值,在堆排序算法中用於升序排列;
小頂堆:每一個結點的值都小於或等於其子結點的值,在堆排序算法中用於降序排列;
堆排序的平均時間複雜度爲 Ο(nlogn)。
7.1 算法描述:
建立一個堆
把堆首(最大值)和堆尾互換;
堆的大小減一,並向下調整堆使之知足堆的性質
重複2,3直到只剩一個元素。
7.2 動圖演示
7.3 代碼實現
#include <cstdio>
#include <cstring>
const int maxn = 10000 + 5;
void swap(int &x,int &y){int t=x;x=y;y=t;}//交換函數
int Heap[maxn],siz=0;
void Push(int x){//向上調整
Heap[++siz]=x;//把插入的元素x放在數組最後
for(int i=siz;i/2>0 && Heap[i]>Heap[i/2];i=i/2)
swap(Heap[i],Heap[i/2]);
}
void Pop(){//向下調整
swap(Heap[siz],Heap[1]);siz--;//交換堆頂和堆底,而後直接彈掉堆底
for(int i=1;2*i<=siz;i*=2){
int j=2*i;//若是存在右兒子且右兒子大於左兒子j就指向右兒子
if(j+1<=siz && Heap[j]<Heap[j+1])++j;
if(Heap[i]<Heap[j])swap(Heap[i],Heap[j]);
else break;
}
}
void Solve(){
int n;scanf("%d",&n);
for(int i=1;i<=n;++i){//建堆
int x;scanf("%d",&x);
Push(x);
}
for(int i=1;i<=n;++i){//輸出堆頂並刪除,此乃降序
printf("%d ",Heap[1]);Pop();
}
printf("\n");
for(int i=1;i<=n;++i)//所有出堆後原數組爲升序
printf("%d ",Heap[i]);
}
int main(){
Solve();
return 0;
}
8. 計數排序
計數排序不是基於比較的排序算法,其核心在於將輸入的數據值轉化爲鍵存儲在額外開闢的數組空間中。做爲一種線性時間複雜度的排序,計數排序要求輸入的數據必須是有肯定範圍的整數。
8.1 算法描述
找出待排序的數組中最大和最小的元素(做爲數組的範圍);
統計數組中每一個值爲i的元素出現的次數,存入數組C的第i項;
對全部的計數累加(從C中的第一個元素開始,每一項和前一項相加);
反向填充目標數組:將每一個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1。
8.2 動圖演示
8.3 代碼實現
#include <bits/stdc++.h>
const int maxn=1e5+5,Inf=2147483647;
int n,a[maxn],b[maxn];
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%100000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
void Counting_sort(){
int Min=Inf,Max=-Inf;
for(int i=1;i<=n;++i){
b[a[i]]++;//值當下標,並計算個數
Min=std::min(Min,a[i]);//
Max=std::max(Max,a[i]);
}
int cnt=0;
for(int i=Min;i<=Max;++i)
while(b[i])a[++cnt]=i,b[i]--;
}
void Solve(){
Read();
Counting_sort();
Print();
}
int main(){
Solve();
return 0;
}
9. 桶排序
桶排序是將待排序集合中處於同一個值域的元素存入同一個桶中,也就是根據元素值特性將集合拆分爲多個區域,則拆分後造成的多個桶,從值域上看是處於有序狀態的。對每一個桶中元素進行排序,則全部桶中元素構成的集合是已排序的。
9.1 算法描
設置一個定量的數組看成空桶子。
尋訪序列,而且把項目一個一個放到對應的桶子去。
對每一個不是空的桶子進行排序。
從不是空的桶子裏把項目再放回原來的序列中。
9.2 動圖演示
9.3 時間效率
桶排序最好狀況下使用線性時間O(n),桶排序的時間複雜度,取決與對各個桶之間數據進行排序的時間複雜度,由於其它部分的時間複雜度都爲O(n)。很顯然,桶劃分的越小,各個桶之間的數據越少,排序所用的時間也會越少。但相應的空間消耗就會增大。
10. 基數排序
基數排序是按照低位先排序,而後收集;再按照高位排序,而後再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最後的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。
10.1 算法描述
取得數組中的最大數,並取得位數;
arr爲原始數組,從最低位開始取每一個位組成radix數組;
對radix進行計數排序(利用計數排序適用於小範圍數的特色);
10.2 動圖演示
代碼實現
#include <bits/stdc++.h>
const int maxn=1e4+5,Inf=2147483647;
int a[maxn];
int n;
void Read(){
scanf("%d",&n);
srand(time(0));
for(int i=1;i<=n;++i)
a[i]=rand()%10000;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
}
int Get_max(int a[],int n){//求數組的最大值
int Max=a[1];
for(int i=1;i<=n;++i)
Max=std::max(Max,a[i]);
return Max;
}
/*
* 參數說明:
* a -- 數組
* n -- 數組長度
* exp -- 指數。對數組a按照該指數進行排序。
*
* 例如,對於數組a={50, 3, 542, 745, 2014, 154, 63, 616};
* (01) 當exp=1表示按照"個位"對數組a進行排序
* (02) 當exp=10表示按照"十位"對數組a進行排序
* (03) 當exp=100表示按照"百位"對數組a進行排序
* ...
*/
void Count_sort(int a[],int n,int exp){
int b[n+5],buckets[10]={0};
// b存儲"被排序數據"的臨時數組
for(int i=1;i<=n;++i)// 將數據出現的次數存儲在buckets[]中
buckets[(a[i]/exp)%10]++;
for(int i=1;i<10;++i)// 更改buckets[i]。目的是讓更改後的buckets[i]的值,是該數據在b[]中的位置。
buckets[i]+=buckets[i-1];
for(int i=n;i>0;--i){// 將數據存儲到臨時數組b[]中
b[buckets[(a[i]/exp)%10]]=a[i];
buckets[(a[i]/exp)%10]--;
}
for(int i=0;i<=n;++i)// 將排序好的數據賦值給a[]
a[i]=b[i];
}
void Radix_sort(int a[],int n){
int Max=Get_max(a,n);// 數組a中的最大值
for(int i=1;Max/i>0;i*=10)// 從個位開始,對數組a按"指數"進行排序
Count_sort(a,n,i);
}
void Solve(){
Read();
Radix_sort(a,n);
Print();
}
int main(){
Solve();
return 0;
}
本文分享自微信公衆號 - java1234(gh_27ed55ecb177)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。