leetcode 链表(一)

题目

  1. leetcode 203 移除链表元素
  2. leetcode 707 设计链表
  3. leetcode 206 反转链表
  4. leetcode 24 两两交换链表中的节点
  5. leetcode 19 删除链表的倒数第N个节点
  6. leetcode 142 环形链表II

leetcode 203 移除链表元素

思路:考察删除链表节点的操作,该链表头结点有值,可以创建一个虚拟节点指向头结点。
这样头结点和其他节点的操作就可以一致。
之后返回虚拟节点的next

ListNode* removeElements(ListNode* head, int val) {
        // 构造虚拟头结点
        ListNode *vhead = (ListNode*)calloc(1, sizeof(ListNode));
        vhead -> next = head; // 指向头节点
        ListNode *p = vhead;
        while (p != nullptr){
            if (p->next != nullptr && p -> next->val == val){
                ListNode *q = p->next; // 要删除的节点
                p->next = q->next;
                delete q;
            }else{
                p = p->next;
            }
        }
        head = vhead -> next;
        free(vhead);
        return head;
    }

leetcode 707 设计链表

思路:本题为链表的基础题,考察增加,删除,查找元素。构造一个虚拟头结点,指向第一个节点。

  1. 增加元素:
    a. 头插法:常规方法。
    b. 尾插法:查找到最后一个节点,然后进行插入。
    c. 在下标为index的位置上插入元素x:查找到下标为index-1的节点,然后进行插入
  2. 删除元素:
    a. 删除下标为index的元素:查找到下标为index-1的节点,然后进行删除
  3. 查找元素
    a. 查找下标为index的元素,并返回其值。
    要注意的是:插入和删除元素都需要找到其插入位置的前置节点,才可以插入

下列代码还可以优化。

class MyLinkedList {
public:
    struct LinkNode{
        int val;
        LinkNode *next;
    };
    MyLinkedList() {
        vhead = (LinkNode *)calloc(1, sizeof(LinkNode)); // 初始化

    }
    
    int get(int index) {
        if (index > size - 1) // size相当于数组的length,所以index最大只能到size-1
            return -1;
        LinkNode *p = vhead -> next;
        int i = 0; // 下标,从0开始
        while (p != nullptr && i != index){
            i ++;
            p = p->next;
        }
        return p->val;

    }
    
    void addAtHead(int val) {
        // 头插法
        LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode));
        p -> val = val;
        p->next = vhead->next;
        vhead -> next = p;
        size += 1; // 添加一个元素,size + 1
    }
    
    void addAtTail(int val) {
        // 尾插法
        // 找到最后一个元素
        LinkNode *r = vhead;
        // 最后一个元素的next为空
        while (r->next != nullptr){
            r = r->next;
        }
        LinkNode *q = (LinkNode *)malloc(sizeof(LinkNode));
        q->val = val;
        q->next = r->next;
        r->next = q;
        size ++;
    }
    
    void addAtIndex(int index, int val) {
        // 将节点插入到下标为index的节点之前,即将节点插入到index的位置
        if (index > size) // index 可以插入到size的位置,即插入到尾部
            return;
        // index为0单独考虑,如果判断的话index-1为负数
        if (index == 0){
            LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
            p -> val = val;
            p->next = vhead->next;
            vhead->next = p;
            size ++;
            return;
        }
        LinkNode *p = vhead->next;
        int i = 0;
        // 查找到下标为i-1的节点
        while (p != nullptr && i < index - 1){
            i ++;
            p = p->next;
        }
        LinkNode *q = (LinkNode *)calloc(1, sizeof(LinkNode));
        q->val = val;
        q->next = p->next;
        p->next = q;
        size ++;
    }
    
    void deleteAtIndex(int index) {
        if (index == 0)
        {
            LinkNode *p = vhead -> next;
            vhead->next = p->next;
            free(p);
            size --;
            return;
        }
        // 删除下标为index的节点
        if (index > size - 1)
            return;
        int i = 0;
        LinkNode *p = vhead->next;
        while (p != nullptr && i < index - 1){
            i ++;
            p = p->next;
        } 
        LinkNode *q = p->next;
        p->next = q->next;
        free(q);
        size --;
    }
private:
    LinkNode *vhead; // 虚拟头结点
    int size = 0; // 表示有几个节点
};

