面试题18:删除链表的结点

面试题18:删除链表的结点

题目1:在O(1)的时间内删除链表结点
给定单项链表的头指针和一个结点指针,定义一个函数在O(1)的时间内删除该结点。

分析:要在O(1)的时间内删除结点,按照常规的找到删除结点的上一个结点,然后删除该结点的思路肯定行不通。但是我们可以将待删除的结点的下一个结点的值赋给待删除结点,然后删除下一个结点,可以达到同样的目的。但以下几个问题要注意:

  1. 待删除的结点是单项链表最后一个结点;
  2. 待删除的结点是头结点(为简化处理,本次写的代码中头节点不包含数据,仅作标记,所以这条可以不考虑,但如果头节点不是标记包含数据就需要考虑);

代码:

//删除某个结点
void deleteNode(Node *pHead, Node *toDelNode)
{

    if (pHead == NULL || toDelNode == NULL)
        return;

    if (toDelNode->next != NULL)
    {
        Node *nextNode = toDelNode->next;
        toDelNode->value = nextNode->value;
        toDelNode->next = nextNode->next;

        delete nextNode;
        nextNode = NULL;
    }
    else
    {
        Node *node = pHead;
        while (node->next != toDelNode)
        {
            node = node->next;
        }
        node->next = NULL;
        delete toDelNode;
        toDelNode = NULL;
    }
}
///
///题目:删除单向链表中的某个结点,时间复杂度要求为O(1)
///

//思路:将删除结点的下一个结点的值复制到该结点,然后删除下一个结点,即可在O(1)的时间复杂度内完成
//注意:若删除结点下一个结点为空,则需从头结点开始找到删除结点的前一个结点
//      头结点不包含数据,只做一个标记

#include <iostream>
using namespace std;

typedef struct Node
{
    int value;
    struct Node *next;
} Node;

//新增结点
Node *addNewNode(Node *pHead, int newValue);

//打印链表
void printNodeList(Node *pHead);

//删除某个结点
void deleteNode(Node *pHead, Node *toDelNode);

int main()
{

    Node *pHead = new Node();
    pHead->value = -1;
    pHead->next = NULL;
    addNewNode(pHead, 1);
    addNewNode(pHead, 2);
    addNewNode(pHead, 3);
    addNewNode(pHead, 3);
    addNewNode(pHead, 4);
    Node *node6 = addNewNode(pHead, 4);
    addNewNode(pHead, 5);
    Node *node8 = addNewNode(pHead, 6);

    //打印删除后的链表
    printNodeList(pHead);

    //删除结点
    deleteNode(pHead, node8);

    //打印删除后的链表
    printNodeList(pHead);
    return 0;
}

//新增结点
Node *addNewNode(Node *pHead, int newValue)
{

    Node *node = pHead;
    while (node->next != NULL)
    {
        node = node->next;
    }

    Node *newNode = new Node();
    newNode->value = newValue;
    newNode->next = NULL;

    node->next = newNode;
    node = NULL;
    return newNode;
}

//打印链表
void printNodeList(Node *pHead)
{

    Node *node = pHead->next;
    while (node != NULL)
    {
        cout << node->value << ",";
        node = node->next;
    }
    cout << endl;
}

当待删除结点是单项链表的最后一个结点时,从头节点开始找到待删除结点的前一个结点的时间复杂度是O(n),所以总的平均时间复杂度是[(n-1)XO(1)]/n,结果还是O(1)

题目二:删除链表中重复的结点
在一个排序的单向链表中,删除链表中存在重复值的结点。例如链表:1->2->3->3->4->4->5,删除重复的3和4后,链表变为:1->2->5

分析:在遍历链表时,我们可以定义三个指针,分别指向当前结点、前一个结点和后一个结点。

  • 比较当前结点和后一个结点的值是否相同;
  • 若相同,从当前结点开始,逐步遍历删除值相同的结点,直到遇到值不同的结点或者遍历链表结束,然后将前一个结点指针的下一个结点指针指向值不同结点,重新使链表连接起来;
  • 若不同,分别将前一个结点指针、当前结点指针和下一个结点指针分别赋值,继续向前遍历,重复1步骤。

代码:

//删除重复的结点
void deleteDuplicateNode(Node *pHead)
{

    Node *node = pHead->next;
    Node *preNode = NULL;

    while (node != NULL)
    {
        Node *nextNode = node->next;
        bool toDelete = false;
        if (nextNode != NULL && node->value == nextNode->value)
        {
            toDelete = true;
        }

        if (toDelete)
        {
            int value = node->value;
            Node *delNode = node;
            while (delNode != NULL && delNode->value == value)
            {
                nextNode = delNode->next;
                delete delNode;
                delNode = NULL;

                delNode = nextNode;
            }
            if (preNode == NULL)
            {
                preNode = pHead;
            }
            preNode->next = nextNode;
            node = nextNode;
        }
        else
        {
            preNode = node;
            node = node->next;
        }
    }
}
int main()
{

    Node *pHead = new Node();
    pHead->value = -1;
    pHead->next = NULL;
    addNewNode(pHead, 1);
    addNewNode(pHead, 2);
    addNewNode(pHead, 3);
    addNewNode(pHead, 3);
    addNewNode(pHead, 4);
    addNewNode(pHead, 6);
    addNewNode(pHead, 7);
    addNewNode(pHead, 7);

    //打印删除后的链表
    printNodeList(pHead);

    //删除结点
    deleteDuplicateNode(pHead);

    //打印删除后的链表
    printNodeList(pHead);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值