題目匯總https://leetcode-cn.com/tag/linked-list/
92. 反轉(zhuǎn)鏈表 II中等(看精選題解會(huì)清晰一點(diǎn),自己還寫(xiě)不出來(lái))
109. 有序鏈表轉(zhuǎn)換二叉搜索樹(shù)中等 [?]
138. 復(fù)制帶隨機(jī)指針的鏈表(分三步走)
141. 環(huán)形鏈表簡(jiǎn)單[?]
142. 環(huán)形鏈表 II中等[?]
143. 重排鏈表中等[?]
147. 對(duì)鏈表進(jìn)行插入排序中等(看評(píng)論區(qū)好久才能大致理解)
148. 排序鏈表中等(遞歸解法不滿(mǎn)足條件,需要迭代解法,存在問(wèn)題)
160. 相交鏈表簡(jiǎn)單[?]
203. 移除鏈表元素簡(jiǎn)單[?]
92. 反轉(zhuǎn)鏈表 II中等
反轉(zhuǎn)從位置 m 到 n 的鏈表。請(qǐng)使用一趟掃描完成反轉(zhuǎn)。
說(shuō)明:1 ≤ m ≤ n ≤ 鏈表長(zhǎng)度。
示例:
輸入: 1->2->3->4->5->NULL, m = 2, n = 4
輸出: 1->4->3->2->5->NULL
類(lèi)似題目206. 反轉(zhuǎn)鏈表簡(jiǎn)單
思路:遞歸
遞歸反轉(zhuǎn)整個(gè)鏈表的題理解了,但是這個(gè)題還是不會(huì)做。以下代碼轉(zhuǎn)自鏈接https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/bu-bu-chai-jie-ru-he-di-gui-di-fan-zhuan-lian-biao/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
if (m == 1) {
// 相當(dāng)于反轉(zhuǎn)前 n 個(gè)元素
return reverseN(head, n);
}
// 前進(jìn)到反轉(zhuǎn)的起點(diǎn)觸發(fā) base case
head.next = reverseBetween(head.next, m - 1, n - 1);
return head;
}
ListNode successor = null; // 后驅(qū)節(jié)點(diǎn)
// 反轉(zhuǎn)以 head 為起點(diǎn)的 n 個(gè)節(jié)點(diǎn),返回新的頭結(jié)點(diǎn)
public ListNode reverseN(ListNode head, int n) {
if (n == 1) {
// 記錄第 n + 1 個(gè)節(jié)點(diǎn)
successor = head.next;
return head;
}
// 以 head.next 為起點(diǎn),需要反轉(zhuǎn)前 n - 1 個(gè)節(jié)點(diǎn)
ListNode last = reverseN(head.next, n - 1);
head.next.next = head;
// 讓反轉(zhuǎn)之后的 head 節(jié)點(diǎn)和后面的節(jié)點(diǎn)連起來(lái)
head.next = successor;
return last;
}
}
109. 有序鏈表轉(zhuǎn)換二叉搜索樹(shù)中等 [?]
給定一個(gè)單鏈表,其中的元素按升序排序,將其轉(zhuǎn)換為高度平衡的二叉搜索樹(shù)。
本題中,一個(gè)高度平衡二叉樹(shù)是指一個(gè)二叉樹(shù)每個(gè)節(jié)點(diǎn)的左右兩個(gè)子樹(shù)的高度差的絕對(duì)值不超過(guò) 1。
示例:
給定的有序鏈表: [-10, -3, 0, 5, 9],
一個(gè)可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面這個(gè)高度平衡二叉搜索樹(shù):
思路:遞歸
問(wèn)題的關(guān)鍵是找到鏈表的中間元素,使用兩個(gè)指針,慢指針每次向后移動(dòng)一個(gè)節(jié)點(diǎn),快指針每次移動(dòng)兩個(gè)節(jié)點(diǎn)。當(dāng)快指針到鏈表的末尾時(shí),慢指針正好訪問(wèn)到鏈表的中間元素,然后將鏈表從中間元素的左側(cè)斷開(kāi),遞歸調(diào)用即可。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode sortedListToBST(ListNode head) {//執(zhí)行用時(shí) :1 ms, 在所有 Java 提交中擊敗了86.03%的用戶(hù),2020/08/05
if(head == null)
return null;
if(head.next == null)
return new TreeNode(head.val);
ListNode pre = head;
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
fast = fast.next.next;//快指針每次走兩步
slow = slow.next;//慢指針每次走一步
}
while(pre.next != slow){
pre = pre.next;
}
TreeNode root = new TreeNode(slow.val);//找到根節(jié)點(diǎn)
ListNode hRight = slow.next;
pre.next = null;//斷開(kāi)鏈表
root.left = sortedListToBST(head);
root.right = sortedListToBST(hRight);
return root;
}
}
138. 復(fù)制帶隨機(jī)指針的鏈表中等
給定一個(gè)鏈表,每個(gè)節(jié)點(diǎn)包含一個(gè)額外增加的隨機(jī)指針,該指針可以指向鏈表中的任何節(jié)點(diǎn)或空節(jié)點(diǎn)。
要求返回這個(gè)鏈表的 深拷貝。
我們用一個(gè)由n個(gè)節(jié)點(diǎn)組成的鏈表來(lái)表示輸入/輸出中的鏈表。每個(gè)節(jié)點(diǎn)用一個(gè)[val, random_index]表示:
val:一個(gè)表示Node.val的整數(shù)。random_index:隨機(jī)指針指向的節(jié)點(diǎn)索引(范圍從0到n-1);如果不指向任何節(jié)點(diǎn),則為null。
思路:
本質(zhì)和復(fù)制鏈表是一樣的,只是這個(gè)鏈表的每個(gè)節(jié)點(diǎn)包含一個(gè)額外增加的隨機(jī)指針random,先把復(fù)制的節(jié)點(diǎn)追加到每個(gè)節(jié)點(diǎn)之后,然后復(fù)制random指針,最后分割成兩個(gè)鏈表,返回新復(fù)制的鏈表即可。
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {//執(zhí)行用時(shí):0 ms, 在所有 Java 提交中擊敗了100.00%的用戶(hù),//2020/08/07
public Node copyRandomList(Node head) {
if(head == null) return null;
//1.復(fù)制每個(gè)節(jié)點(diǎn),追加到原來(lái)的每個(gè)節(jié)點(diǎn)中
Node cur = head;
while(cur != null){
Node copy = new Node(cur.val, cur.next, cur.random);
copy.next = cur.next;
cur.next = copy;
cur = copy.next;
}
//2.復(fù)制random
cur = head;
while(cur != null){
if(cur.random != null){
cur.next.random = cur.random.next;
}
cur = cur.next.next;
}
//3.分割原鏈表和復(fù)制的鏈表
Node node = head;
Node newHead = head.next;
Node newNode = newHead;
while(node != null){
node.next = node.next.next;
if(newNode.next != null){
newNode.next = newNode.next.next;
}
node = node.next;
newNode = newNode.next;
}
return newHead;
}
}
141. 環(huán)形鏈表簡(jiǎn)單[?]
給定一個(gè)鏈表,判斷鏈表中是否有環(huán)。
為了表示給定鏈表中的環(huán),我們使用整數(shù)pos來(lái)表示鏈表尾連接到鏈表中的位置(索引從 0 開(kāi)始)。 如果pos是-1,則在該鏈表中沒(méi)有環(huán)。
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋?zhuān)?/strong>鏈表中有一個(gè)環(huán),其尾部連接到第二個(gè)節(jié)點(diǎn)。
思路:雙指針
定義兩個(gè)指針,一快一慢,快指針每次走兩步,慢指針每次走一步。如果鏈表中不存在環(huán),最終快指針將會(huì)最先到達(dá)尾部,此時(shí)返回 false。如果鏈表中存在環(huán),那么快指針最終一定會(huì)追上慢指針。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {//執(zhí)行用時(shí):0 ms, 在所有 Java 提交中擊敗了100.00%的用戶(hù),2020/08/05
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null) return false;
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow) return true;//快慢指針相遇,一定存在環(huán)
}
return !(fast == null || fast.next == null);
}
}
142. 環(huán)形鏈表 II中等[?]
給定一個(gè)鏈表,返回鏈表開(kāi)始入環(huán)的第一個(gè)節(jié)點(diǎn)。 如果鏈表無(wú)環(huán),則返回
null。
為了表示給定鏈表中的環(huán),我們使用整數(shù)pos來(lái)表示鏈表尾連接到鏈表中的位置(索引從 0 開(kāi)始)。 如果pos是-1,則在該鏈表中沒(méi)有環(huán)。
說(shuō)明:不允許修改給定的鏈表。
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:tail connects to node index 1
解釋?zhuān)?/strong>鏈表中有一個(gè)環(huán),其尾部連接到第二個(gè)節(jié)點(diǎn)。
思路:雙指針
與上一題相比,情況復(fù)雜一些,不僅判斷是否有環(huán),還要找到環(huán)的入口節(jié)點(diǎn)。
在鏈表頭與相遇點(diǎn)分別設(shè)一個(gè)指針,每次各走一步,兩個(gè)指針必定相遇,且相遇第一點(diǎn)為環(huán)入口點(diǎn)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {//執(zhí)行用時(shí) :0 ms, 在所有 Java 提交中擊敗了100.00%的用戶(hù),2020/08/05
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow) break;
}
if(fast == null || fast.next == null) return null;
slow = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
143. 重排鏈表中等[?]
給定一個(gè)單鏈表 L:L0→L1→…→Ln-1→Ln ,
將其重新排列后變?yōu)椋?L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是單純的改變節(jié)點(diǎn)內(nèi)部的值,而是需要實(shí)際的進(jìn)行節(jié)點(diǎn)交換。
示例 1:
給定鏈表 1->2->3->4, 重新排列為 1->4->2->3.
示例 2:
給定鏈表 1->2->3->4->5, 重新排列為 1->5->2->4->3.
思路:
將鏈表平均分成兩半,將第二個(gè)鏈表逆序,依次連接兩個(gè)鏈表。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {//執(zhí)行用時(shí):3 ms, 在所有 Java 提交中擊敗了37.99%的用戶(hù),//2020/08/05
public void reorderList(ListNode head) {
if (head == null || head.next == null || head.next.next == null) {
return;
}
ListNode slow = head;
ListNode fast = head;
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode newHead = slow.next;
slow.next = null;//1.至此,分割成兩個(gè)鏈表
ListNode right = reverse(newHead);//2.反轉(zhuǎn)右半部分鏈表
while (right != null) { //3.依次連接兩個(gè)鏈表節(jié)點(diǎn)
ListNode temp = right.next;
right.next = head.next;
head.next = right;
head = right.next;
right = temp;
}
}
public ListNode reverse(ListNode head){
if(head == null || head.next == null) return head;
ListNode cur = reverse(head.next);
head.next.next = head;
head.next = null;
return cur;
}
}
147. 對(duì)鏈表進(jìn)行插入排序中等
對(duì)鏈表進(jìn)行插入排序。
插入排序的動(dòng)畫(huà)演示如上。從第一個(gè)元素開(kāi)始,該鏈表可以被認(rèn)為已經(jīng)部分排序(用黑色表示)。每次迭代時(shí),從輸入數(shù)據(jù)中移除一個(gè)元素(用紅色表示),并原地將其插入到已排好序的鏈表中。
示例 :
輸入: -1->5->3->4->0
輸出: -1->0->3->4->5
插入排序算法:
- 插入排序是迭代的,每次只移動(dòng)一個(gè)元素,直到所有元素可以形成一個(gè)有序的輸出列表。
- 每次迭代中,插入排序只從輸入數(shù)據(jù)中移除一個(gè)待排序的元素,找到它在序列中適當(dāng)?shù)奈恢?,并將其插入?/li>
- 重復(fù)直到所有輸入數(shù)據(jù)插入完為止。
思路:
引用評(píng)論區(qū)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode insertionSortList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(-1);//偽頭指針
dummy.next = head;
ListNode pre = head;
while (head != null && head.next != null)
{
if(head.val <= head.next.val) {
head = head.next;
continue;
}
pre = dummy;
while (pre.next.val < head.next.val) pre = pre.next;
ListNode curr = head.next;
head.next = curr.next;
curr.next = pre.next;
pre.next = curr;
}
return dummy.next;
}
}
148. 排序鏈表中等
在 O(n log n) 時(shí)間復(fù)雜度和常數(shù)級(jí)空間復(fù)雜度下,對(duì)鏈表進(jìn)行排序。
示例 1:
輸入: 4->2->1->3
輸出: 1->2->3->4
示例 2:
輸入: -1->5->3->4->0
輸出: -1->0->3->4->5
思路一:遞歸,不滿(mǎn)足常數(shù)級(jí)空間復(fù)雜度
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
//遞歸做法,不滿(mǎn)足常數(shù)級(jí)空間復(fù)雜度
class Solution {//執(zhí)行用時(shí):3 ms, 在所有 Java 提交中擊敗了99.16%的用戶(hù),//2020/08/05
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode midNode = slow;
ListNode rightNode = midNode.next;
midNode.next = null;
ListNode left = sortList(head);//排序左半部分鏈表
ListNode right = sortList(rightNode);//排序右半部分鏈表
return mergeTwoLists(left, right);
}
//合并兩個(gè)有序鏈表
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
// 合并后 l1 和 l2 最多只有一個(gè)還未被合并完,我們直接將鏈表末尾指向未合并完的鏈表即可
cur.next = l1 == null ? l2 : l1;
return dummy.next;
}
}
思路二:迭代(看不懂)
https://leetcode-cn.com/problems/sort-list/solution/sort-list-gui-bing-pai-xu-lian-biao-by-jyd/
160. 相交鏈表簡(jiǎn)單[?]
編寫(xiě)一個(gè)程序,找到兩個(gè)單鏈表相交的起始節(jié)點(diǎn)。
思路一:
如果兩個(gè)鏈表相交,那么他們一定有相同的尾結(jié)點(diǎn),分別遍歷兩個(gè)鏈表,記錄他們的尾結(jié)點(diǎn),如果他們的尾結(jié)點(diǎn)相同,那么這兩個(gè)表相交。分別計(jì)算兩個(gè)鏈表的長(zhǎng)度,先對(duì)鏈表head1遍歷(len1-len2)(假設(shè)len1>len2)個(gè)結(jié)點(diǎn)到結(jié)點(diǎn)p,此時(shí)結(jié)點(diǎn)p與head2到他們相交的結(jié)點(diǎn)的距離相等。此時(shí)同時(shí)遍歷兩個(gè)鏈表,直到遇到相同的結(jié)點(diǎn)為止,這個(gè)結(jié)點(diǎn)就是交點(diǎn)。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {//執(zhí)行用時(shí):1 ms, 在所有 Java 提交中擊敗了100.00%的用戶(hù),2020/08/01
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) return null;
int lenA = 1;
ListNode tailA = headA;
while(tailA.next != null){
tailA = tailA.next;
lenA++;//鏈表A的長(zhǎng)度
}
int lenB = 1;
ListNode tailB = headB;
while(tailB.next != null){
tailB = tailB.next;
lenB++;//鏈表B的長(zhǎng)度
}
if(tailA != tailB) return null;
ListNode nodeA = headA;
ListNode nodeB = headB;
if(lenA > lenB){
int d = lenA - lenB;
while(d != 0){
nodeA = nodeA.next;
d--;
}
}else{
int d = lenB - lenA;
while(d != 0){
nodeB = nodeB.next;
d--;
}
}
while(nodeA != nodeB){
nodeA = nodeA.next;
nodeB = nodeB.next;
}
return nodeA;
}
}
思路二:
也是為了消除長(zhǎng)度差,但是代碼更簡(jiǎn)潔
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/tu-jie-xiang-jiao-lian-biao-by-user7208t/
指針 pA 指向 A 鏈表,指針 pB 指向 B 鏈表,依次往后遍歷
如果 pA 到了末尾,則 pA = headB 繼續(xù)遍歷
如果 pB 到了末尾,則 pB = headA 繼續(xù)遍歷
比較長(zhǎng)的鏈表指針指向較短鏈表head時(shí),長(zhǎng)度差就消除了
如此,只需要將最短鏈表遍歷兩次即可找到位置
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
203. 移除鏈表元素簡(jiǎn)單[?]
刪除鏈表中等于給定值 val 的所有節(jié)點(diǎn)。
示例:
輸入: 1->2->6->3->4->5->6, val = 6
輸出: 1->2->3->4->5
思路:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//執(zhí)行用時(shí):1 ms, 在所有 Java 提交中擊敗了99.72%的用戶(hù),//2020/08/05
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy;
ListNode cur = head;
while(cur != null){
if(cur.val == val){
pre.next = cur.next;
}else{
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
}






