1.直接插入排序
直接插入排序就是一个将无序区的内容向有序区放的一个过程,有序区不断地变大,无序区不断的变小,直到最后全部变为有序,就完成了直接插入排序的过程了。
算法步骤:
1)假设待排序序列第一个元素是有序序列,第二个元素到最后一个元素当成是待排序序列。
2)从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
代码如下:
template<typename T>
void InsertSort(T* arr, int size)
{
int pos = 0;
for (int index = pos + 1; index < size; index++)
{
pos = index - 1;
T tmp = arr[index];
while (pos >= 0 && arr[pos] < tmp)
{
arr[pos + 1] = arr[pos];
pos -= 1;
}
arr[pos + 1] = tmp;
}
}
对于直接插入排序来说如果某一个序列是近似有序的,此时是直接插入排序最好的情况,它的时间复杂度为O(n),但是大多数的情况是数据并不是近似有序的,假设这样一种极端的情况该序列是逆序的,那仫在这种情况下直接插入排序的时间复杂度是O(n^2),此时就意味着我们每次进行插入排序的时候数据全部都需要移动,为了解决直接插入排序的这种极端的情况就涉及了今天的第二种插入排序–希尔排序.
2.希尔排序
希尔排序的思想就是一种类似的分治的思想,首先我们把一个序列分成多个模块,然后对每个模块中对应下标相同的元素进行排序,这样就可以达到最快速度的把大的放前面,小的放后面(如果是升序相反)。
希尔排序的实质是把序列变成接近有序的序列,然后进行一次直接插入排序。
关于希尔排序,其实就可以理解为让直接插入排序最差的情况变得更加高效。
希尔排序是直接插入排序的优化,在希尔排序的实现中我们采用了跳跃分隔的策略>将相距某个增量gap的记录组成一个子序列,并对这若干个子序列进行直接插入排序,当所有的子序列都插入完成后我们发现此时的序列已经是基本有序了。这样就可以达到最快速度的把大的放前面,小的放后面(如果是升序相反)。
算法步骤:
(1)预排序(可能存在多次预排序).
预排序之后的原始序列已经基本有序了,此时再进行直接插入排序就避免了最坏的情况了。
(2)直接插入排序
值得注意的是在希尔排序的预排序思路中我们是将每一个小分组按照直接插入的方法排成有序的,但是在代码的实现中下标变量的控制是从增量gap开始一直循环到最后一个变量,其实这是达到了效果的。在实现的过程中我们将分组与分组之间的插入排序不再变成分离的,而是对这个分组的某个数据排序插入之后就去排序插入另一个分组的数据。
这样当gap确定之后变量不停的在不同的分组之间进行插入排序,最终也完成了一趟预排序。
在希尔排序中最重要的就是确定增量gap,在代码的设计方案中涉及的增量的确定方案是gap=n/3+1,值得注意的是增量序列的最后一个增量值必须等于1才可以.当增量等于1的时候序列已经接近有序了此时再进行gap=1的希尔排序也就是相当于进行直接插入排序了.
代码如下:
#include<iostream>
#include<cstdlib>
using namespace std;
template<typename T>
void ShellSort(T *arr, int size)
{
int gap = size;
while (gap > 1)
{
gap = gap / 3 + 1; //确定增量
int pos = 0;
//希尔排序实际操作是按照顺序方式操作,并不是预想的一个模块一个模块的。
for (int index = pos + gap; index < size; index++)
{
pos = index - gap;
T tmp = arr[index];
while (pos >= 0&&arr[pos] < tmp)
{
arr[pos+gap] = arr[pos];
pos -= gap;
}
arr[pos+gap] = tmp;
}
}
}
希尔排序的最好情况对于直接插入排序而言就是最差情况,希尔排序的最差情况对于直接插入排序而言就是最好情况。