传智播客-二叉树(3)

--仅以此文纪念俺“逝去”的巴巴运动网学习笔记(泪。。)

 

说明:
1、二叉树只是笔试或面试的众多可能的考题之一,可以试听传智博客的网络直播课程--java面试与指导http://www.itcast.cn/itcast_static/javajz1.htm,不定期开播,一般由张孝祥老师主讲;还有毕老师的java基础,对于有志从事java但又没有基础或基础薄弱的人来说,这是个好入门,而最最重要的是--免费!!这样你就可以在不需要支付金钱成本的情况下确定自己是否有必要从事java了。
2、这篇文章对二叉树和递归讲得已经很细,我就不做多余的说明了,小菜鸟光瞻仰就好了。。。多句嘴,其实有的地方感觉罗唆了点。。
3、个人感觉这一部分是全文的重点,给出了由简入深学习二叉树的思路
4、原文地址:http://cslibrary.stanford.edu/110/BinaryTrees.html

 

Section 2 -- Binary Tree Problems

第二节、二叉树问题


Here are 14 binary tree problems in increasing order of difficulty. Some of the problems operate on binary search trees (aka "ordered binary trees") while others work on plain binary trees with no special ordering. The next section, Section 3, shows the solution code in C/C++. Section 4 gives the background and solution code in Java. The basic structure and recursion of the solution code is the same in both languages -- the differences are superficial.
这里有14个关于递增排序难点的问题。一些问题应用于二分查询树(“有序二叉树”)而其他的应用于没有特别排序的常规二叉树。下一节,第三节,展示C/C++代码解决方案。第四节给出了java背景和方案代码。基本结构和递归方案代码在两种语言里是相同的--差异是表面的。

 

Reading about a data structure is a fine introduction, but at some point the only way to learn is to actually try to solve some problems starting with a blank sheet of paper. To get the most out of these problems, you should at least attempt to solve them before looking at the solution. Even if your solution is not quite right, you will be building up the right skills. With any pointer-based code, it's a good idea to make memory drawings of a a few simple cases to see how the algorithm should work.
阅读一种数据结构是一个好的入门,但很多时候仅有的需要学习(它)的情况是始于实际尝试解决诸如一张空白表单。为了获得更多的这类问题之外的(知识),你应该至少在看这些方案之前尝试解决它们。哪怕你的方案不是那么正确,你也能加强(解决问题的)正确技巧。对于任何基于指针的代码,这是个在记忆中勾画出一些显示算法如何运作的简单案例的好办法。

 

1. build123()
This is a very basic problem with a little pointer manipulation. (You can skip this problem if you are already comfortable with pointers.) Write code that builds the following little 1-2-3 binary search tree...
这是一个带有一点点指针操作的很基础的问题。(如果你已经对指针很得心应手了可以跳过这个问题)写(一段)构建如下小型1-2-3二分查询树的代码。。。
    2
   / /
  1   3

