LC23 - 合并K个升序链表 - 归并排序/合并k个有序数组/链表/优先队列/比较器/尾插法

题目

合并K个升序链表

方法1 合并分治

归并排序思路:合并K个有序数列(递归 + 合并相邻两个子序列)
此题思路可借鉴为:递归 + 合并2个有序链表

子问题 LC21 合并2个有序链表(递归)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    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;
        }
    }
}

归并排序的主题思想就是通过递归合并k个有序数组。可以参考归并排序的写法。

归并排序代码

import java.util.Arrays;

public class MergeSort {

	public static void main(String[] args) {
		int[] arr = {8,4,5,7,1,3,6,2};
		int[] temp = new int[arr.length];
		mergeSort(arr, 0, arr.length-1, temp);
		
		System.out.println("sorted:" + Arrays.toString(arr));
	}
	
	//分解的方法
	public static void mergeSort(int[] arr, int left, int right, int[] temp) {
		if(left < right) {
			int mid = (left + right) / 2;
			//向左分解
			mergeSort(arr, left, mid, temp);
			//向右 
			mergeSort(arr, mid+1, right, temp);
			//到这一步为止,将所有元素分割成最小单位,输出为{8,4,5,7,1,3,6,2}
			//合并
			merge(arr, left, mid, right, temp);
		}
	}
	
	//这个函数的目的是合并相邻两个子序列:合并的方法

	/**
	 * @param arr 待排序的数组
	 * @param left 左边有序序列的初始索引
	 * @param mid 左边有序序列的最后一个索引
	 * 		  mid+1	右边有序序列的初始索引
	 * @param right 右边有序序列的最后一个索引
	 * @param temp 中转数组
	 */
	public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
		
		int i = left;//初始化i, 左边有序序列的初始索引
		int j = mid + 1;//右边有序序列的初始索引
		int t = 0;//指向temp数组的当前索引
		
		//第一步,将左右两边的数比大小,依次拷贝到temp数组,直到左右两边有一边处理完毕
		while(i <= mid && j <= right) {
			if(arr[i] < arr[j]) {
				temp[t] = arr[i];//temp从0开始,谁小就
				t++;
				i++;				
			}
			else {
				temp[t] = arr[j];
				t++;
				j++;
			}
		}	
		//第二步,将有剩余数据的一方填充到temp数组
		while(i <= mid) {//左边的序列有剩余,全部填充到temp,当i等于mid的时候还有1个数留在左边,所以必须是小于等于
			temp[t] = arr[i];
			i++;
			t++;
		}
		while(j <= right) {//右边的序列有剩余,全部填充到temp
			temp[t] = arr[j];
			j++;
			t++;
		}			
		//第三步,将temp数组拷回arr
		//注意!并不是每次都拷贝所有元素,这个是用于递归过程的
		t = 0;
		int tempLeft = left;
		while(tempLeft <= right) {
			arr[tempLeft] = temp[t];
			t++;
			tempLeft++;
		}
	}
	
}

k个有序链表代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        //归并排序
        return merge(lists, 0, lists.length - 1);
        }

        //分解+递归
        public ListNode merge(ListNode[] lists, int left, int right){
            if(right == left){
                return lists[left];
            }
            if(left > right){
                return null;
            }
            else{
                int mid = (left + right) / 2;
                return mergeTwoLists(merge(lists, left, mid), merge(lists, mid+1, right));
            }
           
        }
        
        //合并 两个相邻的子链表
        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;
        }

    }
}
方法2 优先队列

思路:分别取每个链表的头节点比较,将最小的作为新链表的第一个节点。在选取最小元素的时候,我们可以用优先队列来优化这个过程。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {

        if(lists == null || lists.length <= 0){
            return null;
        }

        PriorityQueue<ListNode> pq = new PriorityQueue<>(cmp);

        for(ListNode node:lists){
            if(node != null){
                pq.add(node);//将每列最靠前的元素加入到队列中
            }
        }

        //哑节点
        ListNode dummy = new ListNode(0);

        //尾插法,将pq中的ListNode建立成链表
        ListNode tail = dummy;

        while(!pq.isEmpty()){
            ListNode curr = pq.poll();
            tail.next = curr;
            //因为每次都是将新节点插入到链表尾部,所以需要加入一个始终指向链表尾部的指针tail,以便于让新节点插入到链表尾部
            tail = tail.next;
            
            if(curr.next != null){
                pq.add(curr.next);//将后边的链表顶替上来
            }
        }

        return dummy.next;
    }

    Comparator<ListNode> cmp = new Comparator<ListNode>(){
    	public int compare(ListNode o1, ListNode o2) {
    		return o1.val - o2.val;//升序
    	}
    };
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值