【Leetcode】链表专题

leetcode链表专题

主要根据CSview一个校招刷题准备网站 做这个网站的人真的很厉害!进行整理
太困了,一学习就困,来刷刷题


前言

一、leetcode 206.反转链表

1.题目描述:

在这里插入图片描述

2.主要有两种方法,迭代法和递归法

先来描述两者的区别:
迭代法(Iteration)和递归法(Recursion)都是解决问题的方法,它们在编程中经常被用到。

迭代法是通过循环的方式,重复执行某段代码直到满足条件为止。通常使用迭代语句(如for循环或while循环)来实现。迭代法通常比递归法更直观,更容易理解,并且在一些情况下效率更高。它的实现方式类似于人们在日常生活中的思考方式,一步一步地解决问题。

递归法则是指一个函数在执行过程中调用自己的编程技巧。递归的本质是将问题划分为规模更小的子问题,并不断地重复这个过程直到达到最基本的情况,然后逐层返回结果。递归在某些情况下更为简洁,但可能会导致栈溢出或性能问题,因为每次递归调用都会增加函数调用栈的深度。

3.迭代法:

我们可以思考,是否可以通过指针指向位置的交换来实现反转。
定义三个指针:
prev代表在迭代过程中已经反转的头节点,初始值指向null;
curr指向当前节点,也就是待反转部分的头节点。初始值为链表的头节点head;
next指向curr.next节点,当前节点的下一个节点;

prev=null;
curr=head;
next=null;
//交换过程如下:
next=curr.next;
curr.next=prev;
prev=curr;
curr=next;

让我们以具体的示例来描述迭代法中的每个循环。

假设原始链表为:1 -> 2 -> 3 -> 4 -> 5,我们将对其进行反转。

  1. 初始化

    • 初始时,prev = nullcurr = 1next = null
    • 链表状态:prev -> null, curr -> 1 -> 2 -> 3 -> 4 -> 5next -> null
  2. 第一次迭代

    • 进入循环,next 指向 curr.next,即 2
    • 链表状态:prev -> null, curr -> 1 -> null, next -> 2 -> 3 -> 4 -> 5
    • curr.next 指向 prev,即 1next 指向 null
    • 移动 prevcurr 的位置,即 prev -> 1
    • 移动 currnext 的位置,即 curr -> 2
  3. 第二次迭代

    • 进入循环,next 指向 curr.next,即 3
    • 链表状态:prev -> 1 -> null, curr -> 2 -> null, next -> 3 -> 4 -> 5
    • curr.next 指向 prev,即 2next 指向 1
    • 移动 prevcurr 的位置,即 prev -> 2 -> 1
    • 移动 currnext 的位置,即 curr -> 3
  4. 第三次迭代

    • 进入循环,next 指向 curr.next,即 4
    • 链表状态:prev -> 2 -> 1 -> null, curr -> 3 -> null, next -> 4 -> 5
    • curr.next 指向 prev,即 3next 指向 2
    • 移动 prevcurr 的位置,即 prev -> 3 -> 2 -> 1
    • 移动 currnext 的位置,即 curr -> 4
  5. 第四次迭代

    • 进入循环,next 指向 curr.next,即 5
    • 链表状态:prev -> 3 -> 2 -> 1 -> null, curr -> 4 -> null, next -> 5
    • curr.next 指向 prev,即 4next 指向 3
    • 移动 prevcurr 的位置,即 prev -> 4 -> 3 -> 2 -> 1
    • 移动 currnext 的位置,即 curr -> 5
  6. 第五次迭代

    • 进入循环,next 指向 curr.next,即 null
    • 链表状态:prev -> 4 -> 3 -> 2 -> 1 -> null, curr -> 5 -> null, next -> null
    • curr.next 指向 prev,即 5next 指向 4
    • 移动 prevcurr 的位置,即 prev -> 5 -> 4 -> 3 -> 2 -> 1
    • 移动 currnext 的位置,即 curr -> null
  7. 迭代结束

    • 循环结束,因为此时 curr 为空,退出循环。
    • prev 指向了反转后的链表头部,即 5
    • 返回 prev,即反转后的链表头部。

通过这个例子,可以清晰地看到每个循环迭代过程中,指针的变化以及链表的状态变化,从而实现了链表的反转。
代码:

