LeetCode刷题 --基础知识篇-- 链表

Posted DogTwo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode刷题 --基础知识篇-- 链表相关的知识,希望对你有一定的参考价值。

  题目来源与力扣,传送门在这里。

  众所周知,链表是很重要的一种数据结构,但同时也很容易出错,二狗在重温这部分内容时被人指点了一些典型的题目,顺手去leetCode刷了一些,记录如下。

《206.单链表反转》(https://leetcode-cn.com/problems/reverse-linked-list/)

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     public int val;
 5  *     public ListNode next;
 6  *     public ListNode(int x) { val = x; }
 7  * }
 8  */
 9 public class Solution {
10     public ListNode ReverseList(ListNode head) {
11         ListNode newHead = null;
12         ListNode temp = null;
13 
14         if(head == null || head.next == null)
15         {
16             return head;
17         }
18 
19         while(head != null)
20         {
21             temp = head;
22             head = head.next;
23 
24             temp.next = newHead;
25             newHead = temp;
26         }
27 
28         return newHead;
29     }
30 }

  上面代码思路是这样的,建立一个新的结点newHead和一个存放临时内容的temp结点。开始遍历head结点,每次循环中都对head取头结点然后移动head到head.next。同时把取到的结点放入链表的头部,有点类似与栈。

  其实这道题目还可以利用三个指针P0(前一个结点),P1(当前结点),P2(当前结点的next)来操作,思路是每次循环都将P1的next改写为P0,即改变它的指向,然后再将P0,P1,P2依次移动。但要注意这种方法的顺序,一定要保证先移动P0到P1的位置才能移动P1到P2的位置。

 

《141.环形链表》(https://leetcode-cn.com/problems/linked-list-cycle/)

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

 

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。


示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。


示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

 

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     public int val;
 5  *     public ListNode next;
 6  *     public ListNode(int x) {
 7  *         val = x;
 8  *         next = null;
 9  *     }
10  * }
11  */
12 public class Solution {
13     public bool HasCycle(ListNode head) {
14         if(head == null || head.next == null)
15         {
16             return false;
17         }
18 
19         ListNode slow = head.next;
20         ListNode fast = head.next.next;
21 
22         while(fast != slow)
23         {
24             if(fast == null || fast.next == null)
25             {
26                 return false;
27             }
28 
29             slow = slow.next;
30             fast = fast.next.next;
31         }
32 
33         return true;
34     }
35 }

  很经典的快慢指针的问题,判断一个链表中有没有环的思路是这样的:如果我们不断的移动当前的指针到next,对于没有环的结点,一定可以到达null。所以如果我们找到了null就说明一定不是环形链表。对于有环的链表,我们设置两个指针开始不断的移动,一个慢指针每次移动一个结点,一个快指针每次移动两个结点。如果存在环,在足够多次的移动后快指针会和慢指针相遇,此时就可以判断出是有环结构了。但这种写法一定要注意边界值的情况。

 

《21.合并两个有序链表》(https://leetcode-cn.com/problems/merge-two-sorted-lists/)

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     public int val;
 5  *     public ListNode next;
 6  *     public ListNode(int x) { val = x; }
 7  * }
 8  */
 9 public class Solution {
10     public ListNode MergeTwoLists(ListNode l1, ListNode l2) {
11         ListNode head = new ListNode(1);
12         ListNode temp = head;
13 
14         while(l1 != null && l2 != null)
15         {
16             if(l1.val <= l2.val)
17             {
18                 temp.next = l1;
19                 temp = temp.next;
20                 l1 = l1.next;
21             }
22             else
23             {
24                 temp.next = l2;
25                 temp = temp.next;
26                 l2 = l2.next;
27             }
28         }
29 
30         if(l1 == null)
31         {
32             temp.next = l2;
33         }
34         else
35         {
36             temp.next = l1;
37         }
38 
39         return head.next;
40     }
41 }

  思路很简单,设置一个哨兵结点,然后从两个链表的头部开始比较,依次取得较小的值将其放入哨兵结点的next,并移动哨兵结点到next。注意如果有一个链表已经移动到了末尾,只需要把另一个链表的剩余结点直接添加即可。

 

《19.删除链表的倒数第n个结点》(https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/)

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     public int val;
 5  *     public ListNode next;
 6  *     public ListNode(int x) { val = x; }
 7  * }
 8  */
 9 public class Solution {
10     public ListNode RemoveNthFromEnd(ListNode head, int n) {
11         if(head.next == null)
12         {
13             return null;
14         }
15 
16         ListNode first = head;
17         ListNode sec = head;
18         ListNode result = sec;
19 
20         for(int i = 0; i<n; i++)
21         {
22             first = first.next;
23         }
24 
25         if(first == null)
26         {
27             return result.next;
28         }
29 
30         while(true)
31         {
32             if(first.next == null)
33             {
34                 sec.next = sec.next.next;
35                 break;
36             }
37 
38             first = first.next;
39             sec = sec.next;
40         }
41 
42         return result;
43     }
44 }

  思路是这样的,设置firat和sec两个结点同时指向head,然后先将first执行n次first = first.next.接着将first和sec同时移动,当first.next == null时说明移动到了链表的尾部,此时sec的next结点即为倒数第n个结点,删除即可。

 

《876.链表的中间结点》(https://leetcode-cn.com/problems/middle-of-the-linked-list/)

给定一个带有头结点 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

 

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
 

提示:

给定链表的结点数介于 1 和 100 之间。

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     public int val;
 5  *     public ListNode next;
 6  *     public ListNode(int x) { val = x; }
 7  * }
 8  */
 9 public class Solution {
10     public ListNode MiddleNode(ListNode head) {
11         if(head.next == null)
12         {
13             return head;
14         }
15 
16         ListNode slow = head;
17         ListNode fast = head;
18 
19         while(true)
20         {
21             fast = fast.next;
22 
23             if(fast == null)
24             {
25                 return slow;
26             }
27 
28             fast = fast.next;
29 
30             if(fast == null)
31             {
32                 return slow.next;
33             }
34 
35             slow = slow.next;
36         }
37     }
38 }

  这道题我借鉴了前面判断环形结点的思路,也是使用快慢两个指针。有一点需要注意的是,对一个链表来说结点的个数可能为偶数个或者奇数个,如果是偶数个根据题目我们需要取得后一个结点。我们在遍历链表时如果保证每次都是快指针先移动就比较方便判断了。快指针是需要移动两次的,当第一次移动后如果快指针为null,则可以说明包含奇数个结点,如果第二次移动后快指针为null则可以说明包含偶数个结点。







以上是关于LeetCode刷题 --基础知识篇-- 链表的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode刷题笔记-数据结构-day12

LeetCode刷题笔记-数据结构-day11

LeetCode刷题笔记-数据结构-day11

LeetCode刷题笔记-数据结构-day13

LeetCode刷题笔记-数据结构-day13

LeetCode Java刷题笔记—142. 环形链表 II