LeetCode-Sort List
题目描述如下:
Sort a linked list in O(n log n) time using constant space complexity.
解题思路:
即找一个时间复杂度为O(nlogn)的基于链表的排序算法,那么,只有归并排序符合要求。快速排序之类的排序算法都需要随机访问能力,链表不符合要求。
然后按照一般的递归归并排序来看,其要占用O(n)的空间,因为采用的是链表数据结构,故可以优化成O(1),就是在合并的时候让两个链表指来指去,最后连成串就好。
tip1:
把一个链表一分为二的小技巧(百度的。。)
如果按照常规思路,需要先遍历链表一边,得到链表长度N,然后从头再走N/2步,进而得到链表的中间节点。
一种较为快速的方法如下(双指针法):
即设置双指针,初始都指向头结点,然后一个每次走1步,另一个每次走2步,当快的指针将要达到或者已经达到链表末尾时,慢指针指向的就是链表的中间节点。
tip2:
此算法另一个比较好的地方在于(也是百度到的。。)
在merge时,先将两个链表拆分成两个独立的链表,这样避免了合并时还要考虑长度的麻烦,直接用ptr==null来判定是否合并完成即可。
ps:最开始我用的迭代的归并算法,思路很清晰,但是边界条件判定巴拉巴拉之类的略麻烦,没有实现成功。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
//将两个有序链表合并,使用常量空间
public ListNode merge(ListNode fptr, ListNode sptr)
{
//自己多加一个临时头指针
ListNode tempHead = new ListNode(-1);
ListNode current = tempHead;
while(null != fptr && null != sptr)
{
if(fptr.val < sptr.val)
{
current.next = fptr;
fptr = fptr.next;
}
else
{
current.next = sptr;
sptr = sptr.next;
}
current = current.next;
}
current.next = (fptr == null)? sptr : fptr;
//返回头指针
return tempHead.next;
}
//寻找链表的中间节点
//采用快慢指针法,这个是寻找链表中间节点比较快的方法,避免了两次遍历
public ListNode findM(ListNode ptr)
{
ListNode slow = ptr;
ListNode fast = ptr;
while(null != fast.next && null != fast.next.next)
{
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode sortList(ListNode head) {
if(null == head || null == head.next)
{
return head;
}
ListNode middle = findM(head);
ListNode next = middle.next;
middle.next = null;
return merge(sortList(head),sortList(next));
}
}