class Solution {
    public ListNode reverseList(ListNode head) {
        
        ListNode prev = null;
        ListNode curr = head;
        ListNode next = null;

        while (curr != null) {
            next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }

        return prev;
    

    }

4.递归法:

当使用递归法解决链表反转问题时,主要思路是将原问题分解为一个或多个规模较小的子问题,并利用递归的特性解决这些子问题。具体地,对于链表反转问题,我们可以考虑以下步骤:

  1. 确定递归函数的参数和返回值

    • 参数:当前节点 head,即待反转链表的头节点。
    • 返回值:反转后的链表的头节点。
  2. 确定递归函数的终止条件

    • 当链表为空或者只有一个节点时,直接返回该节点,因为不需要反转。
  3. 确定递归函数的递推关系

    • 首先递归地反转剩余部分链表(即 head.next)。
    • 然后将当前节点 head 的下一个节点的 next 指针指向 head,即将当前节点反转。
    • 最后将当前节点 headnext 指针置为空,避免形成循环。
  4. 返回结果

    • 返回反转后的链表的头节点。

总的来说,递归法的思路是将原问题分解为子问题,然后递归地解决子问题,最终将子问题的解合并起来得到原问题的解。在链表反转问题中,递归法的核心在于将当前节点与剩余部分链表进行连接,并将当前节点反转。

class RecursiveSolution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode new_head = reverseList(head.next);
        head.next.next = head;
        head.next = null;

        return new_head;
    }
}

通过不断将链表分成两部分,在最后剩下节点5的时候停止递归,进行返回,返回上一层节点时,以节点4为head节点为例,此时的链表是4->5,头节点是4,将4放在当前链表的后方,即head.next.next=head;,再层层返回,便实现了链表的反转。
20240414

二、leetcode 83.删除排序链表中的重复元素

在这里插入图片描述
这个题比较简单的前提是它是一个事先排序好的链表,因而可以采取相邻元素之间两两对比的情况。如果这个链表是一个乱序的情况,比如1,3,4,1,5就不能用这种两两比较的方法。
分析题目,以1,1,2,3,3,为例,我们有两种方法对该问题进行一个解决。

1.迭代法解决该问题

设置一个头节点curr,比较节点curr与下一个节点的值,如果相同则:curr.next=curr.next.next
将curr节点的下一个节点指向这个curr节点下个节点的下一个节点
如果两者不相同,则将curr往右移动一个节点。

// 迭代法
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode curr = head;

        while (curr != null && curr.next != null) {
            if (curr.val == curr.next.val) {
                curr.next = curr.next.next;
            } else {
                curr = curr.next;
            }
        }

        return head;
    }
}

2.采用回归的方法来解决

递归删除排序链表中的重复元素的思路是将问题分解为两部分:首先处理头节点及其重复元素,然后递归处理剩余链表。这种方法的关键在于利用递归处理子链表,并将结果链接到当前节点。详细步骤如下:

递归的基本情况:
递归的终止条件:
如果链表为空(head 为 None)或者链表只有一个节点(head.next 为 None),直接返回 head。
递归调用:
将 head.next 传递给递归函数,将返回的结果赋值给 head.next。
比较当前节点(head)和其下一个节点(head.next)的值:
如果值相同,说明存在重复元素,此时将当前节点的下一个节点指向下一个的下一个节点(即删除 head 的下一个节点),并保持当前节点不变。
如果值不同,说明不存在重复元素,直接返回当前节点。
返回链表头部节点。

(鄙人在学这一段的时候觉得递归返回回来的过程真是让人费解,搞不明白这个语句运行顺序,后来自己在纸上捋了几遍)

public class Solution{
public ListNode deleteDuplication(ListNode head){
if(head==null||head.next==null){
return head;
}
head.next=deleteDuplication(head.next);
if(head.val==head.next.val){
head=head.next
}
return head;
}

首先通过递归函数对链表进行分段,以 1,1,2,3,3,null为例,递归不断深入当head.next=null时输出此时head为第二个3,此时将这个第二个3赋值给head.next,此时的head为第一个3,发现此时head的值与head下一个值相同,令head=head.next,此时就相当于删除了第一个3,返回此时的head值为3,但是整个链表中只有这一个3了。此时的3为这个递归函数输出的值,将它赋值给head.next,说明此时head是2,2与3不相等,输出此时的head为2,接着一直返回上一层,直到head=null。递归完成。
20240415

总结

  • 34
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
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 语言和链表数据结构的知识。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值