插入排序
插入排序主要有直接插入排序,折半插入排序和希尔排序。其基本思想是将一个带排序的记录按其关键词的大小插入到前面已经排好序的子序列中。
1、直接插入排序:
(1)、为了实现将元素L[i]插入到已有的序列L[1......i-1]中,我们需要执行三个步骤(用L[ ]表示一个表,用L( )表示一个元素): 1 .查找出L(i)在L[1.......i]中的插入位置k。
2.将L[k......i]中的所有元素向后移动一位。
3.将L(i)复制到L(k)
c++测试代码如下:
#include<iostream> using namespace std; void InsertSort(int A[] ,int n) { int i, j; for (i = 2;i < n;i++) { if (A[i] < A[i - 1]) { //比较key值 A[0] = A[i]; //A[0]不存数据,作为哨兵 A[i] = A[i - 1]; for (j = i - 1;A[0] < A[j];--j) A[j + 1] = A[j]; //向后移位 A[j + 1] = A[0]; //插入 } } } void main() { int A[10] = { 0,43,55,33,22,11,44,67,53,75 }; //测试数组,A[0]不存数据,共九个数据 InsertSort(A, 10); for (int i = 1;i <10;i++) cout << A[i] << " "; while (1); }
输出结果如下:
(2)、算法分析
1.空间效率:只需一个记录的辅助空间(A[0]),因而空间复杂度为1
2.时间效率:时间效率取决于比较和移动的次数,并且比较和移动次数取决于表的初始状态。
在最好情况下:表中的元素全部已经有序,只需比较n-1次即可,因而时间复杂度为O(n)。
在最坏情况下:表中的元素全部为逆序,则需进行2+3+4......+n=(n+2)(n-1)/2次比较和3+4+5.....+n+1=(n+4)(n-1)/2 次比较。则复杂度为O(n2)。
平均情况下:考虑到表中个元素排列是随机的,可取最好情况和最坏情况的平均值n*n/4。则复杂度为O(n2)。
3.稳定性:由于每次移动前总是先比较。所以不会出现相同元素相对位置的改变,即直接插入算法是一个稳定的排序算 法。
4.适用性:直接插入排序算法适用于顺序存储和链式存储的线性表。当为链式存储时候可以从前往后查找指定元素位置。
2、折半插入排序
(1)、折半插入排序将比较和移动分离开来。即先折半查找找出待插入位置,然后在统一的移动待插入位置之后 的所有元素。 不同于直接插入排序边比较边移动元素。
测试代码如下:
#include<iostream> using namespace std; void HalfInsertSort(int A[], int n) { int i, j, high, low,mid; for (i = 2;i <= n;i++) { A[0] = A[i]; low = 1; high = i - 1; while (low <= high) { mid = (low + high) / 2; if (A[0] < A[mid]) high = mid - 1; else low = mid + 1; } for (j = i - 1;j >= high + 1;--j) A[j + 1] = A[j]; A[high + 1] = A[0]; } } void main() { int A[10] = { 0,19,33,23,43,45,66,75,232,34 }; HalfInsertSort(A, 10); for (int i = 2;i <=10;i++) cout << A[i] << " "; while (1); }
结果输出:
(2)、算法分析:易知折半插入排序仅仅减少了比较元素的次数,约为O(Log2n),比较次数与待排序表的初始状态无关,仅 取决于表中元素个数n,元素移 动次数没有改变,依赖于表的初始状态,因此折半插入排序的时间复杂度仍然为 O(n2)。
3、希尔排序
(1)、希尔排序又称为“缩小增量排序”,其基本思想是:先将整个待排记录序列分割成若干个子序列,分别进行直接插入排 序, 待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。
示例代码如下:
(2)、算法分析:#include<iostream> using namespace std; void ShellInsert(int A[], int dk,int n) { //对数据进行一趟希尔插入排序,和直接插入排序相比,前后记录的增量变为dk,而不是1 int i, j,num; for (i = dk;i <= n-1;i++) { if (A[i] < A[i - dk]) { num = A[i]; for (j = i - dk;j >= 0 && (num< A[j]);j -= dk) A[j + dk] = A[j]; A[j + dk] = num; } } } void ShellInsort(int A[], int dk,int n) { for (;dk >= 1;dk/= 2) ShellInsert(A, dk,n); } void main() { int A[10] = { 50,26,38,80,70,90,8,30,40,20 }; ShellInsort(A, 5, 10); for (int i = 0;i <10;i++) cout << A[i] << " "; while (1); }
结果输出:
1.空间效率:仅适用于了一个辅助单元,因而空间复杂度为O(1)。
2.时间效率:由于希尔排序的时间复杂度依赖于增量序列的函数,涉及数学上尚未解决的难题,则时间复杂度较难分 析,当n在某个特定范围内,希尔排序的时间复杂度约为O(n1.3)。在最坏情况下时间复杂度为o(n2)。
3.稳定性:当相同的关键字被划分为不同字表时,可能改变他们之间的相对次序,因此希尔排序算法是一种不稳定的 算法。
4.适用性:希尔排序算法仅适用于线性存储的情况。