Write the code in three different ways...
用三种不同的方式写这段代码。。。
a: by calling newNode() three times, and using three pointer variables
a:调用newNode()三次,然后使用三个指针变量
b: by calling newNode() three times, and using only one pointer variable
b:调用newNode()三次,然后只使用一个指针变量
c: by calling insert() three times passing it the root pointer to build up the tree
c:调用insert()三次,每次传给它根指针以构建这个树
(In Java, write a build123() method that operates on the receiver to change it to be the 1-2-3 tree with the given coding constraints. See Section 4.)
struct node* build123() {
 

2. size()
This problem demonstrates simple binary tree traversal. Given a binary tree, count the number of nodes in the tree.
int size(struct node* node) {
这个问题演示了简单二叉树的遍历。假定一个二叉树,计算这个树的节点数目。

 

3. maxDepth()
Given a binary tree, compute its "maxDepth" -- the number of nodes along the longest path from the root node down to the farthest leaf node. The maxDepth of the empty tree is 0, the maxDepth of the tree on the first page is 3.
int maxDepth(struct node* node) {
假定一个二叉树,计算它的“最大纵深”--从根节点开始到最远叶子节点的最长路径上的节点数目。空树的最大纵深是0,第一页那个树的最大纵深是3。

 

4. minValue()
Given a non-empty binary search tree (an ordered binary tree), return the minimum data value found in that tree. Note that it is not necessary to search the entire tree. A maxValue() function is structurally very similar to this function. This can be solved with recursion or with a simple while loop.
int minValue(struct node* node) {
假定一个非空二分查询树(一个有序二叉树),返回这个树能找到的最小数据。请注意这并不需要搜索整个树。maxValue()方法结构上很类似于这个方法。这可以通过递归或一个简单的while循环解决。

 

5. printTree()
Given a binary search tree (aka an "ordered binary tree"), iterate over the nodes to print them out in increasing order. So the tree...
假定一个非空二分查询树(一个有序二叉树),以递增的顺序迭代所有节点并打印它们。
       4
      / /
     2   5
    / /
   1   3

Produces the output "1 2 3 4 5". This is known as an "inorder" traversal of the tree.
产生输出“1 2 3 4 5”。这被称为树的“有序”遍历
Hint: For each node, the strategy is: recur left, print the node data, recur right.
提示:对每一个节点,策略是:重复左边,打印节点数据,重复右边。
void printTree(struct node* node) {
 

6. printPostorder()
Given a binary tree, print out the nodes of the tree according to a bottom-up "postorder" traversal -- both subtrees of a node are printed out completely before the node itself is printed, and each left subtree is printed before the right subtree. So the tree...
假定一个二叉树,依据自下而上“位置排序”遍历打印树的节点--每一个节点的子树都先于节点自身被打印之前打印,而且每一个左子树都先于右子树被打印。
       4
      / /
     2   5
    / /
   1   3

Produces the output "1 3 2 5 4". The description is complex, but the code is simple. This is the sort of  bottom-up traversal that would be used, for example, to evaluate an expression tree where a node is an operation like '+' and its subtrees are, recursively, the two subexpressions for the '+'.
产生输出“1 3 2 5 4”。描述有些复杂,但代码简单。这是将要使用的自下而上遍历的一种,例如,求一个表达式目录树(欲知详情,请google百度)的值,一个节点是像“+”这样的操作符,然后它的子树递归地,是“+”的两个子表达式。(最后一句。。。?)
void printPostorder(struct node* node) {
 

7. hasPathSum()
We'll define a "root-to-leaf path" to be a sequence of nodes in a tree starting with the root node and proceeding downward to a leaf (a node with no children). We'll say that an empty tree contains no root-to-leaf paths. So for example, the following tree has exactly four root-to-leaf paths:
我们将定义一个“根至叶子的路径”,是一个树的一序列节点,始于根节点然后向下进行直至叶子(没有子节点的节点)。我们将认为空树不包含根至叶子的路径。例如,下面的树有4条确切的根至叶子路径:
              5
             / /
            4   8
           /   / /
          11  13  4
         /  /      /
        7    2      1

Root-to-leaf paths:
   path 1: 5 4 11 7
   path 2: 5 4 11 2
   path 3: 5 8 13
   path 4: 5 8 4 1

For this problem, we will be concerned with the sum of the values of such a path -- for example, the sum of the values on the 5-4-11-7 path is 5 + 4 + 11 + 7 = 27.
对于这个问题,我们将关注每一条路径的值的总和--例如,5-4-11-7路径的和是5 + 4 + 11 + 7 = 27。


Given a binary tree and a sum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. Return false if no such path can be found. (Thanks to Owen Astrachan for suggesting this problem.)
假定一个二叉树和一个和值,如果这个树有一条root-to-leaf路径值的总和等于给定的和值就返回true。如果没有找到这样的路径就返回false。(谢谢Owen Astrachan提出这个问题)
int hasPathSum(struct node* node, int sum) {
 

8. printPaths()
Given a binary tree, print out all of its root-to-leaf paths as defined above. This problem is a little harder than it looks, since the "path so far" needs to be communicated between the recursive calls. Hint: In C, C++, and Java, probably the best solution is to create a recursive helper function printPathsRecur(node, int path[], int pathLen), where the path array communicates the sequence of nodes that led up to the current call. Alternately, the problem may be solved bottom-up, with each node returning its list of paths. This strategy works quite nicely in Lisp, since it can exploit the built in list and mapping primitives. (Thanks to Matthias Felleisen for suggesting this problem.)
假定一个二叉树,打印出所有如上所定义的root-to-leaf路径。这个问题看上去有一点难度,因为“路径如此遥远”需要在递归回调之间通信。提示:在C,C++和java语言中,也许最好的解决方案是创建一个递归辅助方法printPathsRecur(node, int path[], int pathLen),通过该方法令路径数组与指向当前调用的节点序列通信。可选地,这个问题也许可以通过自下而上(方案)解决,不需要每一个节点返回它的路径列表。这个策略在Lisp运用很优雅,因为它能够扩展列表和映射基本数据的结构。(谢谢Matthias Felleisen提出这个问题)


Given a binary tree, print out all of its root-to-leaf paths, one per line.
假定一个二叉树,打印出所有的root-to-leaf路径,每个一行。
void printPaths(struct node* node) {
 

9. mirror()
Change a tree so that the roles of the left and right pointers are swapped at every node.
改变一个树,令它每一个节点左右指针的角色互换。
 So the tree...
       4
      / /
     2   5
    / /
   1   3

 is changed to...
       4
      / /
     5   2
        / /
       3   1

The solution is short, but very recursive. As it happens, this can be accomplished without changing the root node pointer, so the return-the-new-root construct is not necessary. Alternately, if you do not want to change the tree nodes, you may construct and return a new mirror tree based on the original tree.
解决方案很简短,但递归频繁。当递归运行时,这个能够不改变根节点指针完成(运算),因此“返回新的根”构建就不需要了。可选地,如果你不想改变树节点,你可以构建并返回一个基于原始树的镜象树。
void mirror(struct node* node) {
 

10. doubleTree()
For each node in a binary search tree, create a new duplicate node, and insert the duplicate as the left child of the original node. The resulting tree should still be a binary search tree.
对二分查询树的每个节点,创建一个新的复制节点,然后插入这个复制节点作为原有节点的左边子(节点或叶子)。作为结果的树仍然是二分查询树。
 So the tree...
    2
   / /
  1   3

 is changed to...
       2
      / /
     2   3
    /   /
   1   3
  /
 1

As with the previous problem, this can be accomplished without changing the root node pointer.
像之前的问题一样,这个能够不改变根节点指针完成(运算)。
void doubleTree(struct node* node) {
 

11. sameTree()
Given two binary trees, return true if they are structurally identical -- they are made of nodes with the same values arranged in the same way. (Thanks to Julie Zelenski for suggesting this problem.)
int sameTree(struct node* a, struct node* b) {
假定两个二叉树,如果他们结构一致返回true--他们的节点有相同的值且以同样的方式排列。(谢谢Julie Zelenski提出这个问题)

 

12. countTrees()
This is not a binary tree programming problem in the ordinary sense -- it's more of a math/combinatorics recursion problem that happens to use binary trees. (Thanks to Jerry Cain for suggesting this problem.)
就常规理解这不是一个二叉树程序问题--它更多是需要运用二叉树的数学/组合数学递归问题。(谢谢Jerry Cain提出这个问题)
 
Suppose you are building an N node binary search tree with the values 1..N. How many structurally different  binary search trees are there that store those values? Write a recursive function that, given the number of distinct values, computes the number of structurally unique binary search trees that store those values. For example, countTrees(4) should return 14, since there are 14  structurally unique binary search trees that store 1, 2, 3, and 4. The base case is easy, and the recursion is short but dense. Your code should not construct any actual trees; it's just a counting problem.
设想你正在用值1..N构建一个N节点二分查询树。在结构上有多少个不同的二分查询树要存储这些值?写一个递归的方法,假定不同值的数目,计算结构上唯一的存储这些值的二分查询树的数目。例如,countTrees(4)应该返回14,因为有14个结构上唯一的存储1,2,3和4的二分查询树。基础案例容易,递归简单但密集。你的代码不应该构建任何实际的树;这只是一个计算问题。
int countTrees(int numKeys) {

 

Binary Search Tree Checking (for problems 13 and 14)
This background is used by the next two problems: Given a plain binary tree, examine the tree to determine if it meets the requirement to be a binary search tree. To be a binary search tree, for every node, all of the nodes in its left tree must be <= the node, and all of the nodes in its right subtree must be > the node. Consider the following four examples...
这个(问题的)背景是要用到接下来的两个问题:假定一个常规二叉树,检查这个树以确定它是否符合一个二分查询树的要求。对于一个二分查询树,其每一个节点,所有左边子树的节点必须<=该节点,而所有右边子树的节点必须>该节点。请思考下面的四个问题。。。
a.  5   -> TRUE
   / /
  2   7
 

b.  5   -> FALSE, because the 6 is not ok to the left of the 5
   / /
  6   7
 

c.   5  -> TRUE
    / /
   2   7
  /
 1

d.   5  -> FALSE, the 6 is ok with the 2, but the 6 is not ok with the 5
    / /
   2   7
  / /
 1   6

For the first two cases, the right answer can be seen just by comparing each node to the two nodes immediately below it. However, the fourth case shows how checking the BST quality may depend on nodes which are several layers apart -- the 5 and the 6 in that case.
开始的两个案例,正确答案是能够仅通过比较每个节点和它下面的两个节点就能识别。然而,第四个案例展示如何检查BST品质也许依赖于隔了好几层的节点,这个例子是5和6。

 

13 isBST() -- version 1
Suppose you have helper functions minValue() and maxValue() that return the min or max int value from a non-empty tree (see problem 3 above). Write an isBST() function that returns true if a tree is a binary search tree and false otherwise. Use the helper functions, and don't forget to check every node in the tree. It's ok if your solution is not very efficient. (Thanks to Owen Astrachan for the idea of having this problem, and comparing it to problem 14) 
设想你有辅助方法minValue() 和 maxValue(),从一个非空树返回其最小最大值(参见上文的问题3)。写一个isBST()方法,如果一个树是二分查询树返回true否则返回false。应用这(两个)辅助方法,然后不要忘了检查这个树的每一个节点。如果你的方案不是很有效率也行。(谢谢Owen Astrachan想到这个问题,请与问题14相比较)


Returns true if a binary tree is a binary search tree.
如果一个二叉树是二分查询树返回true
int isBST(struct node* node) {
 

14. isBST() -- version 2
Version 1 above runs slowly since it traverses over some parts of the tree many times. A better solution looks at each node only once. The trick is to write a utility helper function isBSTRecur(struct node* node, int min, int max) that traverses down the tree keeping track of the narrowing min and max allowed values as it goes, looking at each node only once. The initial values for min and max should be INT_MIN and INT_MAX -- they narrow from there.
上面的版本1运行低效因为它遍历了树的一些部分多次。一个更好的解决方案是每个节点只查看一次。这个技巧是写一个工具辅助方法isBSTRecur(struct node* node, int min, int max),向下遍历此树,并且追踪它经过的允许的最小最大值缩口,每个节点只查看一次。最小最大值的初始值应该是INT_MIN和INT_MAX--它们从这里收缩。
/*
 Returns true if the given tree is a binary search tree
 (efficient version).
*/
int isBST2(struct node* node) {
  return(isBSTRecur(node, INT_MIN, INT_MAX));
}

/*
 Returns true if the given tree is a BST and its
 values are >= min and <= max.
*/
int isBSTRecur(struct node* node, int min, int max) {
 

15. Tree-List
The Tree-List problem is one of the greatest recursive pointer problems ever devised, and it happens to use binary trees as well. CLibarary #109 http://cslibrary.stanford.edu/109/  works through the Tree-List problem in detail and includes solution code in C and Java. The problem requires an understanding of binary trees, linked lists, recursion, and pointers. It's a great problem, but it's complex.
树-链表问题是自(递归指针)发明以来最伟大的递归指针问题之一,它也要使用二叉树。CLibarary #109 http://cslibrary.stanford.edu/109/完成了树-链表问题的细节而且包括C和java方案代码。这个问题需要理解二叉树,链表,递归和指针。这是一个伟大的问题,但是很复杂。

 

第三第四节是解决方案的具体代码,可以直接参见原文http://cslibrary.stanford.edu/110/BinaryTrees.html
作者在第四节中说明,java的实现思想和C/C++是一样的,只是外在结构略有不同;客户端可以操作的方法可视为面向对象的,但是内部调用的实际操作方法还是面向过程的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值