插入排序中最常见的是直接插入排序和希尔排序。
(1)直接插入排序其基本思想是:一个有序区和一个无序区,每次扫描扩大有序区,将无序区的数据不断的加入到有序区中。
例如:49、38、65、97、76、13、27、49'
第一趟排序:[13]、49、38、65、97、76、27、49'
第二趟排序:[13、27]、49、38、65、97、76、49'
第三趟排序:[13、27、38]、49、65、97、76、49'
第。。。。。
直接插入排序是稳定的排序,直接插入排序,设置监哨,减少了交换语句。
for (i=1;i<lenth;i++)
{
temp = m_num[i];//temp作为监哨,
j = i-1;
while (temp<m_num[j]&&j>=0)
{
m_num[j+1] = m_num[j--];//大于temp的值都将向后移动一位
}
m_num[j+1] = temp;
}
(2)希尔排序,是不稳定的排序,它先分组,经过几次排序后,使得组内元素基本有序,这样在进入最后一次插入排序进行插入排序时能够减少时间复杂度。它在最坏情况下的执行效率和在平均情况下的执行效率相比没有差很多。
小组排序在分组确定组的步长d,然后将R[d+1..n]分别插入各组当前的有序区。注意希尔排序中步长等于1的情况是一定要排序的否则会导致排序失败。
void CInsertSort::ShellPass(int l)
{
double n;
int k;
for (int j = l;j<lenth;j++)
{
n = m_num[j];
k = j - l;
while (n<m_num[k] && k>=0)
{
m_num[k+l] = m_num[k];
k = k -l;
}
m_num[k+l] = n;
}
}
void CInsertSort::ShellSort()
{
int d;
d = lenth;
do
{
d = d/3 + 1;
ShellPass(d);
}while (d != 1);
}
刚开始写希尔排序的时候,给它多加了一层循环,让它先分组排序,然后在组内排序
即:第一次分组(步长为3)
第一趟排序[49]、38、65、[97]、76、13、[27]、49'=>[27]、38、65、[49]、76、13、[97]、49'
第二趟排序27、[38]、65、49、[76]、13、97、[49']=>27、[38]、65、49、[49']、13、97、[76]
第三趟排序27、38、[65]、49、49'、[13]、97、76=>27、38、[13]、49、49'、[65]、97、76
第二次分组(步长为2)
第一趟排序[27]、38、[13]、49、[49']、65、[97]、76=>[13]、38、[27]、49、[49']、65、[97]、76
第二趟排序13、[38]、27、[49]、49'、[65]、97、[76]=>13、[38]、27、[49]、49'、[65]、97、[76]
第三次分组(步长为1)
一趟插入排序[13]、[38]、[27]、[49]、[49']、[65]、[97]、[76]=>[13]、[27]、[38]、[49]、[49']、[65]、[76]、[97]
排序完毕,但是仔细一看才发现是错的,正确的方法少了一层循环,排序的时候不是按照小组内元素的顺序排序的而是,先排序每组的第一个元素,在排序每组的第二个元素,依次类推
即:第一次分组(步长为3)
第一趟排序(49)、38、65、[97]、76、13、27、49'=>(49)、38、65、[97]、76、13、27、49'
第二趟排序49、(38)、65、97、[76]、13、27、49'=>49、(38)、65、97、[76]、13、27、49'
第三趟排序49、38、(65)、97、76、[13]、27、49'=>49、38、(13)、97、76、[65]、27、49'
第二次分组(步长为2)
第一趟排序(49)、38、[13]、97、76、65、27、49'=>(13)、38、[49]、97、76、65、27、49'
第二趟排序13、(38)、49、[97]、76、65、27、49'=>13、(38)、49、[97]、76、65、27、49'
第三趟排序13、38、(49)、97、[76]、65、27、49'=>13、38、(49)、97、[76]、65、27、49'
第四趟排序13、38、49、(97)、76、[65]、27、49'=>13、38、49、(65)、76、[97]、27、49'
第五趟排序13、38、49、65、(76)、97、[27]、49'=>13、38、49、65、(27)、97、[76]、49'
第六趟排序13、38、49、65、27、(97)、76、[49']=>13、38、49、65、27、(49')、76、97
第三次分组(步长为1)
一趟插入排序[13]、[38]、[27]、[49]、[49']、[65]、[97]、[76]=>[13]、[27]、[38]、[49]、[49']、[65]、[76]、[97]
这样顺次循环较少了循环次数
Shell排序的执行时间依赖于增量序列。
好的增量序列的共同特征:最后一个增量必须为1;应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在nl.25到1.6n1.25之间。