(一)直接插入排序 Insertsort
定义一个[0,end]的待比较范围,保存好end+1位置上的数据tmp,让end+1位置上的数据与该范围内的数据反着比较,以升序为例,即tmp比end位置上的数据小,就让end位置上的数据向后挪动覆盖end+1位置上的数据,再让end--,接着与end位置上的数据(原先end-1位置上的数据)进行比较,但要是tmp更大,就不用挪动了也不用end--了,直接将tmp插入到后一位end位置上就完成了tmp的排序,倘若直到end<0都没有存在tmp更大的情况,那就直接把将X插入到后一位end位置上(也就是第一位)就行了,如此,便完成了对tmp的排序。
然后再扩大待比较范围,让end逐步变大,内里的逻辑都是一样的,等到end=n-1执行完后,直接插入排序就完了,数组也就实现升序了。
(1)代码实现
void Insertsort(int* arr, int n)
{
for (int i = 0;i < n-1;i++)
{
int end = i;
int tmp = arr[end + 1];
//会覆盖,所以需要提前记录下来
while (end >= 0)
{
//处理比较、后移覆盖的问题
if (arr[end] > tmp )
{
arr[end + 1] = arr[end];
end--;
}
else
break;
}
arr[end + 1] = tmp;
}
}
(2)细节理解
①end的意义是什么?为什么是在end+1位置上插入tmp?

end代表的是与待排数据比较的数的下标,从右向左,end在每次比较后如果tmp没找到正确的位置,就会end--,要么找到tmp的正确位置,要么end一直--,直到走到0的左边结束比较循环,所以[0,end]也是待排数据需要比较的范围。
空位置都是end+1对应的位置,一开始,把end+1位置上的值赋给tmp之后,虽然end+1位置上还是有数据,可是end+1位置其实已经相当于是个可以被随意覆盖的空位了,在比较过程中,end--,数据挪动后,新的end位,就又有新的end+1空位,所以tmp数据最后插入的数据就应该是空位,也就是end+1对应的位置。
②两层循环各自代表了什么?循环条件又作何理解?

内层循环循环条件:
待排数据每次都与end位置上的数据进行比较,直到end位置没有数据,也就是超出边界,所以循环条件是end>=0。
外层循环循环条件:
从end的角度出发,要确保除了最后一个数都能成为被比较数据的边界,那么从0第一个数据开始,到n-2就是倒数第二个数据,那么循环条件就是[0,end-2] = [0,end-1)。
至于为什么最后一个数不能成为边界,是因为end+1数据要被提前存储,如果最后一个数成为了边界,那end+1就在界限外,也就是越界访问了。
③为什么可以有else break;?
每个待排数据进行比较前,它前面的[0,end]范围内的数据已经成序了,一旦不再需要挪动,就说明找到正确位置,可以将tmp插入到end+1的位置了,所以就不用再去if判断了,直接跳出内层循环执行插入操作,然后上新的待排数据。
(二)希尔排序
当待排数组和题目要求排成的数组顺序完全相反或差距较大时,性能就比较低,希尔排序能改善优化直接插入排序中存在的问题,希尔排序可以用预排序+直接插入排序,两个关键词来概括。
(1)预排序

预处理的逻辑是:定义一个gap量,将原先的数组按gap间隔划分成数个小组,再让每个进行组内排序。
以图为例,假设gap为3,那么间隔为3的2,8,3,9就构成一个小组,4,10,5也构成一个小组,6,1,7同样构成一个小组,让三个小组进行组内的排序,这就是一次预排序。
让gap减小,进行多次预排序,直到gap=1时进行的排序就相当于最后的直接插入排序了,其实就是每个数据各自成组,一次性全部排了。
(2)代码实现
①三层循环,各组依次排序

②四层循环,各组同时排序

(3)细节理解
①gap=gap/3+1的详细理解
gap/3到最后的结果只会有三种可能性,0,1,2,那么0/3=0+1=1,1,2/3=0+1=1,无论是哪种情况,在gap/3+1后,都能保证gap有等于1的情况出现,符合要求。
②三层和四层的殊途同归
四层循环:严格按照划分好的小组依次进行排序,以gap=3的图举例,即先排完红色组的四个数据的顺序,再排绿色组的三个数据的顺序,最后排蓝色组的三个数据的顺序。然后改变gap的数据后,再重复操作。
三层循环:从前向后进行挨个排序,看上去像是大乱炖,但实则每次比较时,比较的数据都是划分好的小组内的数据,不会存在小组间的数据排序,也就是多条比较线并行。然后改变gap的数据后,再重复操作。
③三层、四层循环的每层理解以及循环条件的意义
四层循环:

红色循环条件:待排数据每次都与end位置上的数据进行比较,直到end位置没有数据(end=end-gap),也就是超出边界,所以循环条件是end>=0。
绿色循环条件:防止越界访问。保证小组内除了最后一个数据,都能成为end边界,由于存储的数据是end+gap,为了防止越界访问,所以end<n-gap。

蓝色循环条件:保证每个小组都能进行排序,那循环的次数就应该是小组数。我是这样判断小组数的,譬如当gap=3时,以红色组为例,2和8之间算上2不算8,一共有3个数(每个空格都看向前一个数据),这3个数要出现在不同组里,那么数组就能被划分成3个小组。那么gap个空格,也就说明有gap个数据要出现在不同组里,那么数组就能被划分成gap个小组。所以j<gap。
紫色循环条件:最后一次循环是gap=1,所以进最后一轮循环前gap肯定是要大于1的,如果gap小于等于1,就说明已经经历过gap=1的最后一轮循环了,那哪还有什么所谓的进最后一轮循环?
------end------