链表知识点总结
1. Linked List Cycle(判断链表是否有环)
思路:设置slow和fast两个指针,初始化为head和head.next。遇到slow == fast说明有环,遇到fast == null || fast.next == null说明无环。模版程序如下。
1 public class Solution { 2 public boolean hasCycle(ListNode head) { 3 if (head == null) { 4 return false; 5 } 6 ListNode slow = head; 7 ListNode fast = head.next; 8 while (fast != slow) { 9 if (fast == null || fast.next == null) { 10 return false; 11 } 12 fast = fast.next.next; 13 slow = slow.next; 14 } 15 return true; 16 } 17 }
2. Intersection of Two Linked Lists(两个链表相交的开始节点)
思路:把第一个链表的尾节点连到第二个链表的头节点,然后判断环的入口节点。
1 public class Solution { 2 public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 3 if (headA == null || headB == null) { 4 return null; 5 } 6 ListNode node = headA; 7 while (node.next != null) { 8 node = node.next; 9 } 10 node.next = headB; 11 ListNode intersection = listCycle(headA); 12 node.next = null; 13 return intersection; 14 } 15 public ListNode listCycle(ListNode head) { 16 ListNode slow = head; 17 ListNode fast = head.next; 18 while (fast != slow) { 19 if (fast == null || fast.next == null) { 20 return null; 21 } 22 fast = fast.next.next; 23 slow = slow.next; 24 } 25 slow = head; 26 fast = fast.next; 27 while (slow != fast) { 28 slow = slow.next; 29 fast = fast.next; 30 } 31 return slow; 32 } 33 }
3. Sorted List
(1)快速排序
思路:先找到中间节点(模版程序,详细见代码里的getMiddlle()函数),然后建立三个dummy分别用来组成比中间节点值小于、等于和大于的节点,注意最后一定要把末尾节点的next域置为null!!!因为末尾节点不一定是原list里的最后一个节点,其next域可能空可能不空。然后递归的排序left和right链表,最后把left、mid和right这三条链表合并。
(2)归并排序链表
思路:先找到中点节点,然后分治思想先排序右边list,再让中间节点的next域为null,再排序左边的list,最后merge。注意该算法空间复杂度为O(1)而不是给数组归并排序时的O(n),因为merge过程植新建了一个dummy node而并没有像数组的归并排序那样开辟了O(n)的临时数组。
1 public class Solution { 2 public ListNode sortList(ListNode head) { 3 if (head == null || head.next == null) { 4 return head; 5 } 6 ListNode mid = getMiddle(head); 7 ListNode right = sortList(mid.next); 8 mid.next = null; 9 ListNode left = sortList(head); 10 return merge(left, right); 11 } 12 public ListNode getMiddle(ListNode head) { 13 ListNode slow = head; 14 ListNode fast = head.next; 15 while (fast != null && fast.next != null) { 16 fast = fast.next.next; 17 slow = slow.next; 18 } 19 return slow; 20 } 21 public ListNode merge(ListNode head1, ListNode head2) { 22 ListNode dummy = new ListNode(0); 23 ListNode cur = dummy; 24 while (head1 != null && head2 != null) { 25 if (head1.val < head2.val) { 26 cur.next = head1; 27 head1 = head1.next; 28 } else { 29 cur.next = head2; 30 head2 = head2.next; 31 } 32 cur = cur.next; 33 } 34 if (head1 != null) { 35 cur.next = head1; 36 } 37 if (head2 != null) { 38 cur.next = head2; 39 } 40 return dummy.next; 41 } 42 }
4. Convert Binary Search Tree to Doubly Linked List
Convert a binary search tree to doubly linked list with in-order traversal. Example Given a binary search tree: 4 / \ 2 5 / \ 1 3 return 1<->2<->3<->4<->5
思路:分治思想,以root为根的BST,利用ResultType类记录其值最小的节点和值最大的节点,然后建立值为root.val的链表节点,它的prev与值最大节点相连(相互连接),它的next与值最小节点相连(相互连接)。
1 /** 2 * Definition of TreeNode: 3 * public class TreeNode { 4 * public int val; 5 * public TreeNode left, right; 6 * public TreeNode(int val) { 7 * this.val = val; 8 * this.left = this.right = null; 9 * } 10 * } 11 * Definition for Doubly-ListNode. 12 * public class DoublyListNode { 13 * int val; 14 * DoublyListNode next, prev; 15 * DoublyListNode(int val) { 16 * this.val = val; 17 * this.next = this.prev = null; 18 * } 19 * } 20 */ 21 class ResultType { 22 DoublyListNode first; 23 DoublyListNode last; 24 public ResultType(DoublyListNode first, DoublyListNode last) { 25 this.first = first; 26 this.last = last; 27 } 28 } 29 public class Solution { 30 /** 31 * @param root: The root of tree 32 * @return: the head of doubly list node 33 */ 34 public DoublyListNode bstToDoublyList(TreeNode root) { 35 // Write your code here 36 return helper(root).first; 37 } 38 public ResultType helper(TreeNode root) { 39 if (root == null) { 40 return new ResultType(null, null); 41 } 42 ResultType left = helper(root.left); 43 ResultType right = helper(root.right); 44 DoublyListNode node = new DoublyListNode(root.val); 45 ResultType result = new ResultType(null, null); 46 if (root.left == null) { 47 result.first = node; 48 } else { 49 result.first = left.first; 50 node.prev = left.last; 51 left.last.next = node; 52 } 53 if (root.right == null) { 54 result.last = node; 55 } else { 56 result.last = right.last; 57 node.next = right.first; 58 right.first.prev = node; 59 } 60 return result; 61 } 62 }
5. Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
思路I:分治法,将问题归结到合并两个有序链表的问题上。
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 public class Solution { 10 public ListNode mergeKLists(ListNode[] lists) { 11 //Divide Conquer 12 if (lists == null || lists.length == 0) { 13 return null; 14 } 15 return mergeHelper(lists, 0, lists.length - 1); 16 } 17 public ListNode mergeHelper(ListNode[] lists, int start, int end) { 18 if (start == end) { 19 return lists[start]; 20 } 21 int mid = start + (end - start) / 2; 22 ListNode left = mergeHelper(lists, start, mid); 23 ListNode right = mergeHelper(lists, mid + 1, end); 24 return mergeTwoLists(left, right); 25 } 26 public ListNode mergeTwoLists(ListNode left, ListNode right) { 27 ListNode dummy = new ListNode(0); 28 ListNode cur = dummy; 29 while (left != null && right != null) { 30 if (left.val < right.val) { 31 cur.next = left; 32 left = left.next; 33 } else { 34 cur.next = right; 35 right = right.next; 36 } 37 cur = cur.next; 38 } 39 if (left != null) { 40 cur.next = left; 41 } 42 if (right != null) { 43 cur.next = right; 44 } 45 return dummy.next; 46 } 47 }
思路II:小根堆
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 public class Solution { 10 public ListNode mergeKLists(ListNode[] lists) { 11 // Minimum Heap 12 if (lists == null || lists.length == 0) { 13 return null; 14 } 15 PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length, new Comparator<ListNode>() { 16 public int compare(ListNode a, ListNode b) { 17 return a.val - b.val; 18 } 19 }); 20 for (int i = 0; i < lists.length; i++) { 21 //remove empty list 22 if (lists[i] != null) { 23 pq.offer(lists[i]); 24 } 25 } 26 ListNode dummy = new ListNode(0); 27 ListNode cur = dummy; 28 while (!pq.isEmpty()) { 29 ListNode node = pq.poll(); 30 cur.next = node; 31 cur = cur.next; 32 if (node.next != null) { 33 pq.offer(node.next); 34 } 35 } 36 //此句多余,应为到最后一个元素,其next指针一定为空。 37 //因为都是有序链表,最后一个元素一定是某一条链表的末尾节点,其next域为空 38 //cur.next = null; 39 return dummy.next; 40 } 41 }
6. Insertion Sort List
Sort a linked list using insertion sort.
思路:pre指针指向每次要插入的元素的前一个位置,依次遍历节点插入即可。注意pre不必每次从头开始移动到插入位置,只需要当待插入元素比pre.next节点的值大才返回到头开始选择插入位置。
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 public class Solution { 10 public ListNode insertionSortList(ListNode head) { 11 if (head == null || head.next == null) { 12 return head; 13 } 14 ListNode dummy = new ListNode(0); 15 ListNode pre = dummy; 16 ListNode cur = head; 17 while (cur != null) { 18 ListNode next = cur.next; 19 // 避免pre指针每次都从头开始移动 20 if (pre.next == null || pre.next.val > cur.val) { 21 pre = dummy; 22 } 23 while (pre.next != null && pre.next.val <= cur.val) { 24 pre = pre.next; 25 } 26 cur.next = pre.next; 27 pre.next = cur; 28 cur = next; 29 } 30 return dummy.next; 31 } 32 }