【Leetcode链表/数组--合并两/多个有序链表/数组】21/BM4.合并两个排序的链表 23/BM5.合并k个已排序的链表 378.有序矩阵中第 K 小的元素 373.查找和最小的 K 对数

Leetcode 21.合并两个有序链表 / BM4.合并两个排序的链表

1.问题描述

在这里插入图片描述
在这里插入图片描述


2.解决方案

解法一:迭代法顺序合并(重点关注!!)

思路核心点就是设置一个哨兵节点和前继节点的思路!

在这里插入图片描述

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //1.虚拟头节点和前继节点
        ListNode preHead = new ListNode(-1);
        ListNode prev = preHead;
        //2.都不为空进入
        while(list1!=null&&list2!=null){
            if(list1.val<list2.val){
                prev.next=list1;
                prev=list1;
                list1=list1.next;
            }else{
                prev.next=list2;
                prev=list2;
                list2=list2.next;
            }
        }
        //3.合并剩余的链表
        if(list1==null){
            prev.next=list2;
        }else{
            prev.next=list1;
        }
        return preHead.next;
    }
}


解法二:递归法(没看)

在这里插入图片描述

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        } else if (l2 == null) {
            return l1;
        } else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}




Leetcode 23.合并K个升序链表 / BM5.合并k个已排序的链表

1.问题描述

在这里插入图片描述

2.解决方案

解法一:归并法顺序合并(重点关注!!)

在这里插入图片描述

1.最关键的就是分治递归的一个思想,divideMerge要返回Merge(list1,list2)的结果,那么list1,list2又是divideMerge处理的结果。
2.最终的递归出口其实是要么为null,要么left==right返回一个链表,然后Merge两个链表,并且作为divideMerge的返回,然后再一层一层往上Merge。
3.体会思想很重要。

import java.util.ArrayList;
public class Solution {
    //两个链表合并函数
    public ListNode Merge(ListNode list1, ListNode list2) { 
        //一个已经为空了,直接返回另一个
        if(list1 == null) 
            return list2;
        if(list2 == null)
            return list1;
        //加一个表头
        ListNode head = new ListNode(0); 
        ListNode cur = head;
        //两个链表都要不为空
        while(list1 != null && list2 != null){ 
            //取较小值的节点
            if(list1.val <= list2.val){ 
                cur.next = list1;
                //只移动取值的指针
                list1 = list1.next; 
            }else{
                cur.next = list2;
                //只移动取值的指针
                list2 = list2.next; 
            }
            //指针后移
            cur = cur.next; 
        }
        //哪个链表还有剩,直接连在后面
        if(list1 != null) 
            cur.next = list1;
        else
            cur.next = list2;
        //返回值去掉表头
        return head.next; 
    }
    
    //划分合并区间函数
    ListNode divideMerge(ArrayList<ListNode> lists, int left, int right){ 
        //终极递归出口:返回null
        if(left > right)  return null;
        //终极递归出口:返回一个链表
        if(left == right) return lists.get(left);
            
        //从中间分成两段,再将合并好的两段合并
        int mid = (left + right) / 2; 
        return Merge(divideMerge(lists, left, mid), divideMerge(lists, mid + 1, right));
    }
    
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        //k个链表归并排序
        return divideMerge(lists, 0, lists.size() - 1);
    }
}


解法二:优先队列 or 辅助数组排序构建新链表

优先队列,把所有链表构造为优先队列,然后从优先队列取出来构造新链表

import java.util.*;
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        //1.先注入优先队列
        PriorityQueue<Interger> queue =  new PriorityQueue<>();
        for(ListNode head : lists){
            while(head!=null){
                queue.add(head.val);
                head = head.next;
            }
        } 
        //2.然后从优先队列里面一直取数,生成链表
        ListNode dummyHead = new ListNode(-1);
        ListNode t = dummyHead;
        while(queue.size()>0){
            t.next = new ListNode(queue.remove());
            t = t.next;
        }
        return dummyHead.next;
    }
}

先把所有链表元素,放到数组中,然后排序,然后构造新链表

