先给出一道很简单,喜闻乐见的二叉树算法题:

给出一个二叉查找树和一个目标值,如果其中有两个元素的和等于目标值则返回真,否则返回假。

例如:

Input:
5
/ \
3 6
/ \ \
2 4 7 Target = 9 Output: True
Input:
5
/ \
3 6
/ \ \
2 4 7 Target = 28 Output: False

 

 什么是二叉树?
  
二叉树是每个节点最多有两个子树的树结构。

 什么是二叉查找树(binary search tree
  
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

  (1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;

  (2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

  (3)左、右子树也分别为二叉排序树;
二叉树的遍历
1.先序遍历(pre-order)
  
首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。 2.后序遍历( post-order )
  后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。 3.中序遍历(in-order)
  中序遍历首先遍历左子树,然后访问根节点,然后遍历右子树。
举个例子:

先序遍历节点遍历顺序: A B D H I K E C F G J L
后序遍历节点遍历顺序: H K I D E B F L J G C A
中序遍历节点遍历顺序: H D I K B E A F C J L G
算法题解法
1.使用HashSet

  最简单的解法就是遍历整个树,然后得到所有的节点对来判断他们是否能相加得到目标值k。但是我们稍微考虑一下缓存,这个方案还能提高一些。
  如果两个元素的和等于k,即x+y=k,并且我们已经知道x是已经存在于树的,我们只要检索y是否也在树的节点里即可,即y=k-x。基于这个简单的缓存,我们可以在每一步去遍历这棵树的两个方向(左子树和右子树)。我们使用一个集合来存遍历过程中被遍历过的元素。
  对于每一个当前的节点,有值p,我们去检查k-p是否存在于集合中。如果存在的话,我们可以得到存在2个元素的值等于k的结论。否则,我们继续把这个值存进集合。
  如果遍历了整棵树后,没有这样的p值,那就证明不存在2个元素的值相加等于k。
  代码如下:

public class Solution {
public boolean findTarget(TreeNode root, int k) {
Set<Integer> set = new HashSet();
return find(root, k, set);
}
public boolean find(TreeNode root, int k, Set < Integer > set) {
if (root == null)
return false;
if (set.contains(k - root.val))
return true;
set.add(root.val);
return find(root.left, k, set) || find(root.right, k, set);
}
}
//时间复杂度为 O(n),空间复杂度也为O(n).
2.使用BFS和HashSet.

  在这个解法中,使用HashSet的想法和解法1是一样的。但是我们在遍历的时候使用广度优先算法(Breadth First Search),这是一个在树的遍历算法中很常用的算法。BFS的方法如如下总结的一样。我们在开始的时候把根节点放进一个队列。
我们还是会用到如上解法的一个集合。然后在每一步我们会这么做:
  1.从队列queue的头部去除一个元素p。
  2.判断k-p是否存在于集合中。如果是,返回true。
  3.否则,把这个元素p加到集合中。然后,把这个节点的左右子节点加到队列queue的末尾。
  4.继续1-3步骤,直到队列为空。
  5.如果队列为空,则返回false。
照这个步骤,我们一层一层的循环了整个树。
代码如下:
public 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()) {
if (queue.peek() != null) {
TreeNode node = queue.remove();
if (set.contains(k - node.val))
return true;
set.add(node.val);
queue.add(node.right);
queue.add(node.left);
} else
queue.remove();
}
return false;
}
}
//时间复杂度为 O(n),空间复杂度也为O(n).

3.使用BST

在这个解法中,我们要充分利用这个树是一个二叉搜索树。现在,我们知道一个树的中序遍历会得到一个递增的节点集。如此一来,我们可以对这棵树进行中序排序,然后把结果存进一个数组,这个数组的元素按照升序的顺序排列。
上面的步骤做好以后,我们使用l和r两个指针指向数组的开头和结尾。然后,我们这么做:
  1.判断l和r指向的节点的值的和是否等于k。如果是,立马返回true。
  2.如果他们的和小于k,l移向下一个元素。我们需要得到更大的和,那我们只有提高我们更小的那个值。
  3.如果他们的和大于k,r移向上一个元素。我们需要得到更小的和,那我们只有减少我们更大的那个值。
  4.循环执行1-3步,直到l和r指针相遇。
  5.如果l和r指针相遇,则返回false.
 
public class Solution {
public boolean findTarget(TreeNode root, int k) {
List<Integer> list = new ArrayList();
inorder(root, list);
int l = 0, r = list.size() - 1;
while (l < r) {
int sum = list.get(l) + list.get(r);
if (sum == k)
return true;
if (sum < k)
l++;
else
r--;
}
return false;
}
public void inorder(TreeNode root, List < Integer > list) {
if (root == null)
return;
inorder(root.left, list);
list.add(root.val);
inorder(root.right, list);
}
}
//时间复杂度为 O(n),空间复杂度也为O(n).

至于广度优先算法和深度优先算法,以后有机会再展开吧。

 


												

