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;
}
}