1.插入排序
插入排序顾名思义是将待排数据插入已经排列好的数据中即有序数列中。实现原理是假设在一个总数为M的数列A中,前N-1个数据是有序数列,将第N个数插入前N-1个有序数列中。
实现过程如下:
遍历排列好的N-1个数据,假设 max = A[M] ,比较有序数列中最后一个元素即A中第N-1个元素即A[N - 1] > max,此时需要继续向前遍历比较A[N - 2] > max ?,假设一直到第j个元素,A[j] <= max,结束遍历
,将max放入A[j],同时将j~N-1个元素依次向后移动。
上述表格中是模拟整个插入过程。
数列int A[] = {2,3,4,5,3}前4个数据是有序数列,第5个数据是待排数据,int max = A[4] = 3。
第一步,比较 N与 j = N - 1即 max < A[3] = true,数列A[3]向后移动一位即 A[4] = A[3],同时max要比较下一位即j = j - 1 = N - 2;
第二步,比较 max < A[2] = true,A[2]值向后移动即A[3] = A[2],同时继续向前遍历 j = j - 1 = N - 3;
依次重复到 max < A[1] = false,结束遍历,那么A[1]保持原值,A[1 + 1] = max;
此过程代码实现如下:
int max = A[N];
int j = N - 1;
while((j >= 0)&&(max < A[j]){
A[j + 1] = A[j]; //数组值向后移动,因为j = N - 1,当A[N - 1] > max时,A[N] = A[N - 1],即 A[j + 1] = A[j]
j--;//依次向前遍历
}
A[j + 1] = max;
这是第N个数据的插入过程,在整个数列中,原始状态下,只有第一个数据是有序的,从第二个数据开始到最后我们都认为是无序的,从而从第二个数据开始到最后依次执行插入过程,就可以实现整个数列的排序。
代码如下:
void Sort(int *A, int len) {
int count = 0;
for (int i = 1; i < len; i++) {
int max = A[i];
int j = i - 1;
while ((j >= 0) && (A[j] > max)) {
count++;
A[j + 1] = A[j];
j--;
}
A[j + 1] = max;
}
cout << "loop count = " << count << endl;
}
插入排序快的原因是不用每个值都遍历整个数列,而最少与1个数据作比较,最多与N-1个数据做比较,因此减少了比较次数,进而加快速度。
2.希尔排序
希尔排序是在插入排序的基础上进行改进,从希尔排序中我们发现,插入第N个数据时,前N-1个数据越有序,排列速度越快。
希尔排序是将插入排序分为几个子序列先进行排序,最后将这些子序列进行整体的插入排序。这样整体上数列是从无序到有序的状态。
再用一个例子表示
首先要对数列进行分割,当然初始越小越好,最好是两个,因为两个直接比较不用进行循环。
这里我们设初始步长gap = len / 2,这样大致可以实现初次每两个数据进行插入排序,实际上就是比较。可以得出一个排好的序列。
如上图 int A = {1, 4, 3, 5, 8, 6, 9, 8, 3, 2}初次分组排序后数列为 {1, 4, 3, 3, 2, 6, 9, 8, 5, 8}
再进行第二次分组排列,步长 gap = gap / 2, 排列后
{1, 3, 2, 4, 3, 6, 5, 8, 9, 8}这样看,是不是比原始的
{1, 4, 3, 5, 8, 6, 9, 8, 3, 2}更有序点,
当步长gap = 1时进行全排列。
代码如下:
void Sort(int A[], int len) {
int count = 0;
int gap = len / 2;
while (gap >= 1) {
for (int i = gap; i < len; i++) {
int max = A[i];
int j = i - gap;
while ((j >= 0) && (A[j] > max)) {
count++;
A[j + gap] = A[j];
j = j - gap;
}
A[j + gap] = max;
}
gap /= 2;
}
cout << "loop count = " << count << endl;
}
从上述可以看出,当数列无序时希尔排序效率更高,但当数列有序时,插入排序显然要效率更高,希尔排序只是将无序的数据进行了更加有序的处理,从而便于插入排序的操作次数,核心还是插入排序。