算法题
链表的归并排序
关键在于分割点的寻找和边界的选择。
- 快慢指针找中点
- 起始以空节点为右侧边界,保证分割点始终置于右半区域。
- 当下一个节点为右边界时,此时只剩一个节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
class Solution { public ListNode sortList(ListNode head) { return sortList(head, null); } public ListNode sortList(ListNode head, ListNode tail){ if(head == null){ return head; } if(head.next == tail){ head.next = null; return head; } ListNode slow, fast; slow = fast = head; while(fast != tail){ slow = slow.next; fast = fast.next; if(fast != tail){ fast = fast.next; } } ListNode mid = slow; ListNode l1 = sortList(head, mid); ListNode l2 = sortList(mid, tail); return merge(l1, l2); } public ListNode merge(ListNode l1, ListNode l2){ ListNode head = new ListNode(0); ListNode cur = head, cur1 = l1, cur2 = l2; while(cur1 != null && cur2 != null){ if(cur1.val <= cur2.val){ cur.next = cur1; cur1 = cur1.next; } else { cur.next = cur2; cur2 = cur2.next; } cur = cur.next; } if(cur1 != null){ cur.next = cur1; } else { cur.next = cur2; } return head.next; } }
|
链表的归并排序【非递归版】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
|
class Solution { public ListNode sortList(ListNode head) { if(head == null){ return head; } int length = 0; ListNode node = head; while(node != null){ length++; node = node.next; } ListNode dummyHead = new ListNode(0, head); for(int subLen=1; subLen < length; subLen *= 2){ ListNode pre = dummyHead, cur = dummyHead.next; while(cur != null){ ListNode head1 = cur; for(int i=1; i<subLen && cur.next != null; i++){ cur = cur.next; } ListNode head2 = cur.next; cur.next = null; cur = head2; for(int i=1; i<subLen && cur != null && cur.next != null; i++){ cur = cur.next; } ListNode next = null; if(cur != null) { next = cur.next; cur.next = null; } pre.next = merge(head1, head2); while(pre.next != null){ pre = pre.next; } cur = next; } } return dummyHead.next; } public ListNode merge(ListNode l1, ListNode l2){ ListNode head = new ListNode(0); ListNode cur = head, cur1 = l1, cur2 = l2; while(cur1 != null && cur2 != null){ if(cur1.val <= cur2.val){ cur.next = cur1; cur1 = cur1.next; } else { cur.next = cur2; cur2 = cur2.next; } cur = cur.next; } if(cur1 != null){ cur.next = cur1; } else { cur.next = cur2; } return head.next; } }
|
K 个一组翻转链表
关键在于如何反转完每一组后,新的尾节点能够接上下一组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
class Solution { public ListNode reverseKGroup(ListNode head, int k) { ListNode hair = new ListNode(0, head); ListNode pre = hair; while(head != null){ ListNode tail = pre; for(int i=0; i<k; i++) { tail = tail.next; if(tail == null){ return hair.next; } } ListNode[] reverse = myReverse(head, tail); head = reverse[0]; tail = reverse[1]; pre.next = head; pre = tail; head = tail.next; } return hair.next; } public ListNode[] myReverse(ListNode head, ListNode tail){ ListNode pre = tail.next; ListNode p = head; while(pre != tail){ ListNode next = p.next; p.next = pre; pre = p; p = next; } return new ListNode[]{tail, head}; } }
|
反转算法:以尾节点为条件,反转链表。