目录
一、各排序算法的时间复杂度与空间复杂度比较
类别 | 排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最好情况 | 最坏情况 | 辅助存储 | |||
插入类排序 | 直接插入 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(n^1.5) | O(n) | O(n^2) | O(1) | 不稳定 | |
交换类排序 | 起泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
快速排序 | O(nlog2n) | O(nlog2n) | O(n^2) | O(log2n) | 不稳定 | |
选择类排序 | 简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 | |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 | |
基数排序 | O(d(n+rd)) | O(d(n+rd)) | O(d(n+rd)) | O(rd) | 稳定 | |
二、直接插入排序
1、算法思想
每趟将一个待排序的关键字按照其值的大小插入到已经排好的部分有序序列的适当位置上,直到所有待排关键字都被插入到有序序列中为止。
2、代码实现
#include <iostream>
using namespace std;
void InsertSort(int R[],int n);
void outPutArr(int arr[],int n);
void main()
{
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
InsertSort(arr,len);
}
/**
* 直接插入排序算法
*/
void InsertSort(int R[],int n){
int i,j;
int temp;
for(i=1;i<n;i++){
temp = R[i]; //将待插入关键字暂存在temp中
j = i-1;
/*下面这个循环完成了从待排关键字之前的关键字开始扫描,如果大于待排关键字,则后移一位*/
while(j>=0&&temp<R[j]){
R[j+1] = R[j];
j--;
}
R[j+1] = temp; //找到插入位置,将temp中暂存的待排关键字插入
}
//对排序了的数组输出
outPutArr(R,n);
}
/**
* 数组的输出函数
*/
void outPutArr(int arr[],int n){
cout <<"<直接插入排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<";\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)最好情况时间复杂度:O(n);
(2)最坏情况时间复杂度:O(n2);
(3)平均时间复杂度:O(n2);
(4)空间复杂度:O(1);
三、希尔排序
1、算法思想
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
2、代码实现
#include<iostream>
using namespace std;
void shellSort(int R[],int n);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
shellSort(arr,len);
}
/**
希尔排序算法
*/
void shellSort(int R[],int n){
int temp;
for(int gap = n/2;gap>0;gap/=2){
for(int i = gap;i<n;i++){
temp = R[i];
int j;
for(j = i;j>=gap&&R[j-gap]>temp;j-=gap){
R[j] = R[j-gap];
}
R[j] = temp;
}
}
outPutArr(R,n);
}
/**
*数组的输出方法
*/
void outPutArr(int arr[],int n){
cout <<"<希尔排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)希尔自己提出的时间复杂度:O(n2);
(2)帕佩尔诺夫和斯塔舍维奇提出的时间复杂度:O(n1.5);
(3)空间复杂度:O(1);
四、起泡排序
1、算法思想
起泡排序属于比较简单的排序,以非递减为例,依次遍历数组,发现R[j-1]>R[j]的情况,则交换R[j-1]和R[j]的顺序,直到没有逆序的数据,即完成排序。
2、代码实现
#include <iostream>
using namespace std;
void bubbleSort(int R[],int n);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
bubbleSort(arr,len);
}
/**
起泡排序算法
*/
void bubbleSort(int R[],int n){
int i,j;
bool flag;
int temp;
for(i = n-1;i>0;i--){
flag = false; //flag用来标记此趟排序是否发生了交换
for(j = 1;j<=i;j++){
if(R[j-1]>R[j]){
temp = R[j];
R[j] = R[j-1];
R[j-1] = temp;
flag = true; //如果没发生交换,则flag为0
}
}
if(!flag){ //一趟排序过程中没有发生排序,则证明剩余序列有序,不在冒泡
outPutArr(R,n);
return;
}
}
}
/**
*数组的输出方法
*/
void outPutArr(int arr[],int n){
cout <<"<起泡排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<";\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)最好情况时间复杂度:O(n);
(2)最坏情况时间复杂度:O(n2);
(3)平均时间复杂度:O(n2);
(4)空间复杂度:O(1);
五、快速排序
1、算法思想
选取一个枢轴元素(通常为待排序序列的第一个数),然后通过一趟排序将比枢轴数大的放在右边,比枢轴小的放在左边,接着对划分好的两个子序列再进行上述的排序。
2、代码实现
#include<iostream>
using namespace std;
void outPutArr(int arr[],int n);
void quickSort(int R[] ,int low,int high);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int length = 9;
quickSort(arr,0,length-1);
outPutArr(arr,length);
}
void quickSort(int R[] ,int low,int high){
int temp;
int i = low, j = high;
if(low<high){
temp = R[low];
while(i<j){ //将数组中小于temp的放在左边,大于temp的放在右边
while(j>i && R[j]>=temp){ //从右往左扫描,找到一个小于temp的关键字
j--;
}
if(i<j){
R[i] = R[j]; //放在temp左边
i++; //右移一位
}
while(i<j && R[i]<temp){ //从左往右扫描,找到一个大于temp的关键字
i++;
}
if(i<j){
R[j] = R[i]; //放在temp右边
j--; //左移一位
}
}
R[i] = temp; //将temp放在最终位置
quickSort(R,low,i-1); //递归的对temp左边的关键字排序
quickSort(R,i+1,high); //递归的对temp右边的关键字排序
}
}
/**
*数组的输出方法
*/
void outPutArr(int arr[],int n){
cout <<"<快速排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)最好情况时间复杂度:O(nlog2n);
(2)最坏情况时间复杂度:O(n2);
(3)平均时间复杂度:O(nlog2n);
(4)空间复杂度:O(log2n);
六、简单选择排序
1、算法思想
选择类排序的主要动作是“选择”,简单选择排序采用最简单的选择方式,从头至尾顺序扫描序列,找出最小的一个关键字,和第一个关键字交换,接着从剩下的关键字中继续这种选择和交换,最终使序列有序。
2、代码实现
#include <iostream>
using namespace std;
void selectSort(int R[],int n);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
selectSort(arr,len);
}
/**
简单选择排序算法
*/
void selectSort(int R[],int n){
int i,j,k;
int temp;
for(i = 0;i<n;i++){
k = i;
/*下面这个循环是算法的关键,它从无序序列中挑出一个最小的关键字*/
for(j = i+1;j<n;j++){
if(R[k]>R[j]){
k = j;
}
}
/*下面这三句完成最小关键字与无序序列第一个关键字的交换*/
temp = R[i];
R[i] = R[k];
R[k] = temp;
}
outPutArr(R,n);
}
/**
*数组的输出
*/
void outPutArr(int arr[],int n){
cout <<"<简单选择排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<";\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)时间复杂度:O(n2);
(2)空间复杂度:O(1);
七、堆排序
1、算法思想
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
2、代码实现
#include<iostream>
using namespace std;
void outPutArr(int arr[],int n);
void sift(int R[] ,int low,int high);
void heapSort(int R[], int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int length = 9;
heapSort(arr,length);
}
/**
堆排序算法主方法
*/
void heapSort(int R[], int n){
int i;
int temp;
for(i = n/2-1;i>=0;i--){ //建立初始堆
sift(R,i,n-1);
}
for(i = n-1;i>0;i--){ //进行n-1次循环,完成堆排序
temp = R[0]; //一下3句换出根节点的关键字,将其放入最终位置
R[0] = R[i];
R[i] = temp;
sift(R,0,i-1); //在减少了一个关键字的无序序列中调整
}
outPutArr(R,n);
}
/**
堆的局部调整方法
*/
void sift(int R[] ,int low,int high){ //关键字设定下表从0开始
int i = low,j = 2*i+1; //R[j]是R[i]的左孩子节点
int temp = R[i];
while(j<=high){
if(j<high && R[j]<R[j+1]){ //若右孩子较大,则j指向右孩子
j++; //j变为2*i+2
}
if(temp<R[j]){
R[i] = R[j]; //将R[j]调整到双亲节点的位置
i = j; //修改i和j的值,继续向下调整
j = 2*i+1;
}else{
break; //调整结束
}
}
R[i] = temp; //被调整节点放入最终位置
}
/**
*数组的输出方法
*/
void outPutArr(int arr[],int n){
cout <<"<堆排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)时间复杂度:O(nlog2n);
(2)空间复杂度:O(1);
八、二路归并排序
1、算法思想
二路归并排序是采用的分而治之的思想。将一个待排序的序列分成两个序列,分别对这两个序列排序。而对于这两个序列排序的方式也是和之前一样,将这两个序列分别分成两个序列分别排序。一直这样分割下去,知道序列中没有元素或者只有一个元素为止。因为没有元素的序列和只有一个元素的序列定是一个有序的序列,所以相当于将这个序列排序完毕,向上返回。返回的过程中做的最重要的一件事就是将两个有序的序列合并成一个有序的序列。所以归并排序最重要的两步是分割和合并。
2、代码实现
#include<iostream>
using namespace std;
void mergeSort(int R[],int low,int high);
void merge(int R[],int low,int mid, int high);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int length = 9;
mergeSort(arr,0,length-1);
outPutArr(arr,length);
}
/**
归并排序
*/
void mergeSort(int R[],int low,int high){
if(low<high){
int mid = (low+high)/2;
mergeSort(R,low,mid); //归并排序前半段
mergeSort(R,mid+1,high); //归并排序后半段
merge(R,low,mid,high); //将R数组中low~mid,mid~high两段序列归并为一个序列
}
}
/**
将两个序列归并为一个有序序列
*/
void merge(int R[],int low,int mid, int high){
int i,j,k;
int n1 = mid - low +1;
int n2 = high - mid;
int left[n1],right[n2];
for(i = 0;i<n1;i++){
left[i] = R[low+i];
}
for(j = 0;j<n2;j++){
right[j] = R[mid + 1 + j];
}
i = 0;j = 0;k = low;
while(i<n1 && j<n2){
if(left[i]<=right[j]){
R[k] = left[i++];
}else{
R[k] = right[j++];
}
k++;
}
while(i<n1){
R[k++] = left[i++];
}
while(j<n2){
R[k++] = right[j++];
}
}
/**
*数组的输出方法
*/
void outPutArr(int arr[],int n){
cout <<"<归并排序算法>\r\n"<<endl;
cout <<"数组长度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、输出结果
4、时间复杂度与空间复杂度
(1)时间复杂度:O(nlog2n);
(2)空间复杂度:O(n);