首页 > 其他分享 >力扣 653. 两数之和 IV 二叉树/binary-tree two-sum IV

力扣 653. 两数之和 IV 二叉树/binary-tree two-sum IV

时间:2024-11-12 22:32:06浏览次数:1  
标签:遍历 resFlag int IV 二叉树 numSet root 节点 两数

数组系列

力扣数据结构之数组-00-概览

力扣.53 最大子数组和 maximum-subarray

力扣.128 最长连续序列 longest-consecutive-sequence

力扣.1 两数之和 N 种解法 two-sum

力扣.167 两数之和 II two-sum-ii

力扣.170 两数之和 III two-sum-iii

力扣.653 两数之和 IV two-sum-IV

力扣.015 三数之和 three-sum

力扣.016 最接近的三数之和 three-sum-closest

力扣.259 较小的三数之和 three-sum-smaller

题目

给定一个二叉搜索树 root 和一个目标结果 k,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true。

示例 1:

        5
       / \
      3   6
     / \    \
    2   4    7

输入: root = [5,3,6,2,4,null,7], k = 9

输出: true

示例 2:

        5
       / \
      3   6
     / \    \
    2   4    7

输入: root = [5,3,6,2,4,null,7], k = 28

输出: false

提示:

二叉树的节点个数的范围是 [1, 10^4].

-10^4 <= Node.val <= 10^4

题目数据保证,输入的 root 是一棵 有效 的二叉搜索树

-10^5 <= k <= 10^5

思路

这种二叉树的题目,我们可以分为两步:

1)二叉树遍历转换为数组

2)数组,然后复用前面 T001/T167 的解法。

常见算法

树的遍历

面试算法:二叉树的前序/中序/后序/层序遍历方式汇总 preorder/Inorder/postorder/levelorder

树的遍历有多种方式:前序 中序 后序 层序

找到符合的结果

  1. 暴力

2)借助 Hash

  1. 排序+二分

4)双指针==》针对有序数组

在这个场景里面,最简单好用的应该是 Hash 的方式。其他的我们就不再演示。

本文主要在复习一下树的遍历,太久没做了,忘记了。

树的遍历回顾

在二叉树中,前序遍历、中序遍历和后序遍历是三种常见的遍历方式,递归实现是最直观和常用的方式。

下面是这三种遍历的基本概念和 Java 递归实现的代码示例。

1. 前序遍历 (Preorder Traversal)

遍历顺序: 根节点 -> 左子树 -> 右子树

递归实现:

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
        val = x;
    }
}

public class BinaryTree {
    public void preorderTraversal(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.print(root.val + " "); // 先访问根节点
        preorderTraversal(root.left);    // 遍历左子树
        preorderTraversal(root.right);   // 遍历右子树
    }
}

2. 中序遍历 (Inorder Traversal)

遍历顺序: 左子树 -> 根节点 -> 右子树

递归实现:

public class BinaryTree {
    public void inorderTraversal(TreeNode root) {
        if (root == null) {
            return;
        }
        inorderTraversal(root.left);     // 遍历左子树
        System.out.print(root.val + " "); // 访问根节点
        inorderTraversal(root.right);    // 遍历右子树
    }
}

3. 后序遍历 (Postorder Traversal)

遍历顺序: 左子树 -> 右子树 -> 根节点

递归实现:

public class BinaryTree {
    public void postorderTraversal(TreeNode root) {
        if (root == null) {
            return;
        }
        postorderTraversal(root.left);    // 遍历左子树
        postorderTraversal(root.right);   // 遍历右子树
        System.out.print(root.val + " "); // 访问根节点
    }
}

总结

  • 前序遍历:先访问根节点,再遍历左子树和右子树。

  • 中序遍历:先遍历左子树,再访问根节点,最后遍历右子树。

  • 后序遍历:先遍历左子树,再遍历右子树,最后访问根节点。

这些遍历方式的递归实现思路基本相同,区别在于访问根节点的时机不同。在实际应用中,可以根据需求选择不同的遍历方式。

前中后是以 root 的节点为主视角,看什么时候被访问。

v1-前序遍历

思路

我们可以把整个数组完全构建出来,然后复用以前的解法。

当然这样会比较慢,我们可以在遍历的时候找到对应的结果。

传递的值更新问题,我们用 resFlag 数组来记录最后的结果。

实现

class Solution {
   public boolean findTarget(TreeNode root, int k) {
        // 构建结果列表
        Set<Integer> numSet = new HashSet<>();

        int[] resFlag = new int[]{1};
        resFlag[0] = 0;
        preOrderTravel(numSet, root, k, resFlag);

        return resFlag[0] != 0;
    }

    private void preOrderTravel(Set<Integer> numSet,
                                TreeNode root,
                                int k,
                                int[] resFlag) {
        if(root == null || resFlag[0] != 0) {
            return;
        }

        // 符合
        int value = root.val;
        if(numSet.contains(k - value)) {
            resFlag[0] = 1;
            return;
        }
        numSet.add(value);

        preOrderTravel(numSet, root.left, k, resFlag);
        preOrderTravel(numSet, root.right, k, resFlag);
    }
}