leetcode 206 反转链表

思路:
用p,q,t三个指针来表示第一,第二,第三个节点,然后进行反转

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // p, q, t, 交换p,q,t用来保存第三个节点
        ListNode* p = head;
        ListNode* q;
        ListNode* t;
        // 存在两个节点时
        if (p != nullptr && p->next != nullptr) {
            q = p->next;
            t = q->next;
            // 让第一个节点的next指向空
            p->next = nullptr;
            // 当第三个指针不为空时,说明至少还有三个节点
            while (t != nullptr) {
                q->next = p; // 第二个指向第一个
                p = q;
                q = t;
                t = t->next;
            }
            // t为空,最后只有两个节点,进行交换
            q->next = p;
            head = q;
        }
        // 存在一个节点或者没有节点不需要考虑
        return head;
    }
};

leetcode 24 两两交换链表中的节点

思路:
与反转链表相似,只不过加了一点条件,为两两交换,需要考虑第三个节点以及第三个的next是否为空

ListNode* swapPairs(ListNode* head) {
        // 两两交换
        // 构造一个虚拟头结点
        ListNode *vhead = (ListNode *)malloc(sizeof(ListNode));
        vhead->next = head;
        // 第一个p,第二个q,第三个t
        ListNode *p, *q, *t;
        p = head;
        // l 指向每两个节点中的第一个节点
        ListNode *l = vhead;
        // 第二个节点存在
        if (p != nullptr && p->next != nullptr){
            q = p->next;
            t = q->next;
            while (t != nullptr && t->next != nullptr){
                // 第三个节点不为空,以及第三个节点的next不为空时
                // 交换p和q
                p->next = t;
                q->next = p;
                // l指向q
                l->next = q;
                // 更新p,q,t,l
                l = p;
                p = t;
                q = p->next;
                t = q->next;
                
            }
            // 此时t为空,交换p,q
            q->next = p;
            p->next = t;
            l->next = q;

        }
        // 第二个节点不存在,只有一个节点时
        return vhead->next;
    }

leetcode 19 删除链表的倒数第N个节点

思路:
常规方法:找到倒数第N个节点,需要求出节点总数,在找到目标节点,需要进行两次链表遍历。
双指针方法:j指针先遍历,j指针与i指针相差n个节点,j指针到头,就可以知道i的位置,i之后的节点就为要删除的节点。
所以j先走;然后i,j同时走,直到j遍历结束,就可求得i。

ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 要求倒数第n个节点,先求出总结点数,才能知道倒数第n个节点
        // 遍历
        ListNode* p = head;
        int count = 0; // 记录节点的数量
        while (p != nullptr) {
            count++;
            p = p->next;
        }
        // 构造虚拟头结点
        ListNode* vhead = (ListNode*)malloc(sizeof(ListNode));
        vhead->next = head;
        // 遍历链表,找到目标节点,然后删除该节点
        //  一共有count个节点,删除倒数第n个节点,就是删除第count - n + 1个节点
        if (count - n + 1 >= 2) {
            p = vhead;
            int i = 0;
            // 找到第count - n个节点
            while (p->next != nullptr) {
                p = p->next;
                i++;
                // 如果是第count - n个节点
                if (i == count - n) {
                    ListNode* q = p->next;
                    p->next = q->next;
                    delete q;
                    break;
                }
            }
        }
        else
        {
            // 删除第一个节点
            ListNode *q = head;
            head = head->next;
            vhead->next = head;
            delete q;
        }
        return vhead->next;
    }  

leetcode 面试题 02.07. 链表相交

