JZ56 删除链表中重复的结点

这篇博客介绍了如何在排序链表中删除重复的结点,提供了递归和循环两种解法。递归方法通过比较头结点与头结点的next值来决定是否保留头结点,并递归处理后续链表;循环遍历方法使用两个指针prev和cur,遍历链表并跳过重复结点。两种方法的时间复杂度均为O(N),空间复杂度分别为O(N)和O(1)。

描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

官方有循环遍历和递归两种解法。

先来看递归:https://blog.nowcoder.net/n/70a35462c5ba412e9a9f9fc70a875dd4?f=comment

①如果链表头结点为空,或者只有头结点,则直接返回pHead。

②然后就是判断头结点等不等于头结点的next,也就是对pHead.val和pHead.next.val的值进行判断,如果不相等,证明头结点的值不重复(因为链表是排序的),所以保留头结点,然后对pHead.next进行递归,即保留pHead,以pHead.next为头结点的链表重新放到函数中来调用,直到去除值重复的点。

那如果头结点的值跟它的next的值重复了呢?这个时候本来应该要删除头结点,但是因为不知道有几个跟头结点值重复的结点,所以需要重新设置一个move指针,一直向后遍历寻找到与 pHead.val不相等的结点(至此前面的结点都是和头结点重复的,去掉),所以move指针这个时候指向的就是一个新的头结点了,把这个头结点代到函数中调用,直到去除所有值重复的点。

class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        if not pHead&nbs***bsp;not pHead.next:
            return pHead
        if pHead.val != pHead.next.val:
            pHead.next = self.deleteDuplication(pHead.next)
        else:
            move = pHead.next
            while move and pHead.val == move.val:
                move = move.next
            return self.deleteDuplication(move)
        return pHead

时间复杂度O(N):N是链表节点数量,递归遍历一次。

空间复杂度O(N):递归调用的时候会用到了系统的栈。

再来看看循环遍历:

设置两个指针,一个是prev,一个是cur。prev初始在head位置,cur初始在pHead位置,也就是prev.next=cur,然后cur一直遍历下去,判断cur和cur.next的值是否相等,相等cur就后移一位,直到不相等时,cur相当于指向的是重复结点的最后一位(比如1,2,3,3,4,4,5,cur指向的就是第二个3),这个时候让prev.next=cur.next,等于说跳过了重复结点,cur=cur.next也后移到新的结点上,继续遍历,直到最后一个重复结点。

class Solution:
    def deleteDuplication(self, pHead):
        # 定义头节点指针,前指针和当前指针
        p0 = ListNode(-1)
        p = p0
        p.next = pHead
        cur = pHead
        # 循环遍历
        while cur and cur.next:
            # 不相等,则前后指针继续遍历
            if cur.val != cur.next.val:
                p = p.next
                cur = cur.next
            else:
                # 出现重复节点,即当前指针节点与下一个节点相等,则cur继续向右判断
                val = cur.val
                while cur and cur.val == val:
                    cur = cur.next
                p.next = cur
        return p0.next

时间复杂度O(N):需要遍历链表,N为链表长度。

空间复杂度O(1):只用到定义的指针。

在C语言中,删除循环链表中已知结点的前驱需要特别注意,因为循环链表的特性是最后一个结点next指针指向第一个结点,形成一个环。因此,在删除操作中,我们需要确保不会破坏循环链表的结构,同时要正确地处理边界情况。以下是删除循环链表中已知结点(记为node)的前驱结点的一般步骤: 1. 检查链表是否为空或者只有一个结点,这种情况下不需要删除操作。 2. 如果链表中只有一个结点且该结点就是要删除的前驱结点,那么删除链表将变为空链表。 3. 如果要删除结点的前驱结点next指向自身,则说明要删除结点就是链表的第一个结点,我们需要重新定位链表的新头结点。 4. 遍历链表以找到要删除结点的前驱结点,注意不要遍历到链表的尾部,因为链表是循环的。 5. 修改找到的前驱结点next指针,使其跳过要删除结点,直接指向该结点的下一个结点。 6. 如果要删除结点是最后一个结点,我们需要更新循环链表的最后一个结点next指针,指向新的第一个结点。 示例代码如下(不包含头文件和必要的错误处理): ```c typedef struct Node { int data; struct Node *next; } Node; void deleteNode(Node **head, Node *node) { if (head == NULL || *head == NULL || node == NULL) { // 处理空指针的情况 return; } if (*head == node) { // 处理删除结点的情况 Node *temp = *head; while (temp->next != *head) { temp = temp->next; } temp->next = (*head)->next; free(*head); *head = temp->next; } else { // 寻找要删除结点的前驱结点 Node *current = *head; while (current->next != *head && current->next != node) { current = current->next; } if (current->next == node) { // 删除中间的结点 current->next = node->next; free(node); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值