面试题 02.01. 移除重复节点

题目描述

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

示例1:

 输入:[1, 2, 3, 3, 2, 1]
 输出:[1, 2, 3]

示例2:

 输入:[1, 1, 1, 1, 2]
 输出:[1, 2]

提示:

链表长度在[0, 20000]范围内。
链表元素在[0, 20000]范围内。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicate-node-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思考与代码

自己没思路,官方给出的是哈希表法建立缓冲区
方法:哈希表
我们对给定的链表进行一次遍历,并用一个哈希集合(HashSet)来存储所有出现过的节点。由于在大部分语言中,对给定的链表元素直接进行「相等」比较,实际上是对两个链表元素的地址(而不是值)进行比较。因此,我们在哈希集合中存储链表元素的值,方便直接使用等号进行比较。

具体地,我们从链表的头节点 head 开始进行遍历,遍历的指针记为 pos。由于头节点一定不会被删除,因此我们可以枚举待移除节点的前驱节点,减少编写代码的复杂度。
这里LeetCode官方给出的答案

struct ListNode* removeDuplicateNodes(struct ListNode* head) {
    if (head == NULL) {
        return head;
    }
    int* occurred = (int*)calloc(20001, sizeof(int));
    occurred[head->val] = 1;
    struct ListNode* pos = head;
    // 枚举前驱节点
    while (pos->next != NULL) {
        // 当前待删除节点
        struct ListNode* cur = pos->next;
        if (!occurred[cur->val]) {
            occurred[cur->val] = 1;
            pos = pos->next;
        } else {
            pos->next = pos->next->next;
        }
    }
    pos->next = NULL;
    return head;
}

我找到了大佬的实现过程,好强。芜湖~~

#include <iostream>

// 单链表节点结构如下
typedef struct node
{
    char data;
    struct node *next;
} NODE;


// 尾插法创建单链表(带头节点)
NODE *createEnd(char arr[], int len)
{
    NODE *head = (NODE *)malloc(sizeof(NODE)); // 生成头节点
    head->next = NULL;
    NODE *end = head;  // 尾指针初始化

    for (int i = 0; i < len; i++) {

        NODE *p = (NODE *)malloc(sizeof(NODE)); // 为每个数组元素建立一个节点
        p->data = arr[i];
        end->next = p;  // 将节点p插入到终端节点之后
        end = p;
    }

    end->next = NULL;  // 单链表建立完毕,将终端节点的指针域置空

    return head;
}

// 单链表打印
void print(NODE *head)
{
    if (head == NULL) return;

    NODE *p = head->next;
    while (p != NULL) {
        printf("%c  ", p->data);
        p = p->next;
    }
}


// 第三种方法:递归方法
NODE *delSame_3(NODE *head)
{
    NODE *p, *temp = head; // 递归过程中head是在不断变化的,但初始时temp都指向新的head。
    if (head->next == NULL)
        return head;

    head->next = delSame_3(head->next); // 递归到head->next指向链表的尾节点;此时head指向链表倒数第二个节点。
    p = head->next; // 让p指向head的下一个节点,注意此时temp=head;

    while (p != NULL)
    {
        if (head->data == p->data) // 单次递归中,head是不变的,每次都把head的数据和head之后所有节点的数据比较,若相同,则删除该节点。
        {
            temp->next = p->next;
            free(p);
            p = temp->next;
        }
        else
        {
            p = p->next;
            temp = temp->next; // temp初始时是指向新的head的,之后作为临时变量,随着p一起后移,始终作为p的前驱节点,是为了当p节点的数据和head数据一样时,在删除p节点时,用temp->next来保存p的后继节点。
        }
    }

    return head;
}


// 第二种方法:非递归实现去重
NODE *delSame_2(NODE *head)
{
    NODE *p,*q,*r;
    p = head->next; // 适用于有头节点的单链表;对于不带头节点的单链表,此处改为 p=head 即可。
    while(p != NULL)    // p用于遍历链表
    {
        q = p;
        while(q->next != NULL) // q遍历p后面的结点,并与p数值比较
        {
            if(q->next->data == p->data)
            {
                r = q->next; // r保存需要删掉的结点
                q->next = r->next;   // 需要删掉的结点的前后结点相接
                free(r);
            }
            else
                q = q->next;
        }

        p = p->next;
    }

    return head;
}


// 第一种方法:hash表去重
NODE *delSame(NODE *head)
{
    if (head == NULL || head->next == NULL)
        return head;

    const int size = 256;
    unsigned int count[size]; // 可用 unsigned int count[size] = {0}; 将数组中每一个元素都初始化为0。
    for (int i = 0; i < size; i++) // 可能上述方法,不一定对所有编译器都适用,所以采用循环的方式更稳妥。
        count[i] = 0;

    // count[(unsigned char)(head->data)] = 1; // 当使用没有头节点的单链表时,打开此行代码即可。

    NODE *p = head; // head是单链表的头节点,注意,头节点没有数据,它的存在仅仅是为了方便计算。
    NODE *q = p->next;
    NODE *r;
    while (q != NULL)
    {
        if (count[(unsigned char)(q->data)] == 0)
        {
            count[(unsigned char)(q->data)] = 1;
            p = q;
            q = q->next; // 始终让p作为q的前驱节点
        }
        else
        {
            r = q; // r存储要删除的节点
            p->next = q->next;
            q = p->next; // 始终让p作为q的前驱节点
            free(r);
        }
    }

    return head;

}

int main(int argc, const char * argv[]) {

    char arr[] = "a1xbeffeaghbm";
    int len = (int)strlen(arr);

    NODE *head = createEnd(arr, len);

    // 去重之前
    print(head);
    printf("\n");

    delSame(head);

    // 去重之后
    print(head);
    printf("\n");


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值