以下是我对常见的几种排序算法的总结并给出的代码,基于C++语言实现,存储格式是顺序表。本人才疏学浅,如果有错漏还请各位指正。
一、存储格式:顺序表
int *data; //存储数据(data[1...size]存储待排序序列,data[0]为临时单元)
int size; //可存放容量
二、插入排序
1.直接插入排序:
(1)思路:当插入data[i](i>=2)时前面的data[1],data[2],...,data[i-1]已经排好序.这时用data[i]的排序码与data[i-1],data[i-2],...的排序码顺序进行比较,找到插入位置即将data[i]插入,原来位置上的对象向后顺移.
(3)时间复杂度:O(n^2)
(4)稳定性:稳定
(5)源代码:
//直接插入排序
void SeqList::directInsertSort()
{
int i, j;
for (i = 2; i <= size; i++)
{
data[0] = data[i]; //暂存
j = i - 1;
while (data[j] > data[0]) //查找插入位置
{
data[j + 1] = data[j];
--j;
}
data[j + 1] = data[0]; //插入
}
}
(6)分析:简单直观,工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。直接插入排序不适用与数据量大的排序,但是数据量较小,例如量级小于千时,插入排序还是一个不错的选择。
2.折半插入排序(又叫二分查找插入排序):
(1)思路:思路与直接插入排序相同,只是在查找插入位置时用二分查找提高效率,是对直接插入排序的改进。
(2)空间复杂度:只需要一个记录的附加空间,即data[0]
(3)时间复杂度:折半插入排序的比较次数数量级为O(n*log(n)),移动记录的次数和直接插入排序一样数量级为O(n^2),所以折半插入排序的时间复杂度为O(n^2)
(4)稳定性:稳定
(5)源代码:
//折半插入排序
void SeqList::halfInsertSort()
{
int i, j, low, high, mid;
for (i = 2; i <= size; i++)
{
data[0] = data[i]; //暂存
low = 1;
high = i - 1;
while (low <= high) //二分查找寻找插入位置
{
mid = (low + high) / 2;
if (data[0] < data[mid])
high = mid - 1;
else
low = mid + 1;
}
for (j = i - 1; j >= low; j--) //依次后移
data[j + 1] = data[j];
data[low] = data[0]; //插入
}
}
(6)分析:优劣点与直接插入排序相同。
3.希尔排序(缩小增量排序):
(1)思路:把所有记录按一定的增量分组,对每组用直接插入排序算法排序,随着增量的减少,各分组中包含的记录将越来越多,当增量减少至1时,所有记录序列变成一个组。由此可见,希尔排序巧妙地利用原有子序列的有序性。步长的选择是希尔排序的关键,一般增量取d1=n/2,di+1=di/2(Shell的取法),已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,...)。注意最后一个增量必须为1
一个更好理解的希尔排序实现:将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size
而不是i++
)。
例如,假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
然后我们对每列进行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
将上述四行数字,依序接在一起时我们得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了,然后再以3为步长进行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
排序之后变为:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
最后以1步长进行排序,此时就是简单的插入排序了(实例取自维基百科点击打开链接)(2)空间复杂度:只需要一个记录的附加空间,即data[0]
(3)时间复杂度:Shell算法的性能与所选取的分组长度序列有很大关系。只对特定的待排序记录序列,可以准确地估算关键词的比较次数和对象移动次数。想要弄清关键词比较次数和记录移动次数与增量选择之间的关系,并给出完整的数学分析,至今仍然是数学难题。
(4)稳定性:不稳定
(5)源代码:
//希尔排序
void SeqList::shellSort()
{
int i, j, gap;
gap = size / 2; //初始步长
while (gap >= 1) //减小步长直至为1(变为一般的直接插入排序)
{
for (i = gap + 1; i <= size;i++)
{
data[0] = data[i]; //暂存以及岗哨
j = i - gap;
while (data[j] > data[0]) //依次后移
{
data[j + gap] = data[j];
j -= gap;
}
data[j + gap] = data[0]; //插入
}
gap /= 2; //减小步长
}
}