效果

3ms 79.82

v2-中序遍历

思路

采用中序遍历,其他保持不变。

代码

public boolean findTarget(TreeNode root, int k) {
    // 构建结果列表
    Set<Integer> numSet = new HashSet<>();
    int[] resFlag = new int[]{1};
    resFlag[0] = 0;
    inOrderTravel(numSet, root, k, resFlag);
    return resFlag[0] != 0;
}

private void inOrderTravel(Set<Integer> numSet,
                           TreeNode root,
                           int k,
                           int[] resFlag) {
    if(root == null || resFlag[0] != 0) {
        return;
    }
    inOrderTravel(numSet, root.left, k, resFlag);
    // 符合
    int value = root.val;
    if(numSet.contains(k - value)) {
        resFlag[0] = 1;
        return;
    }
    numSet.add(value);
    inOrderTravel(numSet, root.right, k, resFlag);
}

效果

3ms 79.82%

v3-后序遍历

思路

很简单,调整为后续遍历即可。

实现

    public boolean findTarget(TreeNode root, int k) {
        // 构建结果列表
        Set<Integer> numSet = new HashSet<>();

        int[] resFlag = new int[]{1};
        resFlag[0] = 0;
        postOrderTravel(numSet, root, k, resFlag);

        return resFlag[0] != 0;
    }

    private void postOrderTravel(Set<Integer> numSet,
                                 TreeNode root,
                                 int k,
                                 int[] resFlag) {
        if(root == null || resFlag[0] != 0) {
            return;
        }

        postOrderTravel(numSet, root.left, k, resFlag);

        postOrderTravel(numSet, root.right, k, resFlag);

        // 符合
        int value = root.val;
        if(numSet.contains(k - value)) {
            resFlag[0] = 1;
            return;
        }
        numSet.add(value);
    }

效果

4ms 29.82%

估计是服务器波动,也和测试用例有一定的关系。

v4-层序遍历

层序遍历

层序遍历(Level Order Traversal)是按层级顺序从上到下、从左到右遍历二叉树。

与前序、中序、后序不同,层序遍历通常是使用广度优先搜索(BFS)实现的,常见的做法是使用队列来辅助遍历。

层序遍历的实现步骤:

  1. 使用一个队列存储当前层的节点。

  2. 先将根节点加入队列。

  3. 然后逐层遍历队列,取出队首节点,访问该节点,并将它的左右子节点(如果有的话)依次加入队列。

  4. 重复这个过程,直到队列为空。

层序遍历的 Java 实现:

// 层序遍历
public void levelOrderTraversal(TreeNode root) {
    if (root == null) {
        return;
    }
    
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root); // 将根节点加入队列
    
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); // 取出队首元素
        System.out.print(node.val + " "); // 访问当前节点
        
        if (node.left != null) {
            queue.offer(node.left); // 左子节点加入队列
        }
        if (node.right != null) {
            queue.offer(node.right); // 右子节点加入队列
        }
    }
}

代码说明:

  1. 队列:我们使用 LinkedList 来实现队列,因为队列的特点是先入先出(FIFO)。

  2. 访问节点:每次从队列中取出一个节点,访问它并将其左右子节点加入队列。

  3. 层级遍历:这种方式会保证节点按照层次顺序被访问,父节点先于子节点。

结合本题

    public boolean findTarget(TreeNode root, int k) {
        // 构建结果列表
        Set<Integer> numSet = new HashSet<>();

        // 队列 模拟
        int[] resFlag = new int[]{1};
        resFlag[0] = 0;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        levelOrderTravel(numSet, queue, k, resFlag);

        return resFlag[0] != 0;
    }

    private void levelOrderTravel(Set<Integer> numSet,
                                  Queue<TreeNode> queue,
                                  int k,
                                  int[] resFlag) {
        while (!queue.isEmpty()) {
            // 取出
            TreeNode root = queue.poll();
            // 符合
            int value = root.val;
            if(numSet.contains(k - value)) {
                resFlag[0] = 1;
                return;
            }
            numSet.add(value);

            // 加入左右
            if(root.left != null) {
                queue.offer(root.left);
            }
            if(root.right != null) {
                queue.offer(root.right);
            }
        }
    }

效果

4ms 29.82

小结

层序遍历放在本题看起来没有特别大的优势。

不过层序遍历在有些场景还是很有用的,比如 T337 打家劫舍 III。

v5-还有高手

思路

除了这 4 种方式,还有其他更快的方式吗?

那就是我们其实对二叉树的理解还是不够深入。

中序遍历之后,结果其实是一个升序数组。

也就是我们可以利用排序后的数组进行处理,结合 T167.

中序是:left>val>right

回顾 T167

其实就是两步

1)构建有序数组

2)双指针直接获取

当然双指针也可以用二分法,此处不再赘述、

