LeetCode 連結串列
基礎部分
160. 相交連結串列
簡單
編寫一個程式,找到兩個單鏈表相交的起始節點。
如下面的兩個連結串列:
在節點 c1 開始相交。
示例 1:
輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 輸出:Reference of the node with value = 8 輸入解釋:相交節點的值為 8 (注意,如果兩個連結串列相交則不能為 0)。從各自的表頭開始算起,連結串列 A 為 [4,1,8,4,5],連結串列 B 為 [5,0,1,8,4,5]。在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
示例 2:
輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Reference of the node with value = 2
輸入解釋:相交節點的值為 2 (注意,如果兩個連結串列相交則不能為 0)。從各自的表頭開始算起,連結串列 A 為 [0,9,1,2,4],連結串列 B 為 [3,2,4]。在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
示例 3:
輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 輸出:null 輸入解釋:從各自的表頭開始算起,連結串列 A 為 [2,6,4],連結串列 B 為 [1,5]。由於這兩個連結串列不相交,所以 intersectVal 必須為 0,而 skipA 和 skipB 可以是任意值。 解釋:這兩個連結串列不相交,因此返回 null。
注意:
- 如果兩個連結串列沒有交點,返回
null
. - 在返回結果後,兩個連結串列仍須保持原有的結構。
- 可假定整個連結串列結構中沒有迴圈。
- 程式儘量滿足 O(n) 時間複雜度,且僅用 O(1) 記憶體。
public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if (headA == null || headB == null) return null; ListNode p = headA, q = headB; int loop = 2; while (loop > 0){ if (p == q) return q; p = p.next; q = q.next; if (p == null) { p = headB; loop--; } if (q == null) q = headA; } return null; } }
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode p = headA, q = headB;
while (p != q){ //都是null,就第二圈都到頭了,省了計算loop的時間
p = p == null ? headB : p.next;
q = q == null ? headA : q.next;
}
return p;
}
}
206. 反轉連結串列
簡單
反轉一個單鏈表。
示例:
輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL
進階:
你可以迭代或遞迴地反轉連結串列。你能否用兩種方法解決這道題?
class Solution {
public ListNode reverseList(ListNode head) {
ListNode res = new ListNode(0);
while (head != null){
ListNode p = head;
head = head.next;
p.next = res.next;
res.next = p;
}
return res.next;
}
}
21. 合併兩個有序連結串列
簡單
將兩個升序連結串列合併為一個新的 升序 連結串列並返回。新連結串列是通過拼接給定的兩個連結串列的所有節點組成的。
示例:
輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode cur = res;
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;
}
while (l1 != null){
cur.next = l1;
cur = cur.next;
l1 = l1.next;
}
while (l2 != null){
cur.next = l2;
cur = cur.next;
l2 = l2.next;
}
return res.next;
}
}
83. 刪除排序連結串列中的重複元素
簡單
給定一個排序連結串列,刪除所有重複的元素,使得每個元素只出現一次。
示例 1:
輸入: 1->1->2
輸出: 1->2
示例 2:
輸入: 1->1->2->3->3
輸出: 1->2->3
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null) return head;
ListNode cur = head;
while (cur.next != null){
if (cur.val == cur.next.val){
cur.next = cur.next.next;
}else {
cur = cur.next;
}
}
return head;
}
}
19. 刪除連結串列的倒數第N個節點
中等
給定一個連結串列,刪除連結串列的倒數第 n 個節點,並且返回連結串列的頭結點。
示例:
給定一個連結串列: 1->2->3->4->5, 和 n = 2.
當刪除了倒數第二個節點後,連結串列變為 1->2->3->5.
說明:
給定的 n 保證是有效的。
進階:
你能嘗試使用一趟掃描實現嗎?
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null) return null;
ListNode first = head, second = head;
while (n-- > 0) first = first.next;
if (first == null) return head.next;
while (first.next != null){
first = first.next;
second = second.next;
}
second.next = second.next.next;
return head;
}
}
24. 兩兩交換連結串列中的節點
中等
給定一個連結串列,兩兩交換其中相鄰的節點,並返回交換後的連結串列。
你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
示例:
給定 1->2->3->4, 你應該返回 2->1->4->3.
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode ans = new ListNode(0);
ListNode pre = ans;
pre.next = head;
ListNode l, r;
while (pre.next != null && pre.next.next != null){
l = pre.next;
r = l.next;
l.next = r.next;
pre.next = r;
r.next = l;
pre = l;
}
return ans.next;
}
}
445. 兩數相加 II
中等
給你兩個 非空 連結串列來代表兩個非負整數。數字最高位位於連結串列開始位置。它們的每個節點只儲存一位數字。將這兩數相加會返回一個新的連結串列。
你可以假設除了數字 0 之外,這兩個數字都不會以零開頭。
進階:
如果輸入連結串列不能修改該如何處理?換句話說,你不能對列表中的節點進行翻轉。
示例:
輸入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 8 -> 0 -> 7
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
StringBuilder a = new StringBuilder();
StringBuilder b = new StringBuilder();
ListNode cur1 = l1, cur2 = l2;
while(cur1 != null){
a.append(cur1.val);
cur1 = cur1.next;
}
while(cur2 != null){
b.append(cur2.val);
cur2 = cur2.next;
}
StringBuilder sum = new StringBuilder();
int la = a.length();
int lb = b.length();
int len = la > lb ? la : lb;
int carry = 0;
for (int i = 0; i < len; i++){
carry += la-i-1 < 0 ? 0 : a.charAt(la-i-1)-'0';
carry += lb-i-1 < 0 ? 0 : b.charAt(lb-i-1)-'0';
sum.append(carry%10);
carry /= 10;
}
if (carry > 0) sum.append(carry);
sum.reverse();
ListNode res = new ListNode(0);
ListNode cur = res;
for (char c : sum.toString().toCharArray()){
ListNode node = new ListNode(c-'0');
cur.next = node;
cur = cur.next;
}
return res.next;
}
}
234. 迴文連結串列
簡單
請判斷一個連結串列是否為迴文連結串列。
示例 1:
輸入: 1->2
輸出: false
示例 2:
輸入: 1->2->2->1
輸出: true
進階:
你能否用 O(n) 時間複雜度和 O(1) 空間複雜度解決此題?
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> list = new ArrayList<>();
while (head != null){
list.add(head.val);
head = head.next;
}
int len = list.size();
for (int i = 0; i < len/2; i++){
if (list.get(i).intValue() != list.get(len-i-1).intValue()) {
//list裡是物件,不是基本資料型別,不能直接用==判斷
//也可以寫成:!Objects.equals(list.get(i), list.get(len-i-1))
return false;
}
}
return true;
}
}
725. 分隔連結串列
中等
給定一個頭結點為 root
的連結串列, 編寫一個函式以將連結串列分隔為 k
個連續的部分。
每部分的長度應該儘可能的相等: 任意兩部分的長度差距不能超過 1,也就是說可能有些部分為 null。
這k個部分應該按照在連結串列中出現的順序進行輸出,並且排在前面的部分的長度應該大於或等於後面的長度。
返回一個符合上述規則的連結串列的列表。
舉例: 1->2->3->4, k = 5 // 5 結果 [ [1], [2], [3], [4], null ]
示例 1:
輸入:
root = [1, 2, 3], k = 5
輸出: [[1],[2],[3],[],[]]
解釋:
輸入輸出各部分都應該是連結串列,而不是陣列。
例如, 輸入的結點 root 的 val= 1, root.next.val = 2, \root.next.next.val = 3, 且 root.next.next.next = null。
第一個輸出 output[0] 是 output[0].val = 1, output[0].next = null。
最後一個元素 output[4] 為 null, 它代表了最後一個部分為空連結串列。
示例 2:
輸入:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
輸出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
解釋:
輸入被分成了幾個連續的部分,並且每部分的長度相差不超過1.前面部分的長度大於等於後面部分的長度。
提示:
root
的長度範圍:[0, 1000]
.- 輸入的每個節點的大小範圍:
[0, 999]
. k
的取值範圍:[1, 50]
.
class Solution {
public ListNode[] splitListToParts(ListNode root, int k) {
ListNode cur = root;
int N = 0; //統計數量
while (cur != null) {
cur = cur.next;
N++;
}
int width = N / k;
int rem = N % k; //前面連結串列長度多一個的數量
ListNode[] ans = new ListNode[k];
cur = root;
for (int i = 0; i < k; ++i) {
ListNode head = new ListNode(0), write = head;
for (int j = 0; j < width + (i < rem ? 1 : 0); ++j) {
write.next = new ListNode(cur.val);
write = write.next;
if (cur != null) cur = cur.next;
}
ans[i] = head.next;
}
return ans;
}
}
328. 奇偶連結串列
中等
給定一個單鏈表,把所有的奇數節點和偶數節點分別排在一起。請注意,這裡的奇數節點和偶數節點指的是節點編號的奇偶性,而不是節點的值的奇偶性。
請嘗試使用原地演算法完成。你的演算法的空間複雜度應為 O(1),時間複雜度應為 O(nodes),nodes 為節點總數。
示例 1:
輸入: 1->2->3->4->5->NULL
輸出: 1->3->5->2->4->NULL
示例 2:
輸入: 2->1->3->5->6->4->7->NULL
輸出: 2->3->6->7->1->5->4->NULL
說明:
- 應當保持奇數節點和偶數節點的相對順序。
- 連結串列的第一個節點視為奇數節點,第二個節點視為偶數節點,以此類推。
class Solution {
public ListNode oddEvenList(ListNode head) {
ListNode odd = new ListNode(0);
ListNode p = odd;
ListNode even = new ListNode(0);
ListNode q = even;
while (head != null){
p.next = head;
head = head.next;
p = p.next;
p.next = null;
if (head == null) break;
q.next = head;
head = head.next;
q = q.next;
q.next = null;
}
p.next = even.next;
return odd.next;
}
}
頻率排序
92,426,2,25,143,23,148,379