import java.util.*;
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        //1.把所有链表数据放到list中
        List<Integer> list = new ArrayList<>();
        for(ListNode head : lists){
            while(head!=null){
                list.add(head.val);
                head = head.next;
            }
        }
        //2.排序
        Collections.sort(list);
        //3.然后用排序后的list构造新链表
        ListNode dummyHead = new ListNode(-1);
        ListNode t = dummyHead;
        for(int a : list){
            t.next = new ListNode(a);
            t = t.next;
        }
        return dummyHead.next;
    }
}




Leetcode 378.有序矩阵中第 K 小的元素

1.问题描述

在这里插入图片描述

2.解决方案

解法一:归并法顺序合并(重点关注!!)

题解链接

在这里插入图片描述
在这里插入图片描述

主要思路就是:
1.优先队列存一个Node,里面包含矩阵的值val和坐标(i,j),然后根据val构建小根堆。
2.每次弹出最小的,然后把这一行后一个元素放到堆中,作为候选的Node。
3.然后弹出第k个就结束。

class Solution {
    class Node{
        //矩阵里面的值
        int val;
        //坐标(i,j)
        int i, j;
        Node(int val, int i, int j){
            this.val = val;
            this.i = i;
            this.j = j;
        }
    }
    public int kthSmallest(int[][] matrix, int k) {
        //按照Node的val从小到大 小根堆
        int len = matrix.length;
        PriorityQueue<Node> queue =  new PriorityQueue<>((t1, t2)->(t1.val - t2.val));
        //把第一列候选最小值加入堆排序
        for(int i=0;i<len;i++){
            queue.add(new Node(matrix[i][0], i, 0));
        }
        //从堆中取最小,如果一个节点被删除,然后把这个节点同行的后一个节点加入堆
        int cnt = 0;
        Node t = null;
        while(cnt!=k){
            //弹出一个元素 cnt++
            t = queue.remove();
            cnt++;
            //如果某一行元素弹没了 就不加入了
            if(t.j+1<=len-1){
                queue.add(new Node(matrix[t.i][t.j+1], t.i, t.j+1));
            }
        }
        return t.val;
    }
}


解法二:优先队列 or 辅助数组排序取值

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        //1.把二维数组全部放到一维数组
        List<Integer> list = new ArrayList<>();
        int len = matrix.length;
        for(int i=0;i<len;i++){
            for(int j=0;j<len;j++){
                list.add(matrix[i][j]);
            }
        }
        //2.排序然后取第k小的数据
        Collections.sort(list);
        return list.get(k-1);
    }
}


解法三:二分法

对于求中间数mid的值有两种方法,对于第一种过不了这个样例,因为-5 -4 求出来一直是-4,
而第二种方法 -5 -4求出来是-5就可以过

//int mid = (left+right)/2;
int mid = left + ((right - left) >> 1);

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

class Solution {
    //1:在左边 2:在右边 3:就是mid但是有可能mid不在矩阵中,所以相当于在mid左边了
    public int check(int[][] matrix, int len, int mid, int k){
        int i = len-1;
        int j = 0;
        int num = 0;
        while(i>=0 && j<=len-1){
            if(matrix[i][j]<=mid){
                //说明这一列都小于等于mid 都算上
                num += i+1;
                j++;
                continue;
            }
            if(matrix[i][j]>mid){
                //这一列往上找 
                i--;
                continue;
            }
        }
        //小于等于mid的数字个数是num
        if(num>k) return 1;
        if(num<k) return 2;
        if(num==k) return 3;

        //编译出口 不可能从这走
        return -1;
    }
    public int kthSmallest(int[][] matrix, int k) {
        int len = matrix.length;
        int left = matrix[0][0];
        int right = matrix[len-1][len-1];
        while(left<right){
            //int mid = (left+right)/2;
            int mid = left + ((right - left) >> 1);
            int flag = check(matrix, len, mid, k);
            if(flag==1||flag==3) right=mid;
            if(flag==2) left=mid+1;
        }

        return right;
    }
}




Leetcode 373.查找和最小的 K 对数

1.问题描述

题目链接
在这里插入图片描述

2.解决方案

解法一:归并最小堆排序(重点关注!!)

在这里插入图片描述

1.先把num1.0.1…len-1 和 num2.0 组合起来加到堆中。
2.取最小t,然后加入结果集。
3.t的nums1_index不变 t.nums2_index+1加入堆。

