Leetcode 23,543合并K个升序链表,二叉树的直径

1.题目描述

题目解答:

这道题让我们求的是二叉树的直径,而不是深度,可能我们在求解的过程中会自动认为它会经过根节点,但并不是这样的

当二叉树是如上的结构的时候,我们发现直径并没有经过根节点。所以我们不可以只是单纯的求解左子树和右子树的最大深度和。

那么我们应该怎样求解呢,其实还是要进行左右子树深度的计算,而且要递归遍历每一个节点,但我们要求出当前节点的最大深度,这样当它返回给它的父节点的时候,父节点就可以同时获取到左右子树的最大深度。

父节点每次将左右深度相加作为当前的最大直径,通过一个变量来维护这个最大值,对每一个节点都进行如此的计算,这样我们就可以得出最后的答案。

java 复制代码
class Solution {
    // 全局变量,记录遍历过程中遇到的最大直径(节点数)
    int max = 0;

    // 递归函数:返回以 root 为根的树的深度(从根到最远叶子的节点数)
    public int dfs(TreeNode root) {
        // 空节点深度为 0
        if (root == null) {
            return 0;
        }

        // 递归计算左子树的深度
        int left = dfs(root.left);

        // 递归计算右子树的深度
        int right = dfs(root.right);

        // 经过当前节点的直径 = 左深度 + 右深度
        // (左子树最远叶子 → 当前节点 → 右子树最远叶子,中间的边数 = 左深度 + 右深度)
        // 注意:这里算的是"路径上的节点数",而题目要求的直径是"边数"
        //       边数 = 节点数 = left + right(因为 left 和 right 已经是各自子树的深度,
        //       正好等于从根到叶子的边数,加起来就是左叶子到右叶子的边数)
        max = Math.max(left + right, max);

        // 返回当前树的深度(取左右较大的深度 + 1,1 代表当前节点自身)
        return Math.max(left, right) + 1;
    }

    // 主函数
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);         // 递归遍历整棵树,期间更新 max
        return max;        // 返回全局最大值
    }
}

2.题目描述

题目解答

1.按次序循环合并

由于我们之前已经写过合并两个升序链表,所以我们可以从第一个位置开始,与第二个进行合并,在和第三个进行合并,直到所有元素都被合并完毕,我们就得到了最终的结果。

这样做的时间复杂度是O(k²·n),假设链表有k条,那么每次循环都会遍历之前合并完成的链表,一共会遍历k²·n/2次。

java 复制代码
//循环遍历
class Solution {
    public ListNode MergeTwoLists(ListNode l1,ListNode l2){
      ListNode cur1 = l1;
      ListNode cur2 = l2;
      ListNode pre = new ListNode(0);
      ListNode pre1 = pre;
      while(cur1!=null&&cur2!=null){
        if(cur1.val<cur2.val){
            pre.next = cur1;
            cur1=cur1.next;
        }
        else{
            pre.next = cur2;
            cur2=cur2.next;
        }
        pre= pre.next;
      }

      if(cur1==null){
        pre.next = cur2;
      }
      if(cur2==null){
        pre.next = cur1;
      }
      return pre1.next;
    }
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
           return null;
        }
        if(lists[0]==null&&lists.length==1){
            return null;
        }
        ListNode pre = new ListNode(0);
        if(lists.length==1){
            return lists[0];
        }
        pre.next= MergeTwoLists(lists[0],lists[1]);
        for(int i = 2;i<lists.length;i++){
           pre.next=MergeTwoLists(pre.next,lists[i]);
        }
        return pre.next;
    }
}

2.分治递归

和上一步都是一样的,只不过将每一个链表拆成一个独立的部分,进行两两合并,最后合并成一个完整的链表

这样做的时间复杂度是O(k·n·log k),因为我们进行了分治,整个数组的遍历只需要log k次。

java 复制代码
//分治递归
class Solution {
    public ListNode MergeTwoLists(ListNode l1,ListNode l2){
      ListNode cur1 = l1;
      ListNode cur2 = l2;
      ListNode pre = new ListNode(0);
      ListNode pre1 = pre;
      while(cur1!=null&&cur2!=null){
        if(cur1.val<cur2.val){
            pre.next = cur1;
            cur1=cur1.next;
        }
        else{
            pre.next = cur2;
            cur2=cur2.next;
        }
        pre= pre.next;
      }

      if(cur1==null){
        pre.next = cur2;
      }
      if(cur2==null){
        pre.next = cur1;
      }
      return pre1.next;
    }
    public ListNode mergeKLists(ListNode[] lists) {
        return merge(lists,0,lists.length-1);
    }

    public ListNode merge(ListNode[] lists,int l,int r){
        if (l == r) {
            return lists[l];
        }
        if (l > r) {
            return null;
        }
        int mid = (l + r) >> 1;
        return MergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    }
}

3.引入最小堆

k 条有序链表各把当前头部节点扔进一个最小堆,堆顶永远是所有候选中的最小值。每次从堆中弹出堆顶节点接入结果链表,然后立即从被弹出的那条链表中补入它的后继节点进堆,确保堆里始终维持 k 个代表参与竞争。每个节点进出堆各一次,堆大小为 k,单次调整需 O(log k),总共处理 k×n 个节点,

时间复杂度 :O(k×n×log k)。

java 复制代码
class Solution {
   PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);
    public ListNode mergeKLists(ListNode[] lists) {
        for(ListNode head:lists){
            if(head!=null){
                pq.add(head);
            }
        }

        ListNode pre = new ListNode(0);
        ListNode cur = pre;

        while(!pq.isEmpty()){
            ListNode node = pq.poll();
            cur.next = node;
            cur=cur.next;
            if(node.next!=null){
                pq.add(node.next);
            }
        }

        return pre.next;
    }

    
}
相关推荐
huameinan狮子1 小时前
Adaboost算法原理与计算实例
算法
别怪我很水1 小时前
API中提供了VelocityTracker类用于计算触摸事件MotionEvent的速度,而其内部默认使用的方法就是最小二乘法,本 ...
算法·gitee·最小二乘法
Re.不晚4 小时前
挑战做100道力扣算法- DAY1
算法·leetcode·职场和发展
蓝悦无人机5 小时前
《Planning algorithms》读书笔记——第1章 引言
算法·读书笔记·规划算法·lavalle
Sagittarius_A*5 小时前
分组密码基础(二):Feistel 结构与 DES 的设计思想
算法·信息安全·密码学·des·数论
青山木5 小时前
Hot 100 --- 搜索插入位置
java·数据结构·算法·leetcode
alexwang2116 小时前
HDU 4348 详细题解
c++·算法·题解·hdu·主席树·可持久化数据结构·可持久化线段树
可编程芯片开发6 小时前
基于分段变步长搜索RMDCFT算法的天基雷达空间机动目标检测方法MATLAB仿真,对比RMDCFT算法
算法