常见排序算法汇总
注:学习心得,仅供参考。如有错误,请不吝赐教!
本文主要介绍如下常用排序算法:冒泡法、选择法、插入法、快速法和Shell排序法。
一、概述
定义:排序(sorting):将一组数据按一定规则调换位置,使数据具有某种次序关系(递增或递减)。
排序分类:
按照数据的移动方式分为两种:直接移动(直接交换存储数据的位置)和逻辑移动(不改变数据的原有存储位置,仅改变指向这些数据的辅助指针的位置)。
按照排序时使用的存储器的不同分为两种:内部排序(对小量数据在内存中进行排序)和外部排序(对大量数据无法在内存完成排序时而在辅助存储器进行的排序)。
主要排序算法:
常见内部排序算法:冒泡排序、选择排序、插入排序、合并排序、快速排序、堆排序、希尔排序、基数排序等。
常见外部排序算法:直接合并排序、k-路合并排序、多相合并排序等。
排序算法分析:
稳定性:算法是否稳定指数据经过排序后两个相同键值的记录依然保持原有的次序。
时间复杂度:排序算法完成排序所需要的时间,包括最好情况(Best Case)、最坏情况(Worst Case)、平均情况(Average Case)。
空间复杂度:指算法在执行过程中需要付出的额外存储器空间。排序算法所用到的额外空间越少,其空间复杂度越佳。如:冒泡排序在排序过程中只用到一个额外空间,是所有排序算法中空间复杂度最好的 。
二、内部排序算法特性
---------------------------------------------------------------------------------------------------
冒泡排序-Bubble Sort------------稳定;空间复杂度为O(1);时间复杂度最差、平均为O(n2),最好为O(n);
插入排序-Insertion Sort---------稳定;空间复杂度为O(1);时间复杂度最差、平均为O(n2),最好为O(n);
归并排序-Merger Sort------------稳定;空间复杂度为O(n);时间复杂度最差、平均、最好都为O(nlogn);
基数排序-Radix Sort-------------稳定;空间复杂度为O(n);时间复杂度为O(dn)--d为常数;
桶排序-Bucket Sort--------------稳定;空间复杂度为O(k);时间复杂度为O(n);
二叉树排序-Binary Sort----------稳定;空间复杂度为O(n);时间复杂度为O(nlogn);
----------------------------------------------------------------------------------------------------
选择排序-Selection Sort--------不稳定;空间复杂度为O(1);时间复杂度最差、平均为O(n2);
希尔排序-Shell Sort------------不稳定;空间复杂度为O(1);时间复杂度为O(nlogn);
堆排序----Heap Sort------------不稳定;空间复杂度为O(1);时间复杂度最差、平均、最好都为O(nlogn);
快速排序-Quick Sort------------不稳定;空间复杂度为O(logn);时间复杂度最差为O(n2),平均为O(nlogn);
-----------------------------------------------------------------------------------------------------
三、实现示例(C/C++)
1.冒泡排序
/****************************************BubbleSort()*********************************************
//
//算法思想:从第一个元素开始,比较相邻元素大小,若大小顺序有误,则对交换后再进行下一个元素的比较;
//接着再逐行进行第二次扫描,直到完成所有元素的排序为止。
//
//最坏及平均需要比较n(n-1)/2次,时间复杂度为O(n2);最好情况比较n-1次,时间复杂度为O(n)。
//
//输入:int data[]--数组数据;
//
*********************************************end*****************************************************/
void BubbleSort(int data[])
{
cout<<"--冒泡排序--"<<endl;
for(int i=9;i>=0;i--)
{
int flag=0;//用来判断是否有执行交换的动作
//由小到大排序
for(int j=0;j<i;j++)
{
if(data[j+1]<data[j])
{
int temp;
temp = data[j];
data[j] = data[j+1];
data[j+1] = temp;
flag++;//如果执行了交换,则flag不为0
}
}
if(flag==0)
break;
cout<<"第"<<10-i<<"次排序后: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl;
}
cout<<"排序结果为: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl<<endl;
}
2.选择排序
/****************************************SelectSort()*********************************************
//
//算法思想:首先将第一个位置的数据依次与2,3,4,...,N个位置的数据进行比较,如果数据>=其中一个位置,则不变;
//若<其中一个位置,则两个位置的数据交换;互换后继续与后续位置的数据进行比较,直到位置末端,此时第一个位置的数据为此
//排序数组的最大值。接下来选择第二个位置的数据依次与3,4,...,N个位置的数据进行比较,将最大值放到第二个位置。
//如此循环直到N-1个位置的最大值找到为止。
//
//最坏、平均、最好情况都需要比较n(n-1)/2次,时间复杂度为O(n2)。
//
//输入:int data[]--数组数据;
//
**********************************************end***************************************************/
void SelectSort(int data[])
{
cout<<"--选择排序--"<<endl;
for(int i=0;i<9;i++)
{
for(int j=i+1;j<10;j++)
{
if(data[i]>data[j])
{
int temp;
temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
cout<<"第"<<i+1<<"次排序: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl;
}
cout<<"排序结果为: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl<<endl;
}
3.插入排序
/****************************************InsertSort()*********************************************
//
//算法思想:逐一将数组中的元素与已经排序好的元素进行比较,再将该数组元素插入到适当的位置。
//此算法会造成数据的大量移动,建议用于链表结构。
//
//最坏及平均需要比较n(n-1)/2次,时间复杂度为O(n2);最好情况比较n-1次,时间复杂度为O(n)。
//
//输入:int data[]--数组数据;
//
*********************************************end****************************************************/
void InsertSort(int data[])
{
cout<<"--插入排序--"<<endl;
int i,j;//i为扫描次数,以j来定位比较的元素
for(i=1;i<10;i++)
{
int temp;
temp=data[i];
j=i-1;
while(j>=0 && temp<data[j])//如果第二个元素小于第一个元素
{
data[j+1] = data[j];//把所有元素往后推一个位置
j--;
}
data[j+1] = temp;//最小的元素放到第一个位置
cout<<"第"<<i<<"次扫描: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl;
}
cout<<endl;
}
4.希尔排序
/****************************************ShellSort()*********************************************
//
//算法思想:改进的插入排序
//
//最坏及平均需要比较n(n-1)/2次,时间复杂度为O(n2);最好情况比较n-1次,时间复杂度为O(n).
//
//输入:int data[]--数组数据; int size--数组元素个数.
//
*********************************************end****************************************************/
void ShellSort(int data[],int size)
{
cout<<"--希尔排序--"<<endl;
int i,j;
int k=1; //输出计数
int temp;
int step;
step = size/2;
/*
while(step != 0) //方式1:
{
for(i=step;i<size;i++)
{
temp=data[i];
j=i-step;
while(temp<data[j] && j>=0)
{
data[j+step] = data[j];
j -= step;
}
data[step+j] = temp;
}
cout<<"第"<<k++<<"次排序: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl;
step = step/2;
}
cout<<endl;
*/
for(step; step>0; step /= 2) //方式2:
{
for(i=step; i<size; i++)
{
for(j=i-step; j>=0 && data[j]>data[j+step]; j-=step)
{
temp = data[j];
data[j] = data[j+step];
data[j+step] = temp;
}
}
cout<<"第"<<k++<<"次排序: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl;
}
cout<<endl;
}
5.快速排序
/****************************************QuickSort()*********************************************
//
//算法思想:基本原理和冒泡排序一样都是利用交换的方式,区别在于本算法会先在数据中找到一个支点,
//把小于支点的数据放在左边而大于支点的数据放在右边,再以同样的方式分别处理左右两边的数据。
//
//最好及平均情况下时间复杂度为O(nlog2n),空间复杂度为O(log2n);
//最坏情况下时间复杂度为O(n2),空间复杂度为O(n)。
//特点:不稳定;平均执行时间最快。
//
//输入:int data[]--数组数据;int size--数组元素个数;int lf--数组首位;int rg--数组末位。
//*********************************************end****************************************************/
void QuickSort(int data[],int size,int lf,int rg)
{
int temp;
int lf_idx;
int rg_idx;
int t;
//1:第一个键值为data[lf]
if(lf<rg)
{
lf_idx=lf+1;
rg_idx=rg;
while(1)
{
cout<<"处理过程"<<process++<<"=> ";
for(int t=0;t<size;t++)
{
cout<<setw(3)<<data[t];
}
cout<<endl;
for(int i=lf+1;i<=rg;i++)//2:由左向右找到一个键值大于data[lf]
{
if(data[i]>=data[lf])
{
lf_idx=i;
break;
}
lf_idx++;
}
for(int j=rg;j>=lf+1;j--)//3:由右向左找到一个键值小于data[lf]
{
if(data[j]<=data[lf])
{
rg_idx=j;
break;
}
rg_idx--;
}
if(lf_idx<rg_idx)//4:若lf_idx<rg_idx
{
temp=data[lf_idx];
data[lf_idx]=data[rg_idx];//则data[lf_idx]和data[rg_idx]交换
data[rg_idx]=temp;
}
else
break;
}
if(lf_idx>=rg_idx)//5-1:若lf_idx>=rg_idx
{
temp = data[lf];
data[lf]=data[rg_idx];//则data[lf]和data[rg_idx]交换
data[rg_idx]=temp;
//5-2:以rg-idx为支点分成左右两半
QuickSort(data,size,lf,rg_idx-1);//以递归方式分别对左右两半进行排序
QuickSort(data,size,rg_idx+1,rg);
}
}
}
测试主程序
#include<iostream>
#include<iomanip>
#include<time.h>
using namespace std;
void BubbleSort(int data[]);//声明冒泡排序程序
void SelectSort(int *);//声明选择排序程序
void InsertSort(int *);//声明插入排序程序
void ShellSort(int *,int);//声明shell排序程序
void QuickSort(int *,int,int,int);//声明快速排序程序
int process=0;
int main(void)
{
int data[10] = {5,3,11,-1,9,50,10,8,1,20};
cout<<"原始数据为: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl<<endl;
//测试排序算法的时间
clock_t start_time=clock();
{
// BubbleSort(data);
// SelectSort(data);
// InsertSort(data);
// ShellSort(data,10);
cout<<"--快速排序--"<<endl;
QuickSort(data,10,0,9);
cout<<"排序结果为: ";
for(int i=0;i<10;i++)
cout<<setw(3)<<data[i]<<" ";
cout<<endl<<endl;
}
clock_t end_time=clock();
cout<<"排序用时:"<<static_cast<double>(end_time-start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl<<endl;
system("pause");
return 0;
}