LeetCode sortList 对链表进行排序

文章介绍了两种针对链表的归并排序方法,一种是迭代版本,通过计算节点数并逐步归并子列表来实现;另一种是递归版本,利用快慢指针找到链表中点,然后对两部分分别进行递归排序并合并。此外,还提到了链表的选择排序,但该方法在LeetCode测试中超时。
摘要由CSDN通过智能技术生成

参考资料:左程云算法课

迭代版本的归并排序

  public ListNode sortList(ListNode head)
	{
		int n=0;
		// count nodes
		ListNode cur=head;
		while(cur!=null)
		{
			n++;
			cur = cur.next;
		}
		
		ListNode first=head;
		ListNode pre = null;
		ListNode h= first;
		for(int len=1;len<n;len<<=1) // make sure that two parts we divide every time 'while' runs
		{ // for the list headed by 'h', while运行一次,则 有两组 长度为len 的 连续子块 被归并排序,
        // 下次运行 while, 就是 下两组  长度为len 的 连续子块 被归并排序,
        // pre 用于连接 归并排序出的 子数组
        // first 用于定位 下两组等待归并排序的起始位置
        // h 表示 在这个len 下 对整个数组 做一遍归并排序 后的结果的起始位置
			while(first!=null)
			{
				ListNode[] twoParts = twoLen(first,len); // 两组子块的起始位置、终止位置,下一组等待归并排序子块的起始位置
				ListNode[] oneMerge = merge(twoParts[0],twoParts[1],twoParts[2],twoParts[3]);// 归并排序 两组子块
				if(pre==null)
				{
					h = oneMerge[0];
					pre = oneMerge[1];
				}else {
					pre.next=oneMerge[0];
					pre = oneMerge[1];
				}
				first = twoParts[4]; //first移到下一个待排序子块的起始位置
			}// end while
			first = h; // ready for the next iteration with new 'len'
			pre=null;
		}// end for
		return h;
	}
	public ListNode[] twoLen(ListNode first, int len)
	{// 从 以first为开头的整体 中,取出两段长度为len的子串。返回两端的分割点 和 这次分到哪了
		// head, tail, head, tail, n
		ListNode ls = first;
		ListNode le = first;
		ListNode rs = null;
		ListNode re = null;
		ListNode next = null;
		int pass=0;
		while(first!=null)
		{
			pass++;
			if(pass<=len)
			{
				le = first;
			}else if(pass==len+1)
			{
				rs=first;
			}
			if(pass>=len+1)
            // you cannot replace it with 'pass>len+1',
            // since the range of 'len' in main fun guarantees that two parts we will get contains at least one num
            // so using 'pass>=len+1', you can make sure re won't be null as its initial value; 
            // 这样更新re还有一个原因是,第二个子块并不总是能达到len个字符
			{
				re=first;
			}
			if(pass==(len<<1))
			{
				break;
			}
			first = first.next;
		}//end while
		le.next=null; // break the connection of two sub bolcks
		if(re!=null)
		{
			next = re.next;
			re.next=null; 
		}
		return new ListNode[] {ls,le,rs,re,next};
	}
	
	public ListNode[] merge(ListNode l1, ListNode r1, ListNode l2, ListNode r2)
	{
		if(l2==null)
		{
			return new ListNode[] {l1,r1};
		}
		
		ListNode head=null;
		
		ListNode pre=null;
		ListNode cur=null;
		
		ListNode tail = null;
		
		while(l1!=r1.next && l2!=r2.next)
		{
			if(l1.val<l2.val)
			{
				cur = l1;
				l1 = l1.next;
			}else {
				cur=l2;
				l2 = l2.next;
			}
			
			if(pre==null)
			{
				head = cur;
			}else {
				pre.next = cur;
			}
			pre = cur;
		}
		if(l1!=r1.next)
        {
            while(l1!=r1.next) 
		{
			pre.next = l1;
			tail=l1;
			pre=l1;
			l1 = l1.next;
		}

        }else{
 // l2 might be null, this case embodies the function of "if(l1!=r1.next)..else.."
 // under this case, only the above 'if' block will be run, so all is right
		while(l2!=r2.next)
		{
			pre.next = l2;
			tail = l2;;
			pre = l2;
			l2 = l2.next;
		}
		

        }
		
		
		return new ListNode[] {head,tail};
		
	}

递归版本的归并排序

思路:
首先 用快慢指针找到 链表的上中点;
然后,将整个链表分为两部分,分别做排序,这里调用递归函数即可;
接着,做归并,注意申请dummy head,以备及时记录排序结果,并返回。

class Solution {
      public ListNode sortList(ListNode head)
	{
		if(head==null || head.next==null)
		{
			return head;
		}
		
		// find upper-mid, by fast and slow pointers
		// 注意 这里 fast,slow指针的设置,保证 拿到的是 上中点
		ListNode fast = head.next;
		ListNode slow = head;
		while(fast!=null && fast.next!=null)
		{
			slow = slow.next;
			fast = fast.next.next;
		}
		// now, slow is upper-mid point
		ListNode tmp = slow.next;
		slow.next=null; // break the connection between the left part and right part
		ListNode left = sortList(head);
		ListNode right = sortList(tmp);

	// merge two parts
		ListNode h = new ListNode(0);// dummy head of final result
		ListNode res = h;
		while(left!=null && right!=null)
		{
			if(left.val<right.val)
			{
				h.next= left;
				left = left.next;
			}else {
				h.next=right;
				right = right.next;
			}
			h=h.next;
		}
		h.next = left!=null?left:right;
		return res.next;
		
	}

链表的选择排序

在leetCode的测试中超时了 ┭┮﹏┭┮
思路是:把链表看作两部分,前部分是已排序的(结尾是tail),后部分是未排序的(开头是cur) ; 每次都从未排序部分选出最小值small,接在已排序部分的结尾。注意:准备 下一轮 未排序部分的开头时,需要看 最小值是否是当前开头cur,如果是,那么cur=cur.next, 因为small要被拿走。注意:small被拿走后,未排序部分中要用smallPre的next接上剩余部分。

	
	public class ListNode{
		int val;
		ListNode next;
		
		public ListNode() {}
		public ListNode(int v)
		{
			val=v;
		}
		public ListNode(int v,ListNode node)
		{
			val=v;
			next=node;
		}
	} // end class ListNode
	
	public static ListNode selection(ListNode head)
	{
		// divide one list into two parts: sorted part and unsorted part
		
		ListNode tail=null; // the tail of unsorted part
		ListNode cur = head; // head of sorted part
		
		ListNode small = null;
		ListNode smallPre = null;
		
		while(cur!=null)
		{
			small = cur;
			smallPre = getSmallpre(cur);
			if(smallPre!=null)
			{
				small = smallPre.next;
				smallPre.next = small.next;
			}
			cur= cur==small?cur.next:cur; // head of unsorted part in the next iteration
			// put away 'small' and 'tail'
			if(tail==null)
			{
				head=small;
			}else {
				tail.next=small;
			}
			
			tail = small;
		}
		
		return head;
	}
	public static ListNode getSmallpre(ListNode head)
	{
		ListNode small=head;
		ListNode smallPre=null;
		
		ListNode cur=head;
		ListNode pre = null;
		
		while(cur!=null)
		{
			if(cur.val<small.val)
			{
				small=cur;
				smallPre = pre;
			}
			
			pre = cur;
			cur = cur.next;
		}
		return smallPre;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值