您的位置:首页 > 编程语言 > C语言/C++

使用c++实现并分析各种排序算法

2017-05-07 21:49 281 查看
这几天在复习排序算法这一块,在这里关于排序算法做一个总结。为了方便起见我们在举例子时使用一个整形数组a={45,12,36,76,45,9,33,19,87,23}作为例子进行展开。排序算法从基本上讲可以分为插入排序,快速排序,选择排序,归并排序,基数排序这几种,当然还有其他的排序算法都是由这几种经过一定的变化得到的。下面我们就针对这几种排序算法展开讨论。

1、插入排序
总体思想:将一组数据前边已经排好序的数据视为有序区,后边的视为无序区。一趟插入排序是将无序区中的第一个元素插入到有序区适当的位置构成新的无序区和有序区。起     初有序区中只有第一个元素,后面的数据都在无序区中。根据将数据插入到有序区中的策略不同将插入排序算法分为三类:
1.1 直接插入排序
按顺序查找的方法从有序区的右侧依次向左找到插入位置,并且移动元素。然后将无序区中第一个数据插入到有序区中,完成一趟排序过程。
1)实现代码如下:
#include<iostream>
using namespace std;
void DirectInsertSort(int *a, int n)
{
for (int i = 1; i < n; ++i)
{
int temp = a[i];
int j = i - 1;
for (; j >= 0; --j)
{
if (a[j] > temp)
{
a[j + 1] = a[j];
}
else
{
break;
}
}
a[j + 1] = temp;
}
}
int main()
{
int a[10] = { 45, 12, 36, 76, 45, 9, 33, 19, 87, 23 };
DirectInsertSort(a, 10);
}
2)排序结果:



从排序结果可以看出,每一趟排序后的数据的位置不是最终的位置,只有在最后一趟排序结束后最终位置才确定。

3)性能分析

直接插入排序每一趟是将无序区中的第一个元素插入到有序区中。因此有n个元素,则需要n-1趟排序。

最好情况为原始数据已经按升序排好,此时在每一趟排序中不用移动任何元素。这种情况下总时间为n-1,时间复杂度为O(n);

最坏情况为原始数据是逆序的,此时第i趟排序需要移动i个元素。这种情况下总时间为1+2+3+...+ n-1 = (n-1)n / 2,时间复杂度为O(n^2)。

因此,直接插入排序的平均时间复杂度为O(n^2)。因此,当数据基本有序时使用直接插入排序是比较好的选择。

1.2 折半查找插入排序

总体思想还是插入排序的思想,即将无序区中第一个元素插入有序区适当位置构成新的无序区和有序区。但是在找到插入位置时使用了折半(二分)查找的思想在有序区中找到插入位置。

1)实现代码

#include<iostream>
using namespace std;
void BinaryInsertSort(int *a, int n)
{
for (int i = 1; i < n; ++i)
{
int temp = a[i];
int low = 0, high = i - 1;
//找到插入位置
while (low <= high)
{
int middle = (low + high) / 2;
if (a[middle] <= temp)
{
low = middle + 1;
}
else
{
high = middle - 1;
}
}
//将无序区中的第一个元素a[i]插入到有序区
for (int j = i - 1; j >= high + 1; --j)
{
a[j + 1] = a[j];
}
a[high + 1] = temp;
}
}
int main()
{
int a[10] = { 45, 12, 36, 76, 45, 9, 33, 19, 87, 23 };
BinaryInsertSort(a, 10);
for (int i = 0; i < 10; ++i)
{
cout << a[i] << " ";
}
return 0;
}


2)排序结果



从排序结果可以看出,每一趟排序后的数据的位置不是最终的位置,只有在最后一趟排序结束后最终位置才确定。

3)性能分析

从代码可以看出,折半插入排序首先需要在有序区使用二分查找的方式找到插入位置,然后哦将无序区中的第一个数据插入到有序区中。无论初始是否有序,折半查找排序的时间复杂度总是为O(n^2)。

1.3 希尔排序

希尔排序又叫做“缩小增量式排序”。它以一定的增量d将原数组中的数据分为d组,然后分别对每一组数据排序。然后缩小增量,获得新的若干组数据,此时的每一组数据已经基本有序,然后在对每一组数组排序。.....直到最后增量减小为1时,也就是所有的数据为一组进行排序。

那么这里有两个问题

question1:如何确定增量大小?

这个问题只是在严蔚敏老师的书中看到过“应使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。”增量序列的初始值应该设为 d = n/2,这样起始时每组中数据个数较少便于排序。经过一趟排序增量序列减少,最后增量为1时是最后一轮排序。

question2:给定一个增量,获得若干组数据,那么我们如何对这每一组数据进行排序呢?

起初时增量比较大,每一组中仅有很少的数据。经过若干轮排序后,每一组中的数据变多了,但是此时数据已经基本有序了。从这两方面分析,希尔排序中对每组数据排序使用直接插入排序较好。因为起初当数据较少时,直接插入排序效率还可以。当数据较多时,数据已经基本有序,此时使用直接插入排序更好。

1)实现代码

#include<iostream>
using namespace std;

void ShellInsertSort(int *a, int n)
{
int d = n;//初始的增量
while (d > 0)
{
d = d / 2;
for (int i = 0; i < d; ++i)
{
for (int j = i + d; j < n; j += d)
{
if (a[j] < a[j - d])
{
int temp = a[j];
int k = j - d;
while (k >= 0 && a[k] > temp)
{
a[k + d] = a[k];
k -= d;
}
a[k + d] = temp;
}
}
}
}
}
int main()
{
int a[10] = { 45, 12, 36, 76, 45, 9, 33, 19, 87, 23 };
cout << "初始序列为:";
for (int k = 0; k < 10; ++k)
{
cout << a[k] << " ";
}
cout << endl;
ShellInsertSort(a, 10);
cout << "排序后的序列为:";
for (int k = 0; k < 10; ++k)
{
cout << a[k] << " ";
}
cout << endl;
return 0;
}






2)排序结果



3)性能分析

希尔排序的分析是一个复杂的问题,以为它的时间是所取“增量”序列的函数,这涉及到一些数学上尚未解决的难题。

由于排序算法较多,分为多个博客写,写一篇会介绍冒泡排序和快速排序。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息