C++ 八种排序算法总结及实现

时间:2024.4.13

八种排序算法总结之C++版本

五种简单排序算法

一、 冒泡排序 【稳定的】

void BubbleSort( int* a,int Count ) //实现从小到大的最终结果

{

int temp;

for(int i=1; i<Count; i++) //外层每循环一次,将最小的一个移动到最前面 for(int j=Count-1; j>=i; j--)

if( a[j] < a[j-1] )

{

temp = a[j];

a[j] = a[j-1];

a[j-1] = temp;

}

}

现在注意,我们给出O方法的定义:

若存在一常量K和起点n0,使当n>=n0时,有f(n)<=K*g(n),则f(n) = O(g(n))。(呵呵,不要说没学好数学呀,对于编程数学是非常重要的!!!)

现在我们来看1/2*(n-1)*n,当K=1/2,n0=1,g(n)=n*n时,1/2*(n-1)*n<=1/2*n*n=K*g(n)。所以f(n) =O(g(n))=O(n*n)。所以我们程序循环的复杂度为O(n*n)。

二、 交换排序 【稳定的】

void ExchangeSort( int *a,int Count)

{

int temp;

for(int i=0; i<Count-1; i++)

for(int j=i+1; j<Count; j++)

if( a[j] < a[i] )

{

temp = a[j];

a[j] = a[i];

a[i] = temp;

}

}

时间复杂度为O(n*n)。

三、 选择法 【不稳定的】

void SelectSort( int *a,int Count)

{

int temp; //一个存储值

int pos; //一个存储下标

for(int i=0; i<Count; i++)

{

temp = a[i];

pos = i;

for(int j=i+1; j<Count; j++)

if( a[j] < temp ) //选择排序法就是用第一个元素与最小的元素交换 {

temp = a[j];

pos = j; //下标的交换赋值,记录当前最小元素的下标位置 }

a[pos] = a[i];

a[i] = temp;

}

}

遗憾的是算法需要的循环次数依然是1/2*(n-1)*n。所以算法复杂度为O(n*n)。

我们来看他的交换。由于每次外层循环只产生一次交换(只有一个最小值)。所以f(n)<=n 所以我们有f(n)=O(n)。所以,在数据较乱的时候,可以减少一定的交换次数。

四、 插入法 【稳定的】

void InsertSort( int *a,int Count)

{

int temp; //一个存储值

int pos; //一个存储下标

for(int i=1; i<Count; i++) //最多做n-1趟插入

{

temp = a[i]; //当前要插入的元素

pos = i-1;

while( pos>=0 && temp<a[pos] )

{

a[pos+1] = a[pos]; //将前一个元素后移一位

pos--;

}

a[pos+1] = temp;

}

}

其复杂度仍为O(n*n)。

最终,我个人认为,在简单排序算法中,直接插入排序是最好的。

五、 希尔排序法 【不稳定的】

/*

* 希尔排序,n为数组的个数

*/

void ShellSort( int arr[], int n )

{

int temp,pos;

int d = n; //增量初值

do{

d = d/3 + 1 ;

for(int i= d; i<n; i++ )

{

temp = arr[i];

pos = i-d;

while( pos>=0 && temp < arr[pos] ) {

arr[ pos + d ] = arr[pos];

pos -= d;

}

arr[ pos + d ] = temp;

}

} while( d > 1 );

}

//实现增量为d的插入排序

三种高级排序算法

一、 快速排序 辅助空间复杂度为O(1) 【不稳定的】 void QuickSort( int *a,int left, int right)

{

int i,j,middle,temp;

i = left;

j = right;

middle = a[ (left+right)/2 ];

do

{

while( a[i]<middle && i<right ) //从左扫描大于中值的数 i++;

while( a[j]>middle && j>left ) //从右扫描小于中值的数 j--;

if( i<=j ) //找到了一对值

{

temp = a[i];

a[i] = a[j];

}

a[j] = temp; i++; j--; } } while ( i<j ); //如果两边的下标交错,就停止(完成一次) //当左半边有值(left<j),递归左半边 if( left < j ) QuickSort( a, left, j); //当右半边有值(right>i),递归右半边 if( i < right ) QuickSort( a, i, right);

它的工作看起来象一个二叉树。首先我们选择一个中间值middle,程序中我们使用数组中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(最容易的方法——递归)。注意,由于数据的随机性,对middle的选择并不会影响该算法的效率。

注意,在扫描过程中,对于给定参考值,对于向右(左)扫描,如果扫描值大(小)于或等于参考值,就需要进行交换。最终得到的结果是,j左边的值都小于参考值,而i右边的值都大于参考值,j和i之间的值都等于参考值。对j左边和i右边的分别使用递归,就可以完成最终的排序。

这里我没有给出行为的分析,因为这个很简单,我们直接来分析算法:首先我们考虑最理想的情况

1.数组的大小是2的幂,这样分下去始终可以被2整除。假设为2的k次方,即k=log2(n)。

2.每次我们选择的值刚好是中间值,这样,数组才可以被等分。

第一层递归,循环n次,第二层循环2*(n/2)......

