探讨两种循环表示方法的区别,while循环与for循环的小总结
基于前天建立的顺序表(sequeue)的其中一个功能函数,引出两个循环的表示方法的区别和比较。
算法需求:在一个顺序队列中,合并相同的元素。
总体思路:利用两层循环的框架,利用外层循环选中顺序表中第一个数(L->data[i]),再用内循环中进行对比(L->data[j]),如果相同就进行删除操作。
首先采用for循环:
int list_purge(sqlink L)
{
int i = 1;
int j;
if (L->last == 0)//如果顺序表内只有一个函数,则无重复元素直接返回。
return 0;
for (i = 1; i <= L->last; i++)//外层循环,选取顺序表中一个数值
{
for (j = i - 1; j >= 0; j--)//内层循环,将外层循环的数值依次与i-1以内的数值进行比较
{
if (L->data[i] == L->data[j])//若出现相同的情况,则进行删除操作
{
list_delete(L, i);
break;
}
}
}
}
为减少功能函数间的耦合,用了独立的删除功能函数,具体可见另一篇《基于C语言的顺序表的建立,及各类功能函数实现》,因不是重点,不讨论该函数。
该算法较为简单,能实现基本功能。但有一个非常致命的问题:当出现连续三个需要操作的元素,无法连续删除其中两个。利用简单的几个数字测试,结果示例如下:
int main(int argc, const char *argv[])
{
sqlink L;
L = list_create();
list_insert(L, 10, 0);
list_insert(L, 20, 0);
list_insert(L, 30, 0);
list_insert(L, 20, 0);
list_insert(L, 30, 0);
list_show(L);
list_purge(L);
list_show(L);
return 0;
}
运行结果:
为方便分析,编写数组来说明,arr[0]=30;arr[1]=20;arr[2]=30;arr[3]=20;arr[4]=10;
在第一轮循环中,arr[1]!=arr[0],没有相同元素。
在第二轮循环中,arr[2]!=arr[1],arr[2] == arr[0],执行删除操作,删除之后变成了:arr[0]=30;arr[1]=20;arr[2]=20;arr[3]=10;即是在删除第一个相同元素后,整体元素往前移动。
在第三轮循环中,arr[3]!=arr[2],arr[3]!=arr[1],arr[3] != arr[0],即第三轮无法将改变后的a[2]与a[1]进行比较,因此出现运行结果的错误。
因此,考虑采用while循环优化:
int list_purge(sqlink L)
{
int i = 1;
int j;
if (L->last == 0)//如果顺序表内只有一个函数,则无重复元素直接返回。
return 0;
while (i <= L->last)//外层循环,选取顺序表中一个数值
{
j = i - 1;
while (j >= 0)
{
if (L->data[i] == L->data[j])//内层循环,将外层循环的数值依次与i-1以内的数值进行比较
{
list_delete(L, i);//若出现相同的情况,则进行删除操作
break;
}
else
j--;
if (j < 0)
i++;
}
}
分析如下,arr[0]=30;arr[1]=20;arr[2]=30;arr[3]=20;arr[4]=10:
在第一轮循环中,arr[1]!=arr[0],没有相同元素。
在第二轮循环中,arr[2]!=arr[1],arr[2] == arr[0],执行删除操作,删除之后变成了:arr[0]=30;arr[1]=20;arr[2]=20;arr[3]=10;整体元素往前移动。
将遍历指数j和i都放进内循环,导致的直接结果是,当进行list_delete(L, i)操作后,通过“break”退出内循环,会重新进行比较,成功解决了重复元素无法删除的问题。
因此运行结果如下:
...
通过这个例子,可较直接理解while与for循环的区别,即在遍历过程中,for的末尾循环体(increment)会直接需要应用在下次循环,而while可在遍历过程结束之后才进行increment操作。总结一句话就是:while遍历更充分。
记录在此供参考学习,如有不足之处,敬请批评指针,欢迎交流。