从一段简单算法题来谈二叉查找树(BST)的基础算法
先给出一道很简单,喜闻乐见的二叉树算法题:
给出一个二叉查找树和一个目标值,如果其中有两个元素的和等于目标值则返回真,否则返回假。
例如:
Input:
5
/ \
3 6
/ \ \
2 4 7 Target = 9 Output: TrueInput:
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)的基础算法的更多相关文章
- LeetCode算法题-Minimum Distance Between BST Nodes(Java实现-四种解法)
这是悦乐书的第314次更新,第335篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第183题(顺位题号是783).给定具有根节点值的二叉搜索树(BST),返回树中任何两个 ...
- php笔试算法题:顺时针打印矩阵坐标-蛇形算法
这几天参加面试,本来笔试比较简单,但是在面试的时候,技术面试官说让我现场写一个算法,顺时针打印矩阵的坐标,如图所示 顺序为,0,1,2,3,4,9,14,19,24,23,22,21,20,15,10 ...
- 笔试算法题(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 ...
- 前端如何应对笔试算法题?(用node编程)
用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...
- python每日经典算法题5(基础题)+1(中难题)
现在,越来越多的公司面试以及考验面试对算法要求都提高了一个层次,从现在,我讲每日抽出时间进行5+1算法题讲解,5是指基础题,1是指1道中等偏难.希望能够让大家熟练掌握python的语法结构已经一些高级 ...
- Java在算法题中的输入问题
Java在算法题中的输入问题 在写算法题的时候,经常因为数据的输入问题而导致卡壳,其中最常见的就是数据输入无法结束. 1.给定范围,确定输入几个数据 直接使用普通的Scanner输入数据范围,然后使用 ...
- 简单的算法题, Find Minimum in Rotated Sorted Array 的Python实现。
简单的算法题, Find Minimum in Rotated Sorted Array 的Python实现. 题目: Suppose a sorted array is rotated at som ...
- SDUT OJ 数据结构实验之串一:KMP简单应用 && 浅谈对看毛片算法的理解
数据结构实验之串一:KMP简单应用 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descr ...
- 简单的PHP算法题
简单的PHP算法题 目录 1.只根据n值打印n个0 2.根据n值打印一行 0101010101010101010101…… 3.根据n值实现1 00 111 0000 11111…… 4.根据n值实现 ...
随机推荐
- js获取滚动条的宽度
function getScrollWidth() { var noScroll, scroll, oDiv = document.createElement("DIV"); oD ...
- 简洁常用权限系统的设计与实现(六):不维护节点的深度level,手动计算level,构造树 (把一颗无序的树,变成有序的)
本篇介绍的方法,参考了网上的代码.在递归过程中,计算level,是受到了这种方法的启发. CSDN上有篇关于树的算法,目标是把一个无序的树,变成有序的. 我看了下代码,并运行了下,感觉是可行的. 我 ...
- 51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp
传送门 题目大意: 给一颗重新编号,叶子节点的值定义为他到根节点编号的最小值,求所有叶子节点值的乘积的最大值. 题目分析: 为什么我觉得这道题最难的是贪心啊..首先要想到 在一条链上,深度大的编号要小 ...
- Docker上定制CentOS7镜像
原文:Docker上定制CentOS7镜像 前言: 环境:centos7.5 64 位 正文: 第一步:下载centos7镜像 docker pull centos 第二步:建立centos7的容器 ...
- springboot 使用传统方式部署
spring boot默认创建出来的应用程序是内嵌web容器的,直接运行jar文件就可以的,但通常我们也需要将程序部署到tomcat中,这需要做如下改进: 1.pom.xml修改 打包方式需要修改成w ...
- 利用navicat写mysql的存储过程
最近项目经理让我给新的活动的预留一个插入红包和查看详情的sql,方便在项目出问题的做一些紧急操作,我想了下这里面还涉及到挺多逻辑和挺多表的一句句查也不方便啊,干脆写到存储过程里,于是开始在navica ...
- 从vue1迁移到vue2踩到的两个坑
先说第一个,在vue1中用v-for的时候,习惯性用$index和$key来取键.今天迁移到vue2之前,也知道vue2里不能这样用了,结果还是出问题了, 数据渲染不出来. <li v-for= ...
- Matlab Tricks(二十四)—— title 置于图像之下(包括 subplots 的情形)
1. 使用 title 的 'position' 属性进行设置 plot(1:10, 1:10), title('y=x', 'position', [5.5, 0]) 2. 使用 xlabel pl ...
- 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 ...
- VS解决方案文件格式说明
作者:朱金灿 来源:http://blog.csdn.net/clever101 VS解决方案文件本质是一个文件文件,这个用记事本或者Node++之类的文本编辑软件打开一个VS解决方案文件就知道了.了 ...