哎,说到底还是很伤心的,最简单的直接插入排序我都写不好,555…
今天夜晚写了一个错的插入排序,觉得有必要记录下来
void insertsort(int arr[],int n)
{
int i,j;
for(i = 1;i<n;i++)
{
int e = arr[i]; //为当前要插入元素保存副本
for(j = i-1;j >= 0 ;j--) //与i的前一个来对比(注意这里循环结束的条件)
//这里循环结束的条件是j < 0 ,这里一个书写不是很好的是j可以取得值-1,但是并不是错误
{
if(arr[j] > e)
{
arr[j+1] = arr[j];
}
}
arr[j+1] = e;
}
}
这个程序的思想是:先给每一次要比较的元素创建一个副本,然后当需要交换顺序的时候,并不交换,而是通过赋值操作,这样来减少时间复杂度。
❗️这个程序是错误的
这里先介绍一下c语言中for循环的语法
for ( init; condition; increment )
{
statement(s);
}
下面是 for 循环的控制流:
1.init 会首先被执行,且只会执行一次。这一步允许您声明并初始化任何循环控制变量。您也可以不在这里写任何语句,只要有一个分号出现即可。
2.接下来,会判断 condition。如果为真,则执行循环主体。如果为假,则不执行循环主体,且控制流会跳转到紧接着 for 循环的下一条语句。
(这一句是重点了)
3.在执行完 for 循环主体后,控制流会跳回上面的 increment 语句。该语句允许您更新循环控制变量。该语句可以留空,只要在条件后有一个分号出现即可。
4.条件再次被判断。如果为真,则执行循环,这个过程会不断重复(循环主体,然后增加步值,再然后重新判断条件)。在条件变为假时,for 循环终止。
上面那个程序的错误就出在那个for循环中,我们再来看一下代码:
for(i = 1;i<n;i++)
{
int e = arr[i]; //为当前要插入元素保存副本
for(j = i-1;j >= 0 ;j--) //与i的前一个来对比(注意这里循环结束的条件)
//这里循环结束的条件是j < 0
{
if(arr[j] > e)
{
arr[j+1] = arr[j];
}
}
arr[j+1] = e;
}
这里的for循环写的是,只要j是大于等于0的我们就可以往下执行,所以,到最后,只有j = -1的时候才会跳出循环,而arr[j+1] = e;
这一句赋值操作永远都赋给了arr[0]了。
所以只要我们将代码改动一小部分,就可以完成我们的插入排序了
void insertsort(int arr[],int n)
{
int i,j;
for(i = 1;i<n;i++)
{
int e = arr[i];//所以这样写错误的
for(j = i-1;j >= 0 && arr[j] > e ;j--) //这里的循环条件变成了两个
{
//if(arr[j] > e)
//{
arr[j+1] = arr[j];
//}
}
//原来死在这里,这里每一次都是不满足条件后才执行,所以都赋值给1了?
arr[j+1] = e;
printArray(arr,n);
}
}
这里for循环执行下去的条件多了一个 arr[j] > e
,这样也就是说只有前一个元素比e大的时候,才会继续执行j--
操作。由于j的位置即为插入的位置,所以,此时我们就正确的找到了插入的位置~
2.带哨兵的插入排序法
哎,严奶奶的伪代码真的只适合聪明的人看。像我这样的渣渣,浪费了一个自习,还是颗粒无收,所以,今 天 一定要弄明白!!!
使用a[0]作为哨兵
for(i = 2;i < n; i++)
{
if(arr[i-1] > arr[i])
{
arr[0] = arr[i];
arr[i] = arr[i-1];
for(j = i -2 ; arr[j] > arr[0];j--) //这里最低在j = 0的时候也退出了
{
arr[j+1] = arr[j];
}
arr[j+1] = arr[0];
}
}
习题10.23答案:
void insertsort(int arr[],int n)
{
int i,j;
for(i = n-3;i >= 0; i--)
{
if( arr[i] > arr[i+1] )
{
arr[n-1] = arr[i];
arr[i] = arr[i+1];
for( j = i + 2; (arr[j] < arr[n-1]); j++)
arr[j-1] = arr[j];
arr[j-1] = arr[n-1];
}
}