写在前面
博客的全部代码以及测试用例全部已上传GitHub:直接插入排序&&希尔排序
直接排序
直接排序其实就是一次将无序空间向有序空间转换的过程,其实所有排序都是这样,只不过,在直接排序中,我们是将原本的数据内容分为了前后两个空间,前面的一个空间是有序的,而后面的空间无序,我要做的就是慢慢的将有序空间增长,将无序空间缩小,直到无序空间为0,我们就可以得到一个有序的数据链;
在上图中,我们大致可以观察出这套算法所运行的过程:
- 在原始数组中,默认第一个数字是有序的,其他后面的数字全部无序;
- 在无序的数字中,从前向后开始,取第一个数字与有序区域的数字从后向前比较;
- 如果有序中的数字大于该数字,就将大于的数字向后移一位;
- 直到遇到一个小于或者等于它的数字,就讲该数字插入到当前位置;
- 直到全部有序,否则就重复2-4的步骤;
//version1 时间复杂度O(N^2)
void InsertSort(int *array, size_t size)
{
for (size_t idx = 1; idx < size; ++idx)
{
int end = idx - 1;
int key = array[idx];
while (end >= 0 && (key < array[end]))
{
array[end + 1] = array[end];
--end;
}
array[end + 1] = key;
}
}
我们可以看到,在上面的代码中,确实比较简单,到那时事件复杂度达到了O(N^2);其实,我们可以在这个代码中找到一些,我们原来学习到的东西,替换它,比如在有序空间中找数字时,可以用到二分查找;
//version2 时间复杂度降低,利用二分查找来找位置
void InsertSort_OP(int *array, size_t size)
{
for (size_t idx = 1; idx < size; ++idx)
{
int end = idx - 1;
int left = 0;
int right = idx;
int key = array[idx];
while (left <= right)
{
int mid = left + ((right - left) >> 1);
if (key < array[mid])
right = mid - 1;
else
left = mid + 1;
}
while (end >= left)
{
array[end + 1] = array[end];
--end;
}
array[end + 1] = key;
}
}
希尔排序
从根本意义上来讲,希尔排序其实就是直接插入排序的一个改良版,希尔排序的速度要远胜于直接插入排序;
从图中可以看到:
1. 我们将原始数据分割按照某个个数分割为几个分组;
2. 然后一直按照默认的大小一直分割下去,知道只有一个元素时停止(前面如果按照规定的元素个数来划分有剩余的话就轮空);
3. 然后从下向上一次排序;知道数组全部有序;
4. 其中,我在画图的时候只是随意的切割方法,其实在很多前辈做了大量的测试后发现,当gap = gap/3+1;时是效率最高的;
//希尔排序 时间复杂度在N^1.25~1.6N^1.25
void ShellSort(int *array, size_t size)
{
int gap = size;
while (gap > 1)
{
gap = gap / 3 + 1;
for (size_t idx = gap; idx < size; ++idx)
{
int end = idx - gap;
int key = array[idx];
while (end >= 0 && (key < array[end]))
{
array[end + gap] = array[end];
end -= gap;
}
array[end + gap] = key;
}
}
}