class Solution {
    class Node{
        int nums1_index;
        int nums2_index;
        Node(int nums1_index, int nums2_index){
            this.nums1_index = nums1_index;
            this.nums2_index = nums2_index;
        }
    }
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        //按照数字对的和从小到大
        PriorityQueue<Node> queue =  new PriorityQueue<>((o1, o2)->((nums1[o1.nums1_index]+nums2[o1.nums2_index])-(nums1[o2.nums1_index]+nums2[o2.nums2_index])));
        //先把num1.0.1..len-1 和 num2.0 组合起来加到堆中
        for(int i=0;i<nums1.length;i++){
            queue.add(new Node(i, 0));
        }
        //取最小数对,加入结果集
        List<List<Integer>> ans = new ArrayList<>();
        int cnt = 0;
        Node t = null;
        while(cnt<k&&queue.size()>0){
            //取最小 然后加入结果集
            t = queue.remove();
            ans.add(Arrays.asList(new Integer[]{nums1[t.nums1_index], nums2[t.nums2_index]}));
            cnt++;
            //t的nums1_index不变 t.nums2_index+1加入堆
            if(t.nums2_index+1<=nums2.length-1){
                queue.add(new Node(t.nums1_index, t.nums2_index+1));
            }
        }
        return ans;
    }
}


解法二:二分查找

没看懂

在这里插入图片描述

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        int m = nums1.length;
        int n = nums2.length;

        /*二分查找第 k 小的数对和的大小*/
        int left = nums1[0] + nums2[0];
        int right = nums1[m - 1] + nums2[n - 1];
        int pairSum = right;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            long cnt = 0;
            int start = 0;
            int end = n - 1;
            while (start < m && end >= 0) {
                if (nums1[start] + nums2[end] > mid) {
                    end--;
                } else {
                    cnt += end + 1;
                    start++;
                }
            }
            if (cnt < k) {
                left = mid + 1;
            } else {
                pairSum = mid;
                right = mid - 1;
            }
        }

        List<List<Integer>> ans = new ArrayList<>();
        int pos = n - 1;
        /*找到小于目标值 pairSum 的数对*/
        for (int i = 0; i < m; i++) {
            while (pos >= 0 && nums1[i] + nums2[pos] >= pairSum) {
                pos--;
            }
            for (int j = 0; j <= pos && k > 0; j++, k--) {
                List<Integer> list = new ArrayList<>();
                list.add(nums1[i]);
                list.add(nums2[j]);
                ans.add(list);
            }
        }

        /*找到等于目标值 pairSum 的数对*/
        pos = n - 1;
        for (int i = 0; i < m && k > 0; i++) {
            int start1 = i;
            while (i < m - 1 && nums1[i] == nums1[i + 1]) {
                i++;
            }
            while (pos >= 0 && nums1[i] + nums2[pos] > pairSum) {
                pos--;
            }
            int start2 = pos;
            while (pos > 0 && nums2[pos] == nums2[pos - 1]) {
                pos--;
            }
            if (nums1[i] + nums2[pos] != pairSum) {
                continue;
            }
            int count = (int) Math.min(k, (long) (i - start1 + 1) * (start2 - pos + 1));
            for (int j = 0; j < count && k > 0; j++, k--) {
                List<Integer> list = new ArrayList<>();
                list.add(nums1[i]);
                list.add(nums2[pos]);
                ans.add(list);
            }
        }
        return ans;
    }
}


解法三:全量排序

把所有数字对都放到list中,然后按照两个的和排序,取出前三对。

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        List<List<Integer>> ans = new ArrayList<>();
        for(int i=0;i<nums1.length;i++){
            for(int j=0;j<nums2.length;j++){
                List<Integer> list = new ArrayList<>();
                list.add(nums1[i]);
                list.add(nums2[j]);
                ans.add(list);
            }
        }
        Collections.sort(ans, (t1, t2) -> (t1.get(0)+t1.get(1)) - (t2.get(0)+t2.get(1)));
        List<List<Integer>> result = new ArrayList<>();
        for(int i=0;i<k&&i<=ans.size()-1;i++) result.add(ans.get(i));
        return result;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值