基於前天建立的順序表(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遍歷更充分。
記錄在此供參考學習,如有不足之處,敬請批評指標,歡迎交流。