所以共有n+2(n/2)+4(n/4)+...+n*(n/n) = n+n+n+...+n=k*n=log2(n)*n

所以算法复杂度为O(log2(n)*n)

其他的情况只会比这种情况差,最差的情况是每次选择到的middle都是最小值或最大值,那么他将变

成交换法(由于使用了递归,情况更糟),但是糟糕的情况只会持续一个流程,到下一个流程的时候就很可能已经避开了该中间的最大和最小值,因为数组下标变化了,于是中间值不在是那个最大或者最小值。但是你认为这种情况发生的几率有多大??呵呵,你完全不必担心这个问题。实践证明,大多数的情况,快速排序总是最好的。

如果你担心这个问题,你可以使用堆排序,这是一种稳定的O(log2(n)*n)算法,但是通常情况下速度要慢

于快速排序(因为要重组堆)。

二、 归并排序(两种实现方法均要掌握) 【稳定的】

归并排序是一种极好的外部排序方法,即针对数据保存在磁盘上而不是高速内存中的问题。

//以下程序参考数据结构课本P286页的模板,为使用指针链表实现的

#include <iostream>

using namespace std;

struct node{ //链表的节点数据

int value;

node *next;

};

node * divide_from( node * head )

{

node * position, * midpoint, * second_half;

if( (midpoint=head) == NULL ) //List is empty

return NULL;

position = midpoint->next;

while( position != NULL ) //Move position twice for midpoint's one move {

position = position->next;

if( position != NULL )

{

midpoint = midpoint->next;

position = position->next;

}

}

second_half = midpoint->next;

midpoint->next = NULL; //在这里将原链拆断,分为两段

return second_half;

}

node * merge( node * first, node * second)

{

node * last_sorted; //当前已经链接好的有序链中的最后一个节点

node combined; //哑节点

last_sorted = &combined;

while( first!=NULL && second!=NULL )

{

if( first->value < second->value ) {

last_sorted->next = first;

last_sorted = first;

first = first->next;

}else {

last_sorted->next = second;

last_sorted = second;

second = second->next;

}

}

if( first==NULL )

last_sorted->next = second;

else

last_sorted->next = first;

return combined.next; //返回哑节点的后继指针,即为合并后的链表的头指针 }

//这里的参数必须是引用调用,需要这个指引去允许函数修改调用自变量

void MergeSort( node * &head)

{

if( head != NULL && head->next != NULL ) //如果只有一个元素,则不需排序 {

node * second_half = divide_from( head );

MergeSort( head );

MergeSort( second_half );

head = merge( head, second_half );

}

}

int main()

{

node a,b,c,d;

node *p1, *p2, *p3, *p4,*head;

p1 = &a;

p2 = &b;

p3 = &c;

p4 = &d;

a.value = 2;

b.value = 4;

c.value = 3;

d.value = 1;

a.next = p2;

b.next = p3;

c.next = p4;

d.next = NULL;

//调用归并排序前的结果

head = p1;

while( head != NULL )

{

cout<<head->value<<" ";

head = head->next;

}

cout<<endl;

MergeSort( p1 );

//调用归并排序后的结果

head = p1;

while( head != NULL )

{

cout<<head->value<<" ";

head = head->next;

}

cout<<endl;

}

//以下程序为使用数组实现的归并排序,辅助空间复杂度为O(n)

#include <iostream>

using namespace std;

void Merge( int data[], int left, int mid, int right ) {

int n1,n2,k,i,j;

n1 = mid - left + 1;

n2 = right - mid;

int *L = new int[n1]; //两个指针指向两个动态数组的首地址 int *R = new int[n2];

for( i=0,k=left; i<n1; i++,k++)

L[i] = data[k];

for( i=0,k=mid+1; i<n2; i++,k++)

R[i] = data[k];

for( k=left,i=0,j=0; i<n1 && j<n2; k++) {

if( L[i] < R[j] ) { //取小者放前面

data[k] = L[i];

i++;

} else {

data[k] = R[j];

j++;

}

}

if( i<n1 ) //左边的数组尚未取尽

for( j=i; j < n1; j++,k++)

}

/*

* left:数组的开始下标,一般为0;right:数组的结束下标,一般为 (n-1) */

void MergeSort( int data[], int left, int right )

{

if( left < right )

{

int mid = left + ( right-left ) / 2; //mid=(right+left)/2,防止溢出 MergeSort( data, left, mid );

MergeSort( data , mid+1, right );

Merge( data , left, mid , right );

}

}

int main()

{

int data[] = {9,8,7,2,5,6,3,55,1};

//排序前的输出

for(int i=0; i<9; i++)

cout<<data[i]<<" ";

cout<<endl;

MergeSort( data, 0, 8);

//排序后的输出

for(int i=0; i<9; i++)

cout<<data[i]<<" ";

cout<<endl;

}

三、 堆排序 【不稳定的】

/*

* 向堆中插入current元素的函数

*/

void insert_heap( int data[], const int &current, int low, int high ) data[k] = L[j]; else //if( j<n2 ) //右边的数组尚未取尽 ,这句话可要可不要 for( i=j; i<n2; i++,k++) data[k] = R[i]; delete []L; //回收内存 delete []R;

