leetcode链表专题
主要根据CSview一个校招刷题准备网站 做这个网站的人真的很厉害!进行整理
太困了,一学习就困,来刷刷题
文章目录
- leetcode链表专题
- 前言
- 一、leetcode 206.反转链表
- 1.题目描述:
- 2.主要有两种方法,迭代法和递归法
- 3.迭代法:
- 4.递归法:
- 二、leetcode 83.删除排序链表中的重复元素
- 1.迭代法解决该问题
- 2.采用回归的方法来解决
- 总结
前言
一、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,我们将对其进行反转。
-
初始化:
- 初始时,
prev = null
,curr = 1
,next = null
。 - 链表状态:
prev
->null
,curr
->1
->2
->3
->4
->5
,next
->null
。
- 初始时,
-
第一次迭代:
- 进入循环,
next
指向curr.next
,即2
。 - 链表状态:
prev
->null
,curr
->1
->null
,next
->2
->3
->4
->5
。 - 将
curr.next
指向prev
,即1
的next
指向null
。 - 移动
prev
到curr
的位置,即prev
->1
。 - 移动
curr
到next
的位置,即curr
->2
。
- 进入循环,
-
第二次迭代:
- 进入循环,
next
指向curr.next
,即3
。 - 链表状态:
prev
->1
->null
,curr
->2
->null
,next
->3
->4
->5
。 - 将
curr.next
指向prev
,即2
的next
指向1
。 - 移动
prev
到curr
的位置,即prev
->2
->1
。 - 移动
curr
到next
的位置,即curr
->3
。
- 进入循环,
-
第三次迭代:
- 进入循环,
next
指向curr.next
,即4
。 - 链表状态:
prev
->2
->1
->null
,curr
->3
->null
,next
->4
->5
。 - 将
curr.next
指向prev
,即3
的next
指向2
。 - 移动
prev
到curr
的位置,即prev
->3
->2
->1
。 - 移动
curr
到next
的位置,即curr
->4
。
- 进入循环,
-
第四次迭代:
- 进入循环,
next
指向curr.next
,即5
。 - 链表状态:
prev
->3
->2
->1
->null
,curr
->4
->null
,next
->5
。 - 将
curr.next
指向prev
,即4
的next
指向3
。 - 移动
prev
到curr
的位置,即prev
->4
->3
->2
->1
。 - 移动
curr
到next
的位置,即curr
->5
。
- 进入循环,
-
第五次迭代:
- 进入循环,
next
指向curr.next
,即null
。 - 链表状态:
prev
->4
->3
->2
->1
->null
,curr
->5
->null
,next
->null
。 - 将
curr.next
指向prev
,即5
的next
指向4
。 - 移动
prev
到curr
的位置,即prev
->5
->4
->3
->2
->1
。 - 移动
curr
到next
的位置,即curr
->null
。
- 进入循环,
-
迭代结束:
- 循环结束,因为此时
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.递归法:
当使用递归法解决链表反转问题时,主要思路是将原问题分解为一个或多个规模较小的子问题,并利用递归的特性解决这些子问题。具体地,对于链表反转问题,我们可以考虑以下步骤:
-
确定递归函数的参数和返回值:
- 参数:当前节点
head
,即待反转链表的头节点。 - 返回值:反转后的链表的头节点。
- 参数:当前节点
-
确定递归函数的终止条件:
- 当链表为空或者只有一个节点时,直接返回该节点,因为不需要反转。
-
确定递归函数的递推关系:
- 首先递归地反转剩余部分链表(即
head.next
)。 - 然后将当前节点
head
的下一个节点的next
指针指向head
,即将当前节点反转。 - 最后将当前节点
head
的next
指针置为空,避免形成循环。
- 首先递归地反转剩余部分链表(即
-
返回结果:
- 返回反转后的链表的头节点。
总的来说,递归法的思路是将原问题分解为子问题,然后递归地解决子问题,最终将子问题的解合并起来得到原问题的解。在链表反转问题中,递归法的核心在于将当前节点与剩余部分链表进行连接,并将当前节点反转。
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