链表题的几大法宝:
- 双指针
- dummyHead
- 断链
- 挂链
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题和链表的插入排序有点像,就是限制了时间复杂度和空间复杂度,但这每一个优化都是一种难点;
链表的插入排序解法:
public ListNode sortList(ListNode head) {
ListNode newHead = new ListNode(Integer.MIN_VALUE);
newHead.next = head;
ListNode cur = head;
ListNode pre = newHead;
while(cur!=null){
//cur.val<pre.val 发现后边的数据 < 前边的数据时候 例如 12341,重新开始扫描
if(cur.val<pre.val){
//差在这里,没有更新头部,要注意呀
ListNode newIndex = newHead.next;
ListNode newPre = newHead;
while(newIndex!=pre&&newIndex.val<cur.val) {
newIndex = newIndex.next;
newPre = newPre.next;
}
//找到位置进行节点交换位置
pre.next = cur.next;
cur.next = newIndex;
newPre.next = cur;
cur = pre.next;
}else{
//cur.val>=pre.val 前边数据小于等于后边数据,说明已经排好序了,继续往前走
cur = cur.next;
pre = pre.next;
}
}
return newHead.next;
}
这个解法的思路:
- 判断是否有序
- 如果无序则在已排好序的链表中找到该插入的位置
- 维护各节点
4-2-3-1 这里维护的方式是将2的next域指向4,;
排序链表解法
归并排序 的解法可以用到这里;
分组;归并
先根据step分组(1,2,4,……)
然后每两组之间进行归并;
分组函数:
public ListNode cut(ListNode head,int step){
while(--step!=0 && head!=null){
head=head.next;
}
//如果链表不够step长就返回null
if(head!=null){
ListNode result=head.next;
head.next=null;
return result;
}else{
return null;
}
}
归并函数
public ListNode merge(ListNode l1,ListNode l2){
ListNode work=new ListNode(0);
ListNode head=work;
while(l1!=null && l2!=null){
if(l1.val<l2.val){
work.next=l1;
l1=l1.next;
}else{
work.next=l2;
l2=l2.next;
}
work=work.next;
}
if(l1!=null){
work.next=l1;
}
if(l2!=null){
work.next=l2;
}
return head.next;
}
主函数:
public ListNode sortList(ListNode head){
ListNode dummyHead = new ListNode(Integer.MAX_VALUE);
dummyHead.next=head;
//计算长度
int length=0;
ListNode work=head;
while(work!=null){
work=work.length;
length++;
}
ListNode tail=dummyHead;
for(int step=1;step<length;step<<=1){
work=dummyHead.next;
tail=dummyHead;
while(work!=null){
ListNode left=work;
ListNode right=cut(left,step);
work=cut(right,step);
//记录当前链表的最后一个位置,用于拼接下一趟循环产生的结果
tail.next=merge(left,right);
while(tail.next!=null){
tail=tail.next;
}
}
}
return dummyHead.next;
}
总的来说就是做链表的题一定要好好画图,来模拟中间维护节点的过程