{

int large; //元素data[low]左右儿子中,大者的位置

large = 2*low + 1;

while( large <= high ) {

if( large < high && data[large] < data[ large+1] )

large++;

if( current > data[ large ] ) //待插入元素的值比它的两个儿子都大 break;

else {

data[ low ] = data[ large ]; //将其左右儿子的大者上移

low = large;

large = 2 * large + 1;

}

}

data[ low ] = current;

}

/*

* 建立堆函数,num为数组data的元素个数

* 只有一个结点的<2-树>自动满足堆的属性,因此不必担心树中的任何树叶,即 * 不必担心表的后一半中的元素。如果从表的中间点开始并从后向前工作,就 * 能够使用函数insert_heap去将每个元素插入到包含了所有后面元素的部分堆 * 中,从而创建完整的堆。

*/

void build_heap( int data[], int num )

{

int current;

for( int low = num/2 - 1; low>=0; low-- ) {

current = data[ low ];

insert_heap( data, current, low, num-1 );

}

}

/*

* 堆排序主函数,num为数组data的元素个数

*/

void heap_sort( int data[], int num )

{

int current, last_sorted;

build_heap( data, num ); //建立堆

for( last_sorted = num-1; last_sorted>0; last_sorted-- ) { //逐个元素处理 current = data[ last_sorted ];

//data[0]在整个数组排序结束前,存储的是待排序元素中最大的元素

data[last_sorted] = data[0];

insert_heap( data, current, 0, last_sorted-1 );

}

}

int main()

{

//用于排序算法的输入输出

int a[8] = {5,7,1,2,9,4,6,3,};

for(int i=0; i< sizeof(a)/sizeof(int); i++) cout<<a[i]<<" ";

cout<<endl;

heap_sort( a, 8 ); //调用堆排序

for(int i=0; i< sizeof(a)/sizeof(int); i++) cout<<a[i]<<" ";

cout<<endl;

return 0;

}

更多相关推荐:
C语言排序方法总结

C语言排序方法学的排序算法有插入排序合并排序冒泡排序选择排序希尔排序堆排序快速排序计数排序基数排序桶排序没有实现比较一下学习后的心得我不是很清楚他们的时间复杂度也真的不知道他们到底谁快谁慢因为书上的推导我确实只...

排序算法总结C语言

排序算法总结一插入排序InsertionSort1基本思想每次将一个待排序的数据元素插入到前面已经排好序的数列中的适当位置使数列依然有序直到待排序数据元素全部插入完为止2排序过程示例初始关键字493865977...

c语言排序算法总结(主要是代码实现)

冒泡排序(BubbleSort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。…

c语言排序算法总结

一希尔Shell排序法Shell排序法includeltstdiohgtvoidsortintvintnintgapijtempforgapn2gapgt0gap2设置排序的步长步长gap每次减半直到减到1fo...

C语言排序算法小结

1稳定排序和非稳定排序简单地说就是所有相等的数经过某种排序方法后仍能保持它们在排序之前的相对次序我们就说这种排序方法是稳定的反之就是非稳定的比如一组数排序前是a1a2a3a4a5其中a2a4经过某种排序后为a1...

常用排序算法总结及C源程序

常用排序算法总结及C源程序直接插入排序思想先将有序序列中的第1个元素看作是有序序列的子序列然后从第2个记录开始逐个进行插入直至整个序列变成按关键字非递减的有序序列为止voidInsertSortintoutin...

C、C++ 经典排序算法 总结

=============================================================================相关知识介绍(所有定义只为帮助读者理解相关概念,并非严格…

C++ 八种排序算法总结及实现

八种排序算法总结之C版本五种简单排序算法一冒泡排序稳定的voidBubbleSortintaintCount实现从小到大的最终结果inttempforinti1iltCounti外层每循环一次将最小的一个移动到...

排序算法总结

排序算法总结概念1稳定度稳定排序算法会依照相等的关键换言之就是值维持纪录的相对次序也就是一个排序算法是稳定的就是当有两个有相等关键的纪录R和S且在原本的串行中R出现在S之前在排序过的串行中R也将会是在S之前2计...

细致的排序算法总结

更细致的排序算法总结一冒泡排序BubbleSort1基本思想两个数比较大小较大的数下沉较小的数冒起来2过程o比较相邻的两个数据如果第二个数小就交换位置o从后向前两两比较一直到比较最前两个数据最终最小数被交换到起...

各种排序算法小结

各种排序算法小结排序算法是一种基本并且常用的算法由于实际工作中处理的数量巨大所以排序算法对算法本身的速度要求很高而一般我们所谓的算法的性能主要是指算法的复杂度一般用O方法来表示在后面我将给出详细的说明对于排序的...

排序算法总结

各种排序方法比较1影响排序效果的因素1排序方法的选择1排序稳定2时间复杂度2各种排序方法比较简单排序中直接插入最好快速排序最快当排序序列为正序时直接插入和冒泡均最佳影响排序效果的因素因为不同的排序方法适应不同的...

c++排序算法总结(39篇)