java

    public boolean findTarget(TreeNode root, int k) {
        List<Integer> sortList = new ArrayList<>();

        // 中序获取排序数组
        inOrderTravel(sortList, root);

        // 双指针
        return twoSum(sortList, k);
    }

    public boolean twoSum(List<Integer> sortList, int target) {
        int n = sortList.size();
        int left = 0;
        int right = n-1;

        while (left < right) {
            int sum = sortList.get(left) + sortList.get(right);
            if(sum == target) {
                return true;
            }
            if(sum < target) {
                left++;
            }
            if(sum > target) {
                right--;
            }
        }

        return false;
    }


    private void inOrderTravel(List<Integer> sortList,
                               TreeNode root) {
        if(root == null) {
            return;
        }
        inOrderTravel(sortList, root.left);

        // add
        sortList.add(root.val);

        inOrderTravel(sortList, root.right);
    }

效果

3ms 79.82%

小结

这种解法,其实已经很巧妙了。

本题的难度定位在简单有点浪费,用到这种方式实际上已经结合了多个知识点。

标签:遍历,resFlag,int,IV,二叉树,numSet,root,节点,两数
From: https://www.cnblogs.com/houbbBlogs/p/18542795

相关文章

  • 痞子衡嵌入式:关于恩智浦SDK2.0里事务型中断处理函数(DriverIRQHandler)的重定向注意事
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是SDK2.0里事务型中断处理函数(DriverIRQHandler)的重定向注意事项。最近有一个i.MXRT客户在使用官方SDK外设驱动里的中断处理函数时遇到了代码重定向失效问题,客户用得是一个XIPFlash工程,想把程序中......
  • 全局平衡二叉树 (GBST) 小记
    全局平衡二叉树(GBST)小记以下全局平衡二叉树简称\(\text{GBST(GlobelBalancedSearchTree)}\)。我认识的大多数人,对\(\text{GBST}\)的理解基本上都是静态\(\text{LCT}\),或者静态\(\text{TopTree}\),不过我对\(\text{LCT}\)的理解可能还差一点,所以我不打算从阉割\(......
  • Recursive Algorithm for Sliding Signal Processing
    目录概滑动窗口上的快速算法Farhang-BoroujenyB.andGazorS.Generalizedslidingfftanditsapplicationtoimplementationofblocklmsadaptivefilters.TSP,1994JacobsenE.andLyonsR.TheslidingDFT.SPM,2003.JacobsenE.andLyonsR.Anupdateto......
  • 【大数据测试 Hive数据库--保姆级教程】
    大数据测试Hive数据库详细教程一、环境准备二、Hive数据库功能测试1.创建表2.插入数据3.查询数据4.使用条件过滤查询5.删除数据三、Hive数据库性能测试1.查询响应时间2.大数据量查询测试3.分区表性能测试4.并发查询性能四、Hive数据完整性测试1.数据加......
  • leetcode 29. 两数相除
    29.两数相除一、使用long类型classSolution{public:longdivide2(longdividend,longdivisor){if(dividend<0&&divisor<0)returndivide2(-dividend,-divisor);elseif(dividend<0&&divisor>0)return-div......
  • 新手入门Java自动化测试的利器:Selenium WebDriver
    新手入门Java自动化测试的利器:SeleniumWebDriver今天我们将深入探讨一款强大的Java自动化测试工具——SeleniumWebDriver。在正式介绍SeleniumWebDriver之前,让我们首先对Selenium本身进行简要概述,以便更好地理解其背景和功能。官方学习网站:https://www.selenium.dev/Sele......
  • cf round 898 (div.4) E
    建造水族馆题目描述你喜欢鱼,所以你决定建造一个水族馆。你有一块由n根柱子组成的珊瑚,其中i根柱子高ai个单位。之后,你将在珊瑚周围建造一个水族箱,具体如下:选择一个整数h--水箱的高度。在水箱两侧建造高度为h的墙壁。然后,在水箱中注满水,使每一列的高度都是h,珊瑚的......
  • 计算二叉树(二叉链表)的带权路径长度
    方法1#include<bits/stdc++.h>#defineintlonglong#definemod998244353usingnamespacestd;usingpii=pair<int,int>;typedefstructBtnode{ intdata; structBtnode*lc,*rc; }*Btree,Btnode;//笔试这个可以放上面,但是真的写程序应该把getwpl放在g......
  • 力扣 170. 两数之和 III - 数据结构设计 two-sum III
    数组系列力扣数据结构之数组-00-概览力扣.53最大子数组和maximum-subarray力扣.128最长连续序列longest-consecutive-sequence力扣.1两数之和N种解法two-sum力扣.167两数之和IItwo-sum-ii力扣.170两数之和IIItwo-sum-iii力扣.653两数之和IVtwo-sum-IV力......
  • Educational Codeforces Round 158 (Rated for Div. 2) - VP记录
    A.LineTrip油量必须支持车子通过所有加油站间的空间,还要注意开回来的时候终点不能加油。点击查看代码#include<cstdio>#include<algorithm>usingnamespacestd;constintN=55;intn,x,a[N];intmain(){ intT;scanf("%d",&T); while(T--) { scanf("%d%d",&am......