排序算法之选择排序、插入排序、希尔排序
1.选择排序
选择排序的思想:首先,找到数组中最小的那个元素,其次,将它与数组的第一个元素的位置进行交换,再在剩下的元素中找到最小的元素,与第二个元素的位置进行交换,如此循环往复,直到这个数组排序完成。
对于长度为N的数组,选择排序需要大约N^2/2次比较和N次交换。时间复杂度为N(N-1)/2,为O(NxN)级,空间复杂度是O(1)。
#include<iostream>
using namespace std;
void selectSort(int r[], int n) {
int i, index, j, temp;
for (i = 0; i < n; i++)//执行第i次扫描操作
{
index = i;
for (j= i+1; j < n; j++)//比较无序序列中的记录
{
if (r[index] > r[j])//记录序列中最小值的位置
{
index = j;
}
}
if (index != i)//如果无序序列中第一个记录不是最小值,则进行交换
{
temp = r[index];
r[index] = r[i];
r[i] = temp;
}
}
}
int main()
{
int r[] = { 1,4,65,64,6,7,8,3,43,35,6547,8,7,8754,87 };
int n = sizeof(r) / sizeof(r[0]);
selectSort(r, n);
for (int i = 0; i < n; i++)
{
cout << r[i] << " ";
}
system("pause");
return 0;
}
2.插入排序
插入排序很容易理解,在我们打扑克牌的时候,每一次摸完牌,都会按数字大小或者花色,插入到合适的位置,直到摸完最后一张牌,我们手中的牌已经按大小顺序排列好了。这整个过程就是一个插入排序。时间复杂度:O(n^2),空间复杂度:O(1)。
#include<iostream>
using namespace std;
void InsertSort(int a[], int n)
{
for (int j = 1; j < n; j++)
{
int key = a[j]; //待排序第一个元素
int i = j - 1; //代表已经排过序的元素最后一个索引数
while (i >= 0 && key < a[i])
{
//从后向前逐个比较已经排序过数组,如果比它小,则把后者用前者代替,
//其实说白了就是数组逐个后移动一位,为找到合适的位置时候便于Key的插入
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;//找到合适的位置了,赋值,在i索引的后面设置key值。
}
}
int main() {
int d[] = { 12, 15, 9, 20, 6, 31, 24 };
cout << "输入数组 { 12, 15, 9, 20, 6, 31, 24 } " << endl;
InsertSort(d, 7);
cout << "排序后结果:";
for (int i = 0; i < 7; i++)
{
cout << d[i] << " ";
}
system("pause");
return 0;
}
3.希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
算法步骤
1)选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2)按增量序列个数k,对序列进行k 趟排序;
3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对各子表进行
直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长
度。
希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n ^ 2)而Hibbard增量的希尔排序的时间复杂度为O(N^(5/4)),但是现今仍然没有人能找出希尔排序的精确下界。
步长序列(Gap Sequences)
步长的选择是希尔排序的重要部分。只要最终步长为 1 任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为 1 进行排序。当步长为 1 时,算法变为插入排序,这就保证了数据一定会被排序。
已知的最好步长串行是由 Sedgewick 提出的 (1, 5, 19, 41, 109,…),该步长的项来自 9 * 4^ i - 9 * 2^ i + 1 和 4^ i - 3 * 2^i + 1 这两个算式。这项研究也表明 “比较在希尔排序中是最主要的操作,而不是交换。” 用这样步长串行的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
复杂度分析:
最差时间复杂度 O(nlog2n)
平均时间复杂度 依赖于步长间隔 O(nlog2n)
最优时间复杂度 O(nlogn)
最差空间复杂度 O(n),辅助空间 O(1)
#include <iostream>
#include <vector>
#include <time.h>
#include <Windows.h>
using namespace std;
void Shell_sort(int a[], size_t n)
{
int i, j, k, group;
for (group = n / 2; group > 0; group /= 2)//增量序列为n/2,n/4....直到1
{
for (i = 0; i < group; ++i)
{
for (j = i + group; j < n; j += group)
{
//对每个分组进行插入排序
if (a[j - group] > a[j])
{
int temp = a[j];
k = j - group;
while (k >= 0 && a[k]>temp)
{
a[k + group] = a[k];
k -= group;
}
a[k] = temp;
}
}
}
}
}
int main(int argc, char**argv)
{
int a[10] = { 1,51,6,2,8,2,564,1,65,6 };
Shell_sort(a, 10);
for (int i = 0; i < 10; ++i)
{
cout << a[i] << " ";
}
cin.get();
}