leetcode23:Merge k Sorted Lists

 Merge k Sorted Lists 

参考:http://www.cnblogs.com/TenosDoIt/p/3673188.html

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

解一:

因为前面有一道题是两条链表的合并,所以由此想到这道题可以先两条链表合并,再不断和下一条合并,直到结束。假设每条链表长n,共有k条链表,则时间复杂度为O(2n+3n+.....+kn),就是O(nk^2)


解二:

其实一开始想到过先把每条链表的第一个节点拿出来比较,这个思路,但是当时觉得这个操作稍微有点麻烦,也没有细想,之后就想看看网上其他人怎么做的,看到  用堆排序的思路,先用堆排序,排每个链表的第一个节点,然后取出最小的节点,修改此节点的指针指向下一节点。而且对于不同的语言,不同的题,选择什么样的数据结构,感觉很重要,此题如果用数组后面判断结束条件就会比较麻烦一点,选择ArrayList就能省好多功夫。元素入堆的时间复杂度是O(logk)总共有nk个元素,则时间复杂度就是O(nklogk)


解三:

还有一种方法采用分治法的思想,将列表数组分成两组,进行合并,然后递归的进行。时间复杂度是T(k)= 2T(k/2)+O(nk),结果是O(nklogk)

package leetcode;

import java.util.ArrayList;
import java.util.List;

public class leet23 {
	
	private static class ListNode{
		int val;
		ListNode next;
		ListNode(){}
		ListNode(int x){ val = x;}
	}
	
	public static void main(String[] args) {
		
		ListNode l0 = new leet23.ListNode(1);
		ListNode l1 = new leet23.ListNode(4);
		ListNode l2 = new leet23.ListNode(5);
		ListNode l3 = new leet23.ListNode(11);
		ListNode l4 = new leet23.ListNode(15);
		l0.next = l1;
		l1.next = l2;
		l2.next = l3;
		l3.next = l4;
		
		ListNode h0 = new leet23.ListNode(3);
		ListNode h1 = new leet23.ListNode(7);
		ListNode h2 = new leet23.ListNode(9);
		ListNode h3 = new leet23.ListNode(12);
		ListNode h4 = new leet23.ListNode(16);
		h0.next = h1;
		h1.next = h2;
		h2.next = h3;
		h3.next = h4;
		
		ListNode m0 = new leet23.ListNode(2);
		ListNode m1 = new leet23.ListNode(6);
		ListNode m2 = new leet23.ListNode(8);
		ListNode m3 = new leet23.ListNode(13);
		ListNode m4 = new leet23.ListNode(17);
		m0.next = m1;
		m1.next = m2;
		m2.next = m3;
		m3.next = m4;
		leet23 leet = new leet23();
		List<ListNode> lists = new ArrayList<ListNode>();
		lists.add(m0);
		lists.add(h0);
		lists.add(l0);	
		
		ListNode result = leet.mergeKLists2(lists);
		while(result.next != null){
			System.out.println(result.next.val);
			result = result.next;
		}
	}
	
	
	
	/**
	 * 第一种方法,先将两个链表合并,然后依次类推直到最后
	 * @param lists
	 * @return
	 */
	public ListNode mergeKLists(ListNode[] lists){
		
		for(int i = 0;i < lists.length - 1;i++){
			
			lists[i+1] = mergeTwoList(lists[i],lists[i+1]);
		}
		return lists[lists.length-1];
		
	}
	//两链表合并
	public ListNode mergeTwoList(ListNode l1,ListNode l2){
		
		ListNode head = l1;
		ListNode p = l1;
		l1 = l1.next;
		l2 = l2.next;
		while(l1 != null && l2 !=null){
			if(l1.val < l2.val){
				p.next = l1;
				p = l1;
				l1 = l1.next;
			}else{
				p.next = l2;
				p = l2;
				l2 = l2.next;
			}
		}
		if(l1 == null)p.next = l2;
		if(l2 == null)p.next = l1;
		return head;
	}
	
	
	
		
	/**
	 * 第二种方法,提取每一个链表的第一个元素构建一个小顶堆,然后取第一个元素,该元素的下一个元素入堆
		直到堆为空,结束
	 * @param lists
	 * @return
	 */
	public ListNode mergeKLists2(List<ListNode> lists){
		
		ListNode head = new ListNode();//记录排序好的节点
		ListNode p = head;//p指向排序好的最后一个节点
		List<ListNode> list = sortHeap(lists);//堆排序
		while(list.size() != 0){//正式因为使用了List这个集合,所以才使得循环的终止条件比较容易确定,如果使用数组则非常麻烦
			p.next = list.get(0);//获取排序后List中第一个链表的,第一个节点,是此次排序中最小的
			p = p.next;//修改p指针指向排序好的最后一个节点
			list.set(0, list.get(0).next);//因为排序后List中第一个链表,的第一个节点是最小的,
										//被拿走后,将此节点的后继节点作为链表的第一个节点,参与下一次堆排序
			if(list.get(0) == null){//当List中的某个链表为空时,就将其移出List,这就是使用List的好处,
									//如果使用数组,在这一步当中则会非常的麻烦。这会影响到循环终止的条件。
				list.remove(0);
			}
			list = sortHeap(list);//下一次堆排序
		}
		return head;		
	}
	//heap中存的是多个ListNode链表,此方法对每一个链表的第一个节点进行堆排序,构造小顶堆
	public List<ListNode> sortHeap(List<ListNode> heap){
		
		for(int i = heap.size()/2-1;i >= 0;i--){
			if(2*i+2 < heap.size()){
				if(heap.get(2*i+2).val < heap.get(i).val){
					ListNode temp = heap.get(i);
					heap.set(i,heap.get(2*i+2));
					heap.set(2*i+2,temp);
				}
			}
			if(2*i+1 < heap.size()){
				if(heap.get(2*i+1).val < heap.get(i).val){
					ListNode temp = heap.get(i);
					heap.set(i,heap.get(2*i+1));
					heap.set(2*i+1,temp);
				}
			}
		}
		return heap;//返回排序后的List
	}
        
       //第三种方法代码未贴
 }




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值