LeetCode刷题day023 (Jieky)

LeetCode第23题

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

Example:
Input:
[
  1->4->5,
  1->3->4,
  2->6
]
Output: 1->1->2->3->4->4->5->6
*/
import java.util.*;

class ListNode{
     
	int val;
	ListNode next;
	ListNode(){
     };
	ListNode(int val){
     this.val=val;}
	ListNode(int val, ListNode next) {
      this.val = val; this.next = next; }
}

public class MergeKSortedLists{
     
	public static void main(String[] args){
     
		ListNode node1_1 = new ListNode(1);
		ListNode node1_2 = new ListNode(4);
		ListNode node1_3 = new ListNode(5);
		node1_1.next = node1_2;
		node1_2.next = node1_3;
		
		ListNode node2_1 = new ListNode(1);
		ListNode node2_2 = new ListNode(3);
		ListNode node2_3 = new ListNode(4);
		node2_1.next = node2_2;
		node2_2.next = node2_3;
		
		ListNode node3_1 = new ListNode(2);
		ListNode node3_2 = new ListNode(6);
		node3_1.next = node3_2;
		
		
		MergeKSortedLists mksl = new MergeKSortedLists();
		// 对象数组静态初始化
		ListNode[] ln = new ListNode[]{
     node1_1,node2_1,node3_1};
		ListNode result = mksl.mergeKLists(ln);
		
		while(result != null){
     
			System.out.println(result.val);
			result = result.next;
		}
	}
	
	// Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示
	//(任意一个非叶子节点的权值,都不大于其左右子节点的权值),
	// 也就意味着可以通过数组来作为PriorityQueue的底层实现。
	// PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
	
	// 算法的时间复杂度是 O(N log(k))
	public ListNode mergeKLists(ListNode[] lists) {
     
		// 为PriorityQueue中的元素定义比较器
		Comparator<ListNode> cmp = new Comparator<ListNode>() {
     
			@Override
			public int compare(ListNode o1, ListNode o2) {
     
				// 小顶堆的比较器只能是前减后
				return o1.val-o2.val;
			}
        };
		
		// https://blog.csdn.net/u010623927/article/details/87179364
		// https://www.cnblogs.com/CarpenterLee/p/5488070.html
		PriorityQueue<ListNode> q = new PriorityQueue<ListNode>(cmp);
		for (ListNode list:lists){
     
			// add、offer失败时分别是抛出异常和false(添加元素)
			if (list != null) q.offer(list);
		}
		
		// 后期第一个节点不存储信息
		ListNode head = new ListNode(0);
        ListNode end = head;
		
		// isEmpty(为空返回true
		while(!q.isEmpty()){
     
			// remove、poll失败时分别是抛出异常和null(删除元素)
			end.next = q.poll();
			end = end.next;
			
			// 判断q.poll()提取出的链表是否只是包含一个元素
			// 若不止一个元素,将剩下的元素没加入到PriorityQueue
			if(end.next != null)q.offer(end.next);
		}
		
		return head.next;
	}
	
	// 优化版本01,所有的当前链表两两合并,减少合并次数,即减少了比较次数
	// 算法的时间复杂度是 O(N k log(k))
	public ListNode mergeKLists02(ListNode[] lists) {
     
		// 判断输入的数组不为空
		if(lists.length == 0) return null;
		
		int interval = 1;
		while(interval<lists.length){
     
			// i + interval防止索引溢出,interval*2跳过已经合并的list
			for (int i = 0; i + interval< lists.length; i=i+interval*2) {
     
				// 合并后覆盖第i个索引的地址
				lists[i]=mergeTwoLists(lists[i],lists[i+interval]);
			}
			// 每次间隔翻倍,画图出来很好理解
			interval*=2;
		}

		return lists[0];
	}
	
	// 算法的时间复杂度是 O(N k k))
	public ListNode mergeKLists01(ListNode[] lists) {
     
		ListNode ret = null;
		for(int i=0;i<lists.length;i++){
     
			ret = mergeTwoLists(ret,lists[i]);
		}
		
		return ret;
	}
	
	// 之前题目的解,递归的效率不高,但是代码简单
	public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
     
		// 停止条件
		if (l1 == null) return l2;
		if (l2 == null) return l1;
		
		// 如何迭代
		if(l1.val < l2.val){
     
			l1.next = mergeTwoLists(l1.next,l2);
			return l1;
		}else{
     
			l2.next = mergeTwoLists(l1,l2.next);
			return l2;
		}
	}
}

你可能感兴趣的:(LeetCode,java,leetcode)