具體總結C++的排序算法。本站提示廣大學習愛好者:(具體總結C++的排序算法)文章只能為提供參考,不一定能成為您想要的結果。以下是具體總結C++的排序算法正文
排序算法經由了很長時光的演化,發生了許多種分歧的辦法。關於初學者來講,對它們停止整頓便於懂得記憶顯得很主要。每種算法都有它特定的應用場所,很難通用。是以,我們很有需要對一切罕見的排序算法停止歸結。
我不愛好逝世記硬背,我更傾向於弄清前因後果,懂得性地記憶。好比上面這張時光龐雜度圖,我們將環繞這張圖來剖析。
下面的這張圖來自一個PPT。它歸納綜合了數據構造中的一切罕見的排序算法,給年夜家總結以下。
辨別穩固與不穩固:疾速、希爾、堆、選擇不穩固,其他排序算法均穩固。
均勻時光龐雜度:冒泡,選擇,拔出是O(n2),其他均是O(n*log2n)
最壞時光龐雜度:冒泡,選擇,拔出,快排是O(n2),其他是O(n*log2n)
均勻和最壞時光龐雜度:只要O(n2)和O(n*log2n)兩種,冒泡,選擇,拔出是O(n2),最壞情形下加一個快排,其他均是O(nlog2n)。
1、直接拔出排序(拔出排序)。
1、算法的偽代碼(如許便於懂得):
INSERTION-SORT (A, n) A[1 . . n] for j ←2 to n do key ← A[ j] i ← j – 1 while i > 0 and A[i] > key do A[i+1] ← A[i] i ← i – 1 A[i+1] = key
2、思惟:以下圖所示,每次選擇一個元素K拔出到之前已排好序的部門A[1…i]中,拔出進程中K順次由後向前與A[1…i]中的元素停止比擬。若發明發明A[x]>=K,則將K拔出到A[x]的前面,拔出前須要挪動元素。
3、算法時光龐雜度。
最好的情形下:正序有序(從小到年夜),如許只須要比擬n次,不須要挪動。是以時光龐雜度為O(n)
最壞的情形下:逆序有序,如許每個元素就須要比擬n次,共有n個元素,是以現實龐雜度為O(n2)
均勻情形下:O(n2)
4、穩固性。
懂得性記憶比逝世記硬背要好。是以,我們來剖析下。穩固性,就是有兩個雷同的元素,排序前後的絕對地位能否變更,重要用在排序時有多個排序規矩的情形下。在拔出排序中,K1是已排序部門中的元素,當K2和K1比擬時,直接插到K1的前面(沒有需要插到K1的後面,如許做還須要挪動!!),是以,拔出排序是穩固的。
2、希爾排序(拔出排序)
1、思惟:希爾排序也是一種拔出排序辦法,現實上是一種分組拔出辦法。先取定一個小於n的整數d1作為第一個增量,把表的全體記載分紅d1個組,一切間隔為d1的倍數的記載放在統一個組中,在各組內停止直接拔出排序;然後,取第二個增量d2(<d1),反復上述的分組和排序,直至所取的增量dt=1
例如:將 n 個記載分紅 d 個子序列:
{ R[0], R[d], R[2d],…, R[kd] }
{ R[1], R[1+d], R[1+2d],…,R[1+kd] }
…
{ R[d-1],R[2d-1],R[3d-1],…,R[(k+1)d-1] }
解釋:d=5 時,先從A[d]開端向前拔出,斷定A[d-d],然後A[d+1]與A[(d+1)-d]比擬,如斯類推,這一回合後將原序列分為d個組。<由後向前>
2、時光龐雜度。
最好情形:因為希爾排序的利害和步長d的選擇有許多關系,是以,今朝還沒有得出最好的步長若何選擇(如今有些比擬好的選擇了,但不肯定能否是最好的)。所以,不曉得最好的情形下的算法時光龐雜度。
最壞情形下:O(N*logN),最壞的情形下戰爭均情形下差不多。
均勻情形下:O(N*logN)
3、穩固性。
因為屢次拔出排序,我們曉得一次拔出排序是穩固的,不會轉變雷同元素的絕對次序,但在分歧的拔出排序進程中,雷同的元素能夠在各自的拔出排序中挪動,最初其穩固性就會被打亂,所以shell排序是不穩固的。(有個猜想,便利記憶:普通來講,若存在不相鄰元素間交流,則極可能是不穩固的排序。)
3、冒泡排序(交流排序)
1、根本思惟:經由過程無序區中相鄰記載症結字間的比擬和地位的交流,使症結字最小的記載如氣泡普通逐步往上“漂浮”直至“水面”。
2、時光龐雜度
最好情形下:正序有序,則只須要比擬n次。故,為O(n)
最壞情形下: 逆序有序,則須要比擬(n-1)+(n-2)+……+1,故,為O(N*N)
3、穩固性
排序進程中只交流相鄰兩個元素的地位。是以,當兩個數相等時,是沒需要交流兩個數的地位的。所以,它們的絕對地位並沒有轉變,冒泡排序算法是穩固的!
4、疾速排序(交流排序)
1、思惟:它是由冒泡排序改良而來的。在待排序的n個記載中任取一個記載(平日取第一個記載),把該記載放入恰當地位後,數據序列被此記載劃分紅兩部門。一切症結字比該記載症結字小的記載放置在前一部門,一切比它年夜的記載放置在後一部門,並把該記載排在這兩部門的中央(稱為該記載歸位),這個進程稱作一趟疾速排序。
2、算法龐雜度
最好的情形下:由於每次都將序列分為兩個部門(普通二分都龐雜度都和logN相干),故為 O(N*logN)
最壞的情形下:根本有序時,退步為冒泡排序,簡直要比擬N*N次,故為O(N*N)
3、穩固性
因為每次都須要和中軸元故舊換,是以本來的次序便可能被打亂。如序列為 5 3 3 4 3 8 9 10 11會將3的次序打亂。所以說,疾速排序是不穩固的!
5、直接選擇排序(選擇排序)
1、思惟:起首在未排序序列中找到最小元素,寄存到排序序列的肇端地位,然後,再從殘剩未排序元素中持續尋覓最小元素,然後放到排序序列末尾。以此類推,直到一切元素均排序終了。詳細做法是:選擇最小的元素與未排序部門的首部交流,使得序列的後面為有序。
2、時光龐雜度。
最好情形下:交流0次,然則每次都要找到最小的元素,是以年夜約必需遍歷N*N次,是以為O(N*N)。削減了交流次數!
最壞情形下,均勻情形下:O(N*N)
3、穩固性
因為每次都是拔取未排序序列A中的最小元素x與A中的第一個元故舊換,是以跨間隔了,極可能損壞了元素間的絕對地位,是以選擇排序是不穩固的!
6、堆排序
1、思惟:應用完整二叉樹中雙親節點和孩子節點之間的內涵關系,在以後無序區當選擇症結字最年夜(或許最小)的記載。也就是說,以最小堆為例,根節點為最小元素,較年夜的節點傾向於散布在堆底鄰近。
2、算法龐雜度
最壞情形下,接近於最差情形下:O(N*logN),是以它是一種後果不錯的排序算法。
3、穩固性
堆排序須要赓續地調劑堆,是以它是一種不穩固的排序!
7、合並排序
1、思惟:屢次將兩個或兩個以上的有序表歸並成一個新的有序表。
2、算法時光龐雜度
最好的情形下:一趟合並須要n次,總共須要logN次,是以為O(N*logN)
最壞的情形下,接近於均勻情形下,為O(N*logN)
解釋:對長度為n的文件,需停止logN 趟二路合並,每趟合並的時光為O(n),故當時間龐雜度不管是在最好情形下照樣在最壞情形下均是O(nlgn)。
3、穩固性
合並排序最年夜的特點就是它是一種穩固的排序算法。合並進程中是不會轉變元素的絕對地位的。
4、缺陷是,它須要O(n)的額定空間。然則很合適於多鏈表排序。
5、C++完成排序算法代碼總結以下:
#include<iostream>
#include<vector>
#include<limits>
using namespace std;
//拔出排序,相當於打牌,相當於把一個值拔出到曾經排序好的一個數組中,
//先把待排序的值放到一個暫時變量外面,讓排好序的數字從年夜到小去與這個值做比擬,若年夜於就把地位往後挪一個,
//騰出一個空地位出來,找到第一個小於他的地位就在該地位前面拔出此值,由於是原地排序,所以待排序的值也在數組中,
//默許數組中第一個值是曾經排好序的
void InsertSort(vector<int> &data)
{
if(!data.empty())
return;
int size = data.size();
for(int j = 1;j < size; ++j)//默許data[0]是排好序的
{
int temp = data[j];
int index = j-1;
while(index >= 0 && data[index] > temp)
{
data[index+1] = data[index];
index--;
}
data[index+1] = temp;
}
}
//合並排序,先分治,在合並
//假定sub1和sub2都是排好序的,result外面包括sub1和sub2中的一切元素
void Merge(vector<int> &result,vector<int> &sub1,vector<int> &sub2)
{
sub1.push_back(INT_MAX);
sub2.push_back(INT_MAX);
int number1 = sub1.size();
int number2 = sub2.size();
int sub1_i = 0,sub2_i = 0;
for(auto it = result.begin();it != result.end();++it)
{
if(sub1[sub1_i] <= sub2[sub2_i])
{
*it = sub1[sub1_i];
++sub1_i;
}
else
{
*it = sub2[sub2_i];
++sub2_i;
}
}
}
void MergeSort(vector<int>& coll)//歸並排序,先分治法,再歸並
{
unsigned int number=coll.size();
if(number<=1)
return;
unsigned int mid=number/2;
vector<int> sub1;
vector<int> sub2;
for(unsigned int i=0;i<mid;++i)
{
sub1.push_back(coll[i]);
}
for(unsigned int j=0;j<number-mid;++j)
{
sub2.push_back(coll[mid+j]);
}
MergeSort(sub1);
MergeSort(sub2);
Merge(coll,sub1,sub2);
}
//冒泡排序法,每次老是拿以後輪回的值與還沒排好序的值停止比擬交流,把這一輪
//中最小的值放在以後輪回的下標數組中,每輪回一次,就排好一個較小的值,如許輪回n次,就排好序了
void BubleSort(vector<int> &data)
{
int size = data.size();
bool sort_flag = false;
for(int i = 0;i < size;++i)
{
if(sort_flag == true) //冒泡改良版,當sort_flag = false;在某次輪回中沒有履行時,解釋剩下的元素都排好序了
return;
sort_flag = true;
for(int j = i;j < size;++j)//經由一次輪回,將最小的值放在i處
{
if(data[i] > data[j])
{
swap(data[i],data[j]);
sort_flag = false;
}
}
}
}
//----------------------------------以下是不穩固排序算法-------------------------
//疾速排序
int Partition(int data[],int length,int start,int end)
{
if(data == NULL || length <= 0 || start < 0 || end >= length)
throw new exception(" Invalid Parameters");
int index = rand()%(start-end+1)+start;
swap(data[index],data[end]);
int left = start-1;//小值放在右邊,年夜值放在左邊,輪回時,if前提不成立時解釋發明小值,不然一向值年夜值
for(index = start;index < end;++index)
{
if(data[index] < data[end])
{
++left;
if(left != index)
swap(data[left],data[index]);
}
}
++left;
swap(data[left],data[end]);
return left;
}
void QuickSort(int data[],int length,int start,int end)
{
if(start == end)
return;
int index = Partition(data,length,start,end);
if(index > start)
QuickSort(data,length,start,index-1);
if(index < end)
QuickSort(data,length,index+1,end);
}
//堆排序 1.堆保護 2、建堆 3、堆排序
void MaxHeapIFY(vector<int> &data,int local,int length)//堆保護,local為要保護的元素的下標,length為數組的長度
{
if(!data.empty())
return;
int left = local*2+1;//由於是從0開端計數,所以盤算公式有2i變成此公式
int right = local*2;
int largest = local;
if(left < length && data[left] > data[local])
{
largest = left;
}
if(right < length && data[right] > data[local])
{
largest = right;
}
if(largest != local)
{
swap(data[largest],data[local]);
MaxHeapIFY(data,largest,length);//largest為加入遞歸的前提,當他年夜於length時,即終止遞歸
}
}
//建堆,是從第一個非葉子節點(length/2-1)停止堆保護
void BuileMaxHeap(vector<int> &data ,int length)
{
int root = length/2-1;
for(int i = root;i >= 0;--i)
{
MaxHeapIFY(data,i,length);
}
}
//將第一個元素的值和最初一個元素互相交流,然後捨去最初一個元素,用剩下的n-1個元素停止堆保護,逐一遞加到最初一個元素
void HeapSort(vector<int> &data)
{
if(!data.empty())
return;
BuileMaxHeap(data,data.size());
int length = data.size();
for(int i = length-1;i >= 0;--i)
{
swap(data[0],data[i]);
--length;
MaxHeapIFY(data,0,length);
}
}
//選擇排序,每次選擇一個本輪回中最小的值,與冒泡排序差不多,只不外少了交流的次數,是直接停止排序的
void SelectionSort(vector<int> &data)
{
int size = data.size();
--size;
for(int i = 0;i < size-1;++i)
{
int min = i;
for(int j = i+1;j < size;++j)
{
if(data[min] > data[j])
min = j;
}
swap(data[min],data[i]);
}
}
//希爾排序,思惟就是拔出排序,把待排序的數組分紅d組(下標每距離為d的停止元素為一組),然後每組停止拔出排序,
//接著遞加d的值,然後拔出排序,直到d=1最初的排序,如許比拔出排序來講,削減了排次序數,相當於跳著停止拔出排序,最初跳度為1
void ShellSort(vector<int> &data)
{
int size = data.size();
size;
int separate = size / 2;
while(separate > 0)
{
for(int i = separate;i < size;++i)
{
int temp = data[i];
int j = i - separate;
while(j >=0 && data[j] > temp)
{
data[j+separate] = data[j];
j = j-separate;
}
data[j+separate] = temp;
}
separate /= 2;//遞加增量
}
}
總結: 每種算法都要它實用的前提,本文也僅僅是回想了下基本。有須要的可以參考。