从一段简单算法题来谈二叉查找树(BST)的基础算法的更多相关文章

  1. LeetCode算法题-Minimum Distance Between BST Nodes(Java实现-四种解法)

    这是悦乐书的第314次更新,第335篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第183题(顺位题号是783).给定具有根节点值的二叉搜索树(BST),返回树中任何两个 ...

  2. php笔试算法题:顺时针打印矩阵坐标-蛇形算法

    这几天参加面试,本来笔试比较简单,但是在面试的时候,技术面试官说让我现场写一个算法,顺时针打印矩阵的坐标,如图所示 顺序为,0,1,2,3,4,9,14,19,24,23,22,21,20,15,10 ...

  3. 笔试算法题(52):简介 - KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm)

    议题:KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm) 分析: KMP算法用于在一个主串中找出特定的字符或者模式串.现在假设主串为长度n的数组T ...

  4. 前端如何应对笔试算法题?(用node编程)

    用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...

  5. python每日经典算法题5(基础题)+1(中难题)

    现在,越来越多的公司面试以及考验面试对算法要求都提高了一个层次,从现在,我讲每日抽出时间进行5+1算法题讲解,5是指基础题,1是指1道中等偏难.希望能够让大家熟练掌握python的语法结构已经一些高级 ...

  6. Java在算法题中的输入问题

    Java在算法题中的输入问题 在写算法题的时候,经常因为数据的输入问题而导致卡壳,其中最常见的就是数据输入无法结束. 1.给定范围,确定输入几个数据 直接使用普通的Scanner输入数据范围,然后使用 ...

  7. 简单的算法题, Find Minimum in Rotated Sorted Array 的Python实现。

    简单的算法题, Find Minimum in Rotated Sorted Array 的Python实现. 题目: Suppose a sorted array is rotated at som ...

  8. SDUT OJ 数据结构实验之串一:KMP简单应用 && 浅谈对看毛片算法的理解

    数据结构实验之串一:KMP简单应用 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descr ...

  9. 简单的PHP算法题

    简单的PHP算法题 目录 1.只根据n值打印n个0 2.根据n值打印一行 0101010101010101010101…… 3.根据n值实现1 00 111 0000 11111…… 4.根据n值实现 ...

随机推荐

  1. js获取滚动条的宽度

    function getScrollWidth() { var noScroll, scroll, oDiv = document.createElement("DIV"); oD ...

  2. 简洁常用权限系统的设计与实现(六):不维护节点的深度level,手动计算level,构造树 (把一颗无序的树,变成有序的)

     本篇介绍的方法,参考了网上的代码.在递归过程中,计算level,是受到了这种方法的启发. CSDN上有篇关于树的算法,目标是把一个无序的树,变成有序的. 我看了下代码,并运行了下,感觉是可行的. 我 ...

  3. 51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp

    传送门 题目大意: 给一颗重新编号,叶子节点的值定义为他到根节点编号的最小值,求所有叶子节点值的乘积的最大值. 题目分析: 为什么我觉得这道题最难的是贪心啊..首先要想到 在一条链上,深度大的编号要小 ...

  4. Docker上定制CentOS7镜像

    原文:Docker上定制CentOS7镜像 前言: 环境:centos7.5 64 位 正文: 第一步:下载centos7镜像 docker pull centos 第二步:建立centos7的容器 ...

  5. springboot 使用传统方式部署

    spring boot默认创建出来的应用程序是内嵌web容器的,直接运行jar文件就可以的,但通常我们也需要将程序部署到tomcat中,这需要做如下改进: 1.pom.xml修改 打包方式需要修改成w ...

  6. 利用navicat写mysql的存储过程

    最近项目经理让我给新的活动的预留一个插入红包和查看详情的sql,方便在项目出问题的做一些紧急操作,我想了下这里面还涉及到挺多逻辑和挺多表的一句句查也不方便啊,干脆写到存储过程里,于是开始在navica ...

  7. 从vue1迁移到vue2踩到的两个坑

    先说第一个,在vue1中用v-for的时候,习惯性用$index和$key来取键.今天迁移到vue2之前,也知道vue2里不能这样用了,结果还是出问题了, 数据渲染不出来. <li v-for= ...

  8. Matlab Tricks(二十四)—— title 置于图像之下(包括 subplots 的情形)

    1. 使用 title 的 'position' 属性进行设置 plot(1:10, 1:10), title('y=x', 'position', [5.5, 0]) 2. 使用 xlabel pl ...

  9. Method for finding shortest path to destination in traffic network using Dijkstra algorithm or Floyd-warshall algorithm

    A method is presented for finding a shortest path from a starting place to a destination place in a ...

  10. VS解决方案文件格式说明

    作者:朱金灿 来源:http://blog.csdn.net/clever101 VS解决方案文件本质是一个文件文件,这个用记事本或者Node++之类的文本编辑软件打开一个VS解决方案文件就知道了.了 ...