剑指offer 学习笔记 删除链表的节点

面试题18:删除链表的节点。
(1)在O(1)的时间内删除链表节点。给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点,链表节点定义如下:

struct ListNode {
    int m_nValue;
    ListNode *m_pNext;
};

该问题的常规做法是从头节点开始,顺序遍历查找要删除的节点,并在链表中删除该节点。这种思路需要顺序查找,时间复杂度为O(n)。

删除一个节点并不需要遍历整个链表,只需要将当前要删除的节点内容换成的下一个节点的内容,再把下一个节点删除就好了。但如果要删除的节点是链表尾部的节点,我们就只能从链表头开始遍历直到该节点的前序节点再删除它了。最后还有一点,当链表中只有一个头节点,并且我们要删除它,在删除之后,还要将头结点设置为nullptr:

void DeleteNode(ListNode** pHead, ListNode* pToBeDeleted) {
    if (pHead == nullptr || *pHead == nullptr || pToBeDeleted == nullptr) {
        return;
    }

    if (pToBeDeleted->m_pNext != nullptr) {    // 如果要删除的节点不是尾节点
        ListNode* pNode = pToBeDeleted->m_pNext;
        pToBeDeleted->m_nValue = pToBeDeleted->m_pNext->m_nValue;
        pToBeDeleted->m_pNext = pToBeDeleted->m_pNext->m_pNext;    // 将要删除的节点内容替换成下一个节点内容
        
        delete pNode;    // 删除下一个节点,但指针没有被删除
        pNode = nullptr;    // 使指向被删除节点的指针变为空指针,否则该指针还是指向被删除对象的地址,不安全
    } else if (pToBeDeleted == *pHead) {    // 要删除的是只有头节点的链表
        delete pToBeDeleted;    // 直接删除头结点
        pToBeDeleted = nullptr; 
        
        *pHead = nullptr;    // 删除头节点后,还需让指向头节点的指针的值变为nullptr
    } else {    // 要删除的节点位于尾部且不是头节点
        ListNode* pNode = *pHead;
        while (pNode->m_pNext != pToBeDeleted) {
            pNode = pNode->m_pNext;
        }
        pNode->m_pNext = nullptr;
        delete pToBeDeleted;
        pToBeDeleted = nullptr;
    }
}

(2)删除链表中重复的节点。在一个排序的链表中,删除重复的所有节点。

首先头结点也可能与后面的节点相同,因此函数定义时头结点应该表示为头结点指针的指针。接下来从头遍历整个链表,如果当前节点与下一个节点值相同,那么它们就是重复的节点,都要被删除。为保证删除之后的链表仍然是相连的,我们要把当前节点的前一个节点和后边值比当前节点的值大的节点相连:

#include <iostream>
#include <stack>
using namespace std;

struct ListNode {
    int m_nValue;
    ListNode* m_pNext;
};

void AddToTail(ListNode** pHead, int value) {
    ListNode* pNew = new ListNode();    // 为新结点分配内存
    pNew->m_nValue = value;
    pNew->m_pNext = nullptr;

    if (*pHead == nullptr) {    // 如果是空链表
        *pHead = pNew;
    }
    else {
        ListNode* pNode = *pHead;

        while (pNode->m_pNext != nullptr) {    // 找到链表末尾
            pNode = pNode->m_pNext;
        }

        pNode->m_pNext = pNew;    // 将链表末尾元素的指针指向新节点
    }
}

void printListReversingly_iteratively(ListNode* pHead) {
    stack<ListNode*> nodes;

    ListNode* pNode = pHead;
    while (pNode != nullptr) {    // 将链表从头到尾压栈
        nodes.push(pNode);
        pNode = pNode->m_pNext;
    }

    while (!nodes.empty()) {    // 打印栈中内容
        cout << nodes.top()->m_nValue << endl;    // 打印栈顶值
        nodes.pop();    // 栈顶元素出栈
    }
}

void DeleteDplication(ListNode** pHead) {
    if (pHead == nullptr || *pHead == nullptr) {
        return;
    }

    ListNode* preNode = nullptr;
    ListNode* pNode = *pHead;
    while (pNode) {
        ListNode* pNext = pNode->m_pNext;
        bool isDup = false;    // 当前节点值是否与下一节点值相等
        if (pNext && pNode->m_nValue == pNext->m_nValue) {    // 如果是
            isDup = true;
        }

        if (!isDup) {    // 不用删除当前节点时
            preNode = pNode;    // 将当前节点设为preNode
            pNode = pNode->m_pNext;    // 后移一位
        } else {    // 需要删除当前节点时
            int value = pNode->m_nValue;    // 记录当前节点的值
            ListNode* toBeDel = pNode;    // 要删除的节点
            while (toBeDel != nullptr && toBeDel->m_nValue == value) {    // 当要删除的节点不为空且该节点与要删除到的节点值相同时,则删除
                pNext = toBeDel->m_pNext;    // 先将pNext指向要删除的节点之后

                delete toBeDel;    // 删除该节点
                toBeDel = pNext;    // 验证下一个节点是否应该删除
            }

            if (preNode == nullptr) {     // 如果preNode的值还为空,说明刚刚删除的是头结点
                *pHead = pNext;    // 令头结点的值为最后一个删除的节点的后一个结点
            } else {    // 如删除的不是头结点
                preNode->m_pNext = pNext;    // 令第一个删除的节点之前的节点与最后一个删除的节点之后的节点连接起来
            }
            pNode = pNext;    // 从最后删除的节点的后一个节点开始继续循环查找
        }
    }
}

int main() {
    ListNode* pHead = new ListNode();
    pHead->m_nValue = 0;
    pHead->m_pNext = nullptr;
    AddToTail(&pHead, 0);
    AddToTail(&pHead, 2);
    AddToTail(&pHead, 3);
    AddToTail(&pHead, 3);
    AddToTail(&pHead, 5);
    AddToTail(&pHead, 7);
    AddToTail(&pHead, 7);    // 向链表中插入节点
    DeleteDplication(&pHead);    // 删除重复值
    printListReversingly_iteratively(pHead);    // 打印验证
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值