剔除单向链表重复值节点时的烦人bug

一、初步实现:

以前在写单项链表重复值删除的时候,用的是先排序然后进行重复值剔除的方法。重复值剔除完毕之后,得到的元素也是有序的,然后顺利的写完了,没有出现任何bug。也就没有去尝试新的方法去做,代码如下:

# include<stdio.h>
# include<stdlib.h>
# include<time.h>

typedef struct Node{
    int data;
    struct Node* pNext;
}NODE,*PNODE;

PNODE Create(int n);//采用rand()函数生成10~14的随机数来建链
void Print(PNODE pHead);//遍历
void DeleteRepeatKey_Link(PNODE pHead);//有序删除
void SortOne_Link(PNODE pHead);//排序

int main (void)
{
    int n;
    PNODE pHead = NULL;
    printf("请输入您要生成的随机数个数\n");//为观察出效果,n在20~50个为宜
    scanf("%d",&n);
    pHead = Create(n);
    Print(pHead);

    DeleteRepeatKey_Link_One(pHead);
    Print(pHead);

    return 0;
}

PNODE Create(int n)
{
    int i;
    PNODE pHead = NULL,pNew = NULL;
    srand(time(NULL));

    for(i = 0; i<n; i++)
    {
        pNew = (PNODE)malloc(sizeof(NODE));
        if(NULL == pNew){
            printf("内存分配失败!\n");
            exit(EXIT_FAILURE);
        }
        pNew->data = rand()%5 + 10;
        pNew->pNext = pHead;
        pHead = pNew;
    }
    return pHead;
}
void Print(PNODE pHead)
{
    PNODE p = pHead;
    for(p;p;p=p->pNext)
        printf("%-4d",p->data);
    printf("\n");
}
void DeleteRepeatKey_Link(PNODE pHead)
{
    PNODE p = pHead,q;
    q = p->pNext;

    Sort_Link(pHead);
    if(pHead == NULL){
        printf("链表为空,无需删除所谓重复项!\n");
        exit(EXIT_FAILURE);
    }

    for(;q;q = p->pNext)
        if(p->data == q->data){
            p->pNext = q->pNext;
            free(q);    
        }else
            p = q;
}
void Sort_Link(PNODE pHead)
{
    PNODE p = pHead,q;

    for(; p; p=p->pNext)
        for(q=p; q; q=q->pNext)
            if(p->data > q->data){
                int temp = p->data;
                p->data = q->data;
                q->data = temp;
            }
}

二、新思路编写代码出现BUG

但是今天在重新写该程序的时候,由于没有提前排序。而是采用了pOne指向链表的头节点的指针,然后依次用后面的节点数据域与pOne指向的节点数据域来比较,当相等时,删除后面相等的那个节点。遍历一遍之后,pOne指向的节点数据域就是无重复值的,然后pOne后移,再次重复以上步骤。双层循环跑完之后就完成了剔除所有重复值的操作。但是依照下面的代码,却出现了一个让我苦恼了好一阵的Bug。代码如下:

void DeleteRepeatKey_Link(PNODE pHead)
{
    PNODE pOne = pHead,pFind = NULL,qFind = NULL;
    if(!pHead){
        printf("链表为空!\n");
        exit(EXIT_FAILURE);
    }
    for(pOne;pOne->pNext;pOne = pOne->pNext)//bug的产生之处
    {
        qFind = pOne;
        pFind = qFind->pNext;
        while(pFind){
            if(pOne->data - pFind->data){
                qFind = pFind;
                pFind = pFind->pNext;
            }
            else{
                qFind->pNext = pFind->pNext;
                free(pFind);
                pFind = qFind->pNext;
            }
        }
    }
}

首先,pOne指向pHead,由于pFind=qFind->pNext=pOne->pNext,所以退出外层循环的条件是pFind不为空,也就是pOne->pNext不为空。(但是此时我就给自己把坑挖娃好了,并且完全没有怀疑地认为这毫无破绽,十分完美!实际上我因为太过自信而跳进了自己的坑)。
其次,内层循环退出的条件是pFind不为空(到这时我依旧没有发现我跳进了自己挖的坑中)。并运行了一遍程序“完全正确”(如图1所示),但是当我给朋友演示该代码的时候,BUG出现了(如图二所示):

图1:
这里写图片描述

图2:
这里写图片描述
然而当我把以上代码稍作修改之后,就消除了该Bug。代码如下:

void DeleteRepeatKey_Link(PNODE pHead)
{
    PNODE pOne = pHead,pFind = NULL,qFind = NULL;
    if(!pHead){
        printf("链表为空!\n");
        exit(EXIT_FAILURE);
    }
    for(pOne;pOne;pOne = pOne->pNext)//修改的唯一一处
    {
        qFind = pOne;
        pFind = qFind->pNext;
        while(pFind){
            if(pOne->data-pFind->data){
                qFind = pFind;
                pFind = pFind->pNext;
            }
            else{
                qFind->pNext = pFind->pNext;
                free(pFind);
                pFind = qFind->pNext;
            }
        }
    }
}

运行结果:
这里写图片描述

BUG分析:
一直只是在内循环中判断内循环执行条件是否满足,并且认为外循环pOne->pNext不为空就是pFind不为空。BUG会不会表现出来的条件是:最后一次进入内循环时,剩余的数是否有重复值。

(1)、

第一次测试中,13,14,11一次剔除重复值之后,pOne指向数据域为12的节点,退出内循环,此时pOne并不在尾节点之上,pOne->pNext也不为空,外循环pOne后移使得pOne指向数据域为11的节点,此时pOne->pNext已经为空,直接就结束循环,最后一组数不用剔除重复值,因为最后一组已经是没有重复的了,不会出现进入内层循环之后pOne->pNext才为空的情况。

(2)、

而第二次测试中,11,13,12,14四组依次剔除重复值之后,最后一组4个元素:连续的4个10使得pOne最终跳出内循环时指向pFind的前一个节点上,即pFind为空时,pOne指向尾节点,跳出内循环,再回到外循环时BUG出现了:
由于pOne=pOne->pNext;执行之后,pOne就为空了,再对外循环条件判断就成了矛盾的了:pOne为空,则pOne没有pNext,而外循环条件为pOne->pNext != NULL;判断时,系统找不到pOne的pNext,所以就崩溃了。

三、总结:

修改之后的程序,虽然外循环只考虑了pOne不为空,要有数据域;没有考虑pFind是否为空,但是即使pFind为空进入外层循环,在进内循环之前也会被识别出来。外循环替内循环考虑太多,却导致了该BUG的产生,有越俎代庖之嫌,值得深思!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值