合并K个有序链表

public class _023_MergeKLists {
	/**
	 * 链表结点类
	 * 
	 * @author luzhen-work-pc
	 *
	 */
	private static class ListNode {
		int val;
		ListNode next;

		ListNode(int val) {
			this.val = val;
		}
	}

	/**
	 * 堆中结点类
	 * 
	 * @author luzhen-work-pc
	 *
	 */
	private static class Node implements Comparable<Node> {
		int val; // 链表中结点值
		int index; // 对应值所在链表(lists的下标)

		Node(int val, int index) {
			this.val = val;
			this.index = index;
		}

		@Override
		public int compareTo(Node o) { // 构建堆和维护对所用的比较方法,注意越界问题
			// TODO Auto-generated method stub
			if (val == o.val)
				return 0;
			else if (val == Integer.MAX_VALUE)
				return 1;
			else if (o.val == Integer.MAX_VALUE)
				return -1;
			else
				return val - o.val;
		}
	}

	/**
	 * 将K个有序链表归并成一个链表。 
	 * 思想:每次取一条链表的头元素,构建成一个最小堆,然后从堆顶取出当前最小元素作为归并后链表的下一个元素(末尾)
	 * 如果此结点所在链表还有元素,那么就将那个元素放入堆顶,维护堆,对应代码①
	 *  
	 * 如: 
	 * 1-3-5 
	 * 2-4-6 
	 * 2-3-5
	 * 当前堆:(1-0)-(2-1)-(2-2) 
	 * 取出(1-0),链表0中还有元素,将(3-0)取出,然后维护堆 
	 * 当前堆:(2-1)-(3-0)-(2-2)
	 * 
	 * 如果此结点所在链表中没有剩余元素,那么从剩余链表(还有剩余元素的)中找到下一个合适的(当前未加入堆的最小元素)
	 * 加入到堆顶,维护堆,然后移动指针,对应代码②
	 * 
	 * 如:
	 * 1-2-3
	 * 4-5-6
	 * 7-8-9
	 * 当前堆:(1-0)-(4-1)-(7-2)
	 * 取出(1-0),加入(2-0);取出(2-0),加入(3-0),取出(3-0),此时链表0为空
	 * 那么需要找出链表1和链表2当前头结点下一个结点中的最小值,这里就是链表1中的5,将其加入到堆顶,然后维护堆
	 * 
	 * @param lists
	 * @return
	 */
	private static ListNode mergeKLists(ListNode[] lists) {
		if (lists == null || lists.length == 0)
			return null;
		Node[] minHeap = new Node[lists.length];
		boolean stop = true; // 循环终止标记(当所有链表中元素都被加入到堆中后,终止)
		for (int i = 0; i < lists.length; i++) { // 初始化堆元素
			if (lists[i] != null) {
				minHeap[i] = new Node(lists[i].val, i);
				stop = false;
			} else { // 如果当前链表为空,那么加入一个值为整形最大值的结点(不会参与排序,用于构建最大堆和处理空链表)
				minHeap[i] = new Node(Integer.MAX_VALUE, -1);
			}
		}
		buildHeap(minHeap);
		ListNode dummy = new ListNode(0), curr = dummy; // 维护一个归并后链表的虚头结点dummy,用来返回,curr用来构建链表
		while (!stop) { // 每次循环开始设置stop为true,只要存在非空的链表,stop就会被设置为false
			stop = true;
			Node minNode = minHeap[0];
			curr.next = new ListNode(minNode.val);
			curr = curr.next;
			if (lists[minNode.index] != null) // 堆顶元素(最小)对应的链表还有元素,移动指针到下一个结点,准备将它加入堆顶
				lists[minNode.index] = lists[minNode.index].next;
			if (lists[minNode.index] != null) { // ①
				stop = false;
				minHeap[0] = new Node(lists[minNode.index].val, minNode.index);
				heapify(minHeap, 0);
			} else { // ②
				// 到这里,说明之前堆顶元素(最小)对应的链表已经为空
				int minIndex = -1, minValue = Integer.MAX_VALUE;
				for (int i = 0; i < lists.length; i++) {
					if (lists[i] == null) // 跳过空链表
						continue;
					// 当前非空链表的头结点都已经在堆中(因为每次取堆顶元素,其他链表头结点还在堆中)
					// 找到这些链表中头结点下一个结点中的最小值,放到堆顶,维护堆对应③
					if (lists[i] != null && lists[i].next != null && lists[i].next.val < minValue) {
						minIndex = i;
						minValue = lists[i].next.val;
						stop = false;
					}
				}
				if (!stop) { // ③ 存在非空链表,找到了这些链表中链表头结点下一个结点中的最小结点
					lists[minIndex] = lists[minIndex].next;
					minHeap[0] = new Node(minValue, minIndex);
					heapify(minHeap, 0);
				}
			}
		}
		for (int i = 0; i < lists.length - 1; i++) { // 此时所有链表为空,检查堆中是否还有未参与排序的结点
			minHeap[0] = new Node(Integer.MAX_VALUE, -1);
			heapify(minHeap, 0);
			if (minHeap[0].val == Integer.MAX_VALUE)
				break;
			curr.next = new ListNode(minHeap[0].val);
			curr = curr.next;
		}
		return dummy.next;
	}

	private static void buildHeap(Node[] nodes) {
		for (int i = (nodes.length - 2) / 2; i >= 0; i--)
			heapify(nodes, i);
	}

	private static void heapify(Node[] nodes, int i) {
		int left = i * 2 + 1, right = i * 2 + 2;
		int min = i;
		if (left < nodes.length && nodes[left].compareTo(nodes[min]) < 0)
			min = left;
		if (right < nodes.length && nodes[right].compareTo(nodes[min]) < 0)
			min = right;
		if (min != i) {
			Node temp = nodes[i];
			nodes[i] = nodes[min];
			nodes[min] = temp;
			heapify(nodes, min);
		}
	}
	
	public static void main(String[] args) {
		// ListNode l1 = new ListNode(1);
		// l1.next = new ListNode(4);
		// l1.next.next = new ListNode(6);
		// ListNode l2 = new ListNode(2);
		// l2.next = new ListNode(3);
		// l2.next.next = new ListNode(4);
		// ListNode l3 = new ListNode(1);
		// l3.next = new ListNode(2);
		// l3.next.next = new ListNode(5);

		// ListNode l1 = new ListNode(1);
		// l1.next = new ListNode(2);
		// l1.next.next = new ListNode(3);
		// ListNode l2 = new ListNode(4);
		// l2.next = new ListNode(5);
		// l2.next.next = new ListNode(6);
		// ListNode l3 = new ListNode(7);
		// l3.next = new ListNode(8);
		// l3.next.next = new ListNode(9);

		ListNode l1 = null;
		ListNode l2 = new ListNode(-1);
		l2.next = new ListNode(5);
		l2.next.next = new ListNode(11);
		ListNode l3 = null;
		ListNode l4 = new ListNode(6);
		l4.next = new ListNode(10);

		ListNode res = mergeKLists(new ListNode[] { l1, l2, l3, l4 });
		printList(res);
	}

	private static void printList(ListNode l) {
		ListNode curr = l;
		while (curr != null) {
			System.out.print(curr.val);
			if (curr.next != null)
				System.out.print("->");
			curr = curr.next;
		}
		System.out.println();
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值