(简单)剑指Offer II 056. 二叉搜索树中两个节点的和 Java

在这里插入图片描述

方法一:深度优先搜索+哈希表

使用深度优先搜索的方式遍历整棵树,用哈希表记录遍历过的节点的值

对于一个值为x的节点,检查哈希表中是否存在k-x即可。如果存在对应的元素,那么我们就可以在该树上找到两个节点的和为k;否则,将x放入到哈希表中

如果遍历完整棵树都不存在对应的元素,那么该树上不存在两个和为k的节点

import java.util.HashSet;
import java.util.Set;

class Solution {

    Set<Integer> set = new HashSet<>();

    public boolean findTarget(TreeNode root, int k) {
        if (root == null) {
            return false;
        }
        if (set.contains(k - root.val)) {
            return true;
        }
        set.add(root.val);
        return findTarget(root.left, k) || findTarget(root.right, k);
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n为二叉树的大小。需要遍历整棵树一次。
  • 空间复杂度:O(n),其中n为二叉树的大小。主要为哈希表的开销,最坏情况下需要将每个节点加入哈希表一次。
    在这里插入图片描述
    方法二:广度优先搜索 + 哈希表

使用广度优先搜索的方式遍历整棵树,用哈希表记录遍历过的节点值

首先,创建一个哈希表和一个队列,将根节点加入队列中,然后执行以下步骤:(层序遍历)

  1. 从队列中取出队头,假设其值为x
  2. 检查哈希表中是否存在k-x,如果存在返回true
  3. 否则,将该节点的值加入哈希表,将该节点的左右非空节点加入队尾
  4. 重复以上步骤,直到队列为空
  5. 如果队列为空,说明树上不存在两个和为k的节点,返回false
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        Set<Integer> set = new HashSet<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if (set.contains(k - node.val)) {
                return true;
            }
            set.add(node.val);
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return false;
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n为二叉搜索树的大小。需要遍历整棵树一次。
  • 空间复杂度:O(n),其中n为二叉搜索树的大小。主要为哈希表和队列的开销,最坏情况下需要将每个节点加入哈希表和队列各一次。

在这里插入图片描述

方法三:深度优先搜索+中序遍历+双指针

二叉搜索树的中序遍历是升序排列的,可以将该二叉搜索树的中序遍历的结果记录下来,得到一个升序数组。

具体地,使用两个指针分别指向数组的头尾,当两个指针指向的元素之和小于k时,让左指针右移;当两个指针指向的元素之和大于k时,让右指针左移;当两个指针指向的元素之和等于k时,返回true

最终,当左指针和右指针重合时,树上不存在两个和为k的节点,返回false

import java.util.ArrayList;
import java.util.List;

class Solution {

    List<Integer> list = new ArrayList<>();

    public boolean findTarget(TreeNode root, int k) {
        inorder(root);
        int n = list.size();
        int left = 0;
        int right = n - 1;
        while (left < right && right < n) {
            int val = list.get(left) + list.get(right);
            if (val < k) {
                left++;
            } else if (val > k) {
                right--;
            } else {
                return true;
            }
        }
        return false;
    }

    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        list.add(root.val);
        inorder(root.right);
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n为二叉搜索树的大小。我们需要遍历这棵树一次,并对得到的升序数组使用双指针遍历。
  • 空间复杂度:O(n),其中n为二叉搜索树的大小。主要为升序数组的开销。

在这里插入图片描述
方法四:迭代 + 中序遍历 + 双指针

在方法三中,是在中序遍历得到的数组上进行双指针,这样需要消耗O(n)的空间,实际上可以将双指针的移动理解为在树上的遍历过程的一次移动。因为递归方法较难控制移动过程,因此可以使用迭代的方式进行遍历。

具体地,对于每一个指针新建一个栈。初始,让左指针移动到树的最左端点,并将路径保存在栈中,接下来依据栈来O(1)地计算出左指针的下一个位置。右指针也是同理。

计算下一个位置时,首先将位于栈顶的当前节点从栈中弹出,此时,首先判断当前节点是否存在右子节点,如果存在,那么将右子节点的最左子树加入到栈中;否则就完成了当前层的遍历,无需进一步修改栈的内容,直接回溯到上一层即可。

import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        TreeNode left = root;
        TreeNode right = root;
        Stack<TreeNode> leftStack = new Stack<>();
        Stack<TreeNode> rightStack = new Stack<>();

        leftStack.push(left);
        while (left.left != null) {
            leftStack.push(left.left);
            left = left.left;
        }

        rightStack.push(right);
        while (right.right != null) {
            rightStack.push(right.right);
            right = right.right;
        }

        while (left != right) {
            int val = left.val + right.val;
            if (val > k) {
                right = getRight(rightStack);
            } else if (val < k) {
                left = getLeft(leftStack);
            } else {
                return true;
            }
        }
        return false;
    }

    private TreeNode getLeft(Stack<TreeNode> leftStack) {
        TreeNode root = leftStack.pop();
        TreeNode node = root.right;
        while (node != null) {
            leftStack.push(node);
            node = node.left;
        }
        return root;
    }

    private TreeNode getRight(Stack<TreeNode> rightStack) {
        TreeNode root = rightStack.pop();
        TreeNode node = root.left;
        while (node != null) {
            rightStack.push(node);
            node = node.right;
        }
        return root;
    }

}

复杂度分析:

  • 时间复杂度:O(n),其中n为二叉搜索树的大小。在双指针的过程中,实际上遍历了整棵树一次。
  • 空间复杂度:O(n),其中n为二叉搜索树的大小。主要为栈的开销,最坏情况下二叉搜索树为一条链,需要O(n)的栈空间。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值