「这是我参与2022首次更文挑战的第42天,活动详情查看:2022首次更文挑战」。
题目:给定链表头节点head,要求按照升序返回排好之后的链表。
解题思路
题目要求在的时间复杂度完成排序,在排序算法中,时间复杂度在级别的只有二分法,而排序算法中含二分思想的主要是归并排序和快速排序,这两个时间复杂度都是,并且归并排序是稳定的,快速排序是不稳定的。
本题采用归并排序进行求解,归并排序利用的是自顶向下的思想,将一个大问题分割为等效的子问题,通过对子问题的求解来得到整个问题的解,可得代码如下:
public ListNode sortList(ListNode head) {
return sorted(head);
}
public ListNode sorted(ListNode head){
if(head==null||head.next==null) return head;
ListNode fast = head.next;
ListNode slow = head;
while(fast!=null&&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
ListNode right = sorted(slow.next);
slow.next=null;
ListNode left = sorted(head);
return mergeList(left, right);
}
public ListNode mergeList(ListNode left, ListNode right){
ListNode temp = new ListNode(-1);
ListNode cur = temp;
while(left!=null&&right!=null){
if(left.val<right.val){
cur.next = left;
left = left.next;
}else {
cur.next = right;
right = right.next;
}
cur = cur.next;
}
cur.next = left==null?right:left;
return temp.next;
}
上述代码需要注意的是,在sorted的归并里面,fast指向的并不是head,而是head.next,之所以这么设置是为了避免栈溢出。举个例子:假如当前list只有两个节点[3, 4],如果fast和slow都指向头节点,则sorted会把链表划分为[3, 4]和null两部分,之后[3, 4]再次递归,一直循环,最终会导致栈溢出。
当然除了这种解决方法,还有一种解决方法,就是让fast和slow都还指向head,所不同的是寻找mid节点的判断条件要改为下面的:
public ListNode sorted(ListNode head){
if(head==null||head.next==null) return head;
ListNode fast = head;
ListNode slow = head;
while(fast.next!=null&&fast.next.next!=null){
fast = fast.next.next;
slow = slow.next;
}
ListNode right = sorted(slow.next);
slow.next=null;
ListNode left = sorted(head);
return mergeList(left, right);
}
这样做的目的也是解决当链表只剩两个节点避免一直递归的情况。
LeetCode 147和本题类似,只不过那题是要求对链表排序使用插入排序,插入排序思路很简单,无非就是将当前节点和已排好的末尾节点进行比较,如果大于则直接插入,如果小于则往前面找,直到找到合适的插入点,插入即可,插入排序难点在于对细节的把握,代码如下:
public ListNode insertionSortList(ListNode head) {
if(head==null||head.next==null) return head;
ListNode point = new ListNode(-1);
point.next = head;
ListNode lastNode = head;
ListNode cur = head.next;
while(cur!=null){
if(cur.val>=lastNode.val){
lastNode = lastNode.next;
}else {
ListNode temp = point;
while(temp.next.val<cur.val){
temp = temp.next;
}
lastNode.next=cur.next;
cur.next=temp.next;
temp.next=cur;
}
cur = lastNode.next;
}
return point.next;
}
上述代码时间复杂度为,空间复杂度为。