思路:

  1. 暴力:时间复杂度 O(N^2)
    先遍历第一个链表,再遍历第二个链表,然后两两进行比较,判断节点是否相等
  2. 找到相交的节点,两个链表之间应该是右对齐;
    从最短的那个链表初始位置开始进行遍历,然后节点进行比较,相等则返回,没有则返回null。
    先遍历长的链表,遍历到短的链表开始的位置。此时需要知道两个链表的长度。然后求它们的差值,让长的链表遍历那个差值。
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        // 计算链表a和链表b的长度,那么相交的节点数量是a和b节点数量的最小值
        int countA = 0;
        int countB = 0;
        ListNode *a = headA;
        while (a != nullptr)
        {
            countA ++;
            a = a->next;
        }
        ListNode *b = headB;
        while (b != nullptr){
            countB ++;
            b = b->next;
        }
        // 计算a和b长度的差值
        int sub = abs(countA - countB);
        // 如果countA > countB, a指针移动到第sub + 1个位置
        if (countA > countB){
            int i = 0;
            a = headA;
            while (i != sub){
                i ++;
                a = a->next;
            }
            b = headB;
        }
        else{
            int i = 0;
            b = headB;
            while (i != sub){
                i ++;
                b = b->next;
            }
            a = headA;
        }
        // 同时遍历,判断是否相等,相等则返回
        while (a != nullptr && b != nullptr){
            if (a == b)
                return a;
            a = a->next;
            b = b->next;
        }
        return a;
    }

leetcode 142 环形链表II

思路:

  1. 首先要判断链表是否有环:
    用双指针来判断。链表如果有环,快慢指针必定会在环内相遇。
  2. 找到相遇的节点
  3. 相遇之后,罗列快慢指针路程等式,进行解方程,可得到起始位置到环内起点的距离 == 相遇点到环内起点的距离
  4. 快慢指针分别从起始位置和相遇点开始遍历,相等时即得到链表环内起始节点
ListNode *detectCycle(ListNode *head) {
        // 利用双指针判断是否有环,如果有环,快慢指针在环内相遇、
        ListNode *i = head;
        ListNode *j = head;
        while (j != nullptr && j->next != nullptr){
            j = j->next->next; // 快指针走两步
            i = i->next; // 慢指针走一步
            // 相同的时间下,快指针所走的位移应该是慢指针所走位移的两倍
            // 找到环内相遇的节点后,就可以开始寻找环的起始节点
            if (i == j){
                // 画图可得,慢指针从头开始,快指针从相遇节点开始,一次一个节点,相遇节点就是环的起始节点
                i = head;
                while (i != j){
                    i = i->next;
                    j = j->next;
                }
                return i;
            }
        }
        return nullptr;
    }
  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Rust 是一种现代的编程语言,特别适合处理内存安全和线程安全的代码。在 LeetCode 中,链表是经常出现的题目练习类型,Rust 语言也是一种非常适合处理链表的语言。接下来,本文将从 Rust 语言的特点、链表的定义和操作,以及 Rust 在 LeetCode链表题目的练习等几个方面进行介绍和讲解。 Rust 语言的特点: Rust 是一种现代化的高性能、系统级、功能强大的编程语言,旨在提高软件的可靠性和安全性。Rust 语言具有如下几个特点: 1. 内存安全性:Rust 语言支持内存安全性和原语级的并发,可以有效地预防内存泄漏,空悬指针以及数据竞争等问题,保证程序的稳定性和可靠性。 2. 高性能:Rust 语言采用了“零成本抽象化”的设计思想,具有 C/C++ 等传统高性能语言的速度和效率。 3. 静态类型检查:Rust 语言支持静态类型检查,可以在编译时检查类型错误,避免一些运行时错误。 链表的定义和操作: 链表是一种数据结构,由一个个节点组成,每个节点保存着数据,并指向下一个节点。链表的定义和操作如下: 1. 定义:链表是由节点组成的数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。 2. 操作:链表的常用操作包括插入、删除、查找等,其中,插入操作主要包括在链表首尾插入节点和在指定位置插入节点等,删除操作主要包括删除链表首尾节点和删除指定位置节点等,查找操作主要包括根据数据元素查找节点和根据指针查找节点等。 Rust 在 LeetCode链表题目的练习: 在 LeetCode 中,链表是常见的题目类型,而 Rust 语言也是一个非常适合练习链表题目的语言。在 Rust 中,我们可以定义结构体表示链表的节点,使用指针表示节点的指向关系,然后实现各种操作函数来处理链表操作。 例如,针对 LeetCode 中的链表题目,我们可以用 Rust 语言来编写解法,例如,反转链表,合并两个有序链表,删除链表中的重复元素等等,这样可以更好地熟悉 Rust 语言的使用和链表的操作,提高算法和编程能力。 总之,在 Rust 中处理链表是非常方便和高效的,而 LeetCode 中的练习也是一个非常好的机会,让我们更好地掌握 Rust 语言和链表数据结构的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值