链表的插入删除操作中,p != NULL 和 p->next !=NULL的区别

本文探讨了在链表操作中,条件p != NULL与p->next != NULL的区别。前者在遍历到末尾时会将p设为NULL,后者则允许在最后一个节点进行操作。在查找和插入过程中通常使用p != NULL,而在删除操作时,应使用p->next != NULL来确保正确判断节点数据。

1、首先两个表达式的输出情况

while(p!=NULL)
{
    printf("%d",p->data);
    p=p->next;
}

第一步:p指向第一个节点,第一个节点数据不为空;打印数据:1;p=p->next,指向第二个节点; 
第二步:p指向第二个节点,第二个节点数据不为空;打印数据:2;p=p->next,指向第三个节点; 
第三步:p指向第三个节点,第三个节点数据不为空;打印数据:3;p=p->next,指向第四个节点; 
第四步:p指向第四个节点,第四个节点数据不为空;打印数据:4;p=p->next,指向第五个节点; 
第五步:p指向第五个节点,第五个节点数据不为空;打印数据:5;p=p->next,指向第六个节点(不存在); 
第六步:p指向第六个节点(不存在),p为空;退出循环。
while(p->next!=NULL)
{
    printf("%d",p->data);
    p=p->next;
}
第一步:p->next指向第二个节点,第二个节点数据不为空;打印数据:1;p=p->next,指向第二个节点; 
第二步:p->next指向第三个节点,第三个节点数据不为空;打印数据:2;p=p->next,指向第三个节点; 
第二步:p->next指向第四个节点,第四个节点数据不为空;打印数据:3;p=p->next,指向第四个节点; 
第三步:p->next指向第五个节点,第五个节点数据不为空;打印数据:4;p=p->next,指向第五个节点; 
第四步:p->next指向第六个节点(不存在),第六个节点为空;退出循环

2、说明

前一个:p != NULL -- 一直到最后未查找到时,p为NULL,对p进行操作了,如p->data等

后一个: p->next !=NULL--一直到最后未查找到时,p为最后一个节点,可以对p进行操作判断其数据等

 

3、什么时候使用

查找、插入时,使用p != NULL

删除时,使用p->next !=NULL,这样可以对p->data的数据进行判断

 

上述代码实现的是**删除链表中相邻的重复节点**,保留第一个出现的节点。具体来说,该函数 `deval` 接收一个链表头结点指针 `header`,遍历链表,并删除所有与前一个节点数据相同的后续节点(即去除连续重复元素)。 ### 问题分析 假设链表是这样的: ``` 1 -> 2 -> 2 -> 3 -> 3 -> 3 -> 4 ``` 经过 `deval(header)` 处理后,结果应为: ``` 1 -> 2 -> 3 -> 4 ``` 但注意:这个函数只删除“连续”重复项。例如如果是 `2->3->2`,不会删除后面的 `2`,因为它不前面连续相等。 --- ### 完整可运行示例(C语言) ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构 typedef struct Node { int data; struct Node* next; } node; // 删除连续重复节点 void deval(node* header) { if (header == NULL || header->next == NULL) return; node* p = header->next; // 从第一个实际节点开始比较 while (p != NULL && p->next != NULL) { if (p->next->data == p->data) { node* q = p->next; p->next = q->next; free(q); q = NULL; } else { p = p->next; } } } // 辅助函数:创建新节点 node* create_node(int data) { node* new_node = (node*)malloc(sizeof(node)); new_node->data = data; new_node->next = NULL; return new_node; } // 辅助函数:打印链表 void print_list(node* header) { node* p = header->next; // 假设 header 是带头节点的哑节点 while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } // 测试程序 int main() { // 创建带头节点的链表 node* header = create_node(0); // 哑节点,方便操作 node* cur = header; int arr[] = {1, 2, 2, 3, 3, 3, 4}; int n = sizeof(arr)/sizeof(arr[0]); for (int i = 0; i < n; i++) { node* new_node = create_node(arr[i]); cur->next = new_node; cur = new_node; } printf("原始链表: "); print_list(header); deval(header); printf("去重后链表: "); print_list(header); // 释放内存... return 0; } ``` --- ### 对原函数的逐行解释: ```c void deval(node* header) { if (header == NULL || header->next == NULL) return; ``` - 如果头指针为空或链表没有实际节点(只有头),直接返回。 ```c node* p = NULL; p = header->next; ``` - 让 `p` 指向第一个有效节点(跳过头节点)。这里多此一举可以简化为 `node* p = header->next;` ```c while (p != NULL && p->next != NULL) { if (p->next->data == p->data) { node* q = p->next; p->next = q->next; free(q); q = NULL; } else { p = p->next; } } ``` - 遍历过程中检查当前节点 `p` 它的下一个节点 `p->next` 是否值相同。 - 若相同,则删除 `p->next` 节点(用临时指针 `q` 保存,然后释放)。 - 否则移动 `p` 到下一个节点。 ⚠️ 注意:**在删除节点后,`p` 不前进**,因为可能还有更多连续重复(如三个 3 连续)。下一次继续用 `p` 新的 `p->next` 比较。 --- ### 潜在问题与改进建议 1. **是否需要头节点?** - 当前代码使用了 `header` 并判断 `header->next`,说明可能是带“哑节点”(dummy head)的设计。 - 如果传入的是真正的头节点(无 dummy),且链表只有一个节点,也能正确处理。 2. **不能处理非连续重复** - 如 `1->2->1`,中间的 `2` 断开了,两个 `1` 不会被去重。 - 若要全局去重,需要用哈希表记录已见元素(适用于无序链表)。 3. **内存安全** - 使用 `free()` 后置 `q = NULL` 是良好习惯,但在函数作用域内意义不大。 4. **空指针访问风险** - 条件 `p != NULL && p->next != NULL` 确保不会解引用空指针,逻辑安全。 --- ### 扩展:如何实现**完全去重**(非仅连续)? ```c void remove_duplicates(node* header) { if (!header || !header->next) return; node* p = header->next; while (p) { node* q = p; while (q->next) { if (q->next->data == p->data) { node* temp = q->next; q->next = temp->next; free(temp); } else { q = q->next; } } p = p->next; } } ``` 这会删除链表中所有与 `p` 相同的数据(除了 `p` 自身),实现全局唯一。 --- ### 时间复杂度分析 - 当前 `deval`: O(n),每个节点最多访问一次。 - 全局去重版本:O(n²),嵌套循环。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值