传智播客-二叉树(2)

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

 

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

 

Section 1 -- Introduction To Binary Trees
第一节、二叉树入门

 

A binary tree is made of nodes, where each node contains a "left" pointer, a "right" pointer, and a data element. The "root" pointer points to the topmost node in the tree. The left and right pointers recursively point to smaller "subtrees" on either side. A null pointer represents a binary tree with no elements -- the empty tree. The formal recursive definition is: a binary tree is either empty (represented by a null pointer), or is made of a single node, where the left and right pointers (recursive definition ahead) each point to a binary tree.
一个二叉树是由多个节点组成,每个节点又包含一个“左”指针,一个“右”指针,和一个数据元素。“根”指针指向树的最高节点。左右指针递归地指向(两边中的)任意一边较小的“子树”。一个空指针表示一个没有元素的二叉树--空树。规范的递归定义是:一个二叉树要么为空(声明为一个空指针),或只有一个单一的节点,该节点上的左指针和右指针(前面的递归定义)又分别指向一个二叉树。

 

A "binary search tree" (BST) or "ordered binary tree" is a type of binary tree where the nodes are arranged in order: for each node, all elements in its left subtree are less-or-equal to the node (<=), and all the elements in its right subtree are greater than the node (>). The tree shown above is a binary search tree -- the "root" node is a 5, and its left subtree nodes (1, 3, 4) are <= 5, and its right subtree nodes (6, 9) are > 5. Recursively, each of the subtrees must also obey the binary search tree constraint: in the (1, 3, 4) subtree, the 3 is the root, the 1 <= 3 and 4 > 3. Watch out for the exact wording in the problems -- a "binary search tree" is different from a "binary tree".
一个“二分查询树”(BST)或“排序二叉树”是二叉树的一种类型,(该类型二叉树的)节点依序排列:对每一个节点而言,
所有左边子树的元素都小于或等于该节点,而所有右边子树的元素都大于该节点。上面的图示显示的树即是一个二分查询树--“根”节点是5,它左边子树的节点(1,3,4)都小于等于5,而它右边子树的节点(6,9)都大于5。(以此)递推,每一个子树都必须遵守二分查询树约束:(1,3,4)子树,3是根,1<=3而4>3。注意(上述)问题的精确措词--一个“二分查询树”不同于一个“二叉树”。

 

二分查询树示意图

 

The nodes at the bottom edge of the tree have empty subtrees and are called "leaf" nodes (1, 4, 6) while the others are "internal" nodes (3, 5, 9).
树底部的节点拥有空子树被称为“叶子”节点(1,4,6)而其他的是“内”节点(3,5,9)。

 

Binary Search Tree Niche
二分查询树空间


Basically, binary search trees are fast at insert and lookup. The next section presents the code for these two algorithms. On average, a binary search tree algorithm can locate a node in an N node tree in order lg(N) time (log base 2). Therefore, binary search trees are good for "dictionary" problems where the code inserts and looks up information indexed by some key. The lg(N) behavior is the average case -- it's possible for a particular tree to be much slower depending on its shape.
基本上,二分查询树插入和查找很快。下一节就会展示实现这两个算法的代码。平均地,一个二分查询树算法能在lg(N)次(log底数是2)内在一个有N个节点的树中定位一个节点。因此,当代码(处理)插入和查找以某些关键(字)建立索引的信息的“字典”问题时,二分查询树很有效。lg(N)(次)行为是平均情况--对于某些特定的(算法)树由于它的形状(插入和查找的速度)可能很慢。

 

Strategy
策略


Some of the problems in this article use plain binary trees, and some use binary search trees. In any case, the problems concentrate on the combination of pointers and recursion. (See the articles linked above for pointer articles that do not emphasize recursion.)
本文中的问题一些用常规二叉树,一些用二分查询树。任何情况下,问题的焦点都在指针和递归的结合。(可以参见上述关于指针的链接文章,该文没有强调递归)

 

For each problem, there are two things to understand...
对于每一个问题,要明白两件事。。。
    1、The node/pointer structure that makes up the tree and the code that manipulates it 
    1、组成树的节点/指针结构和操作节点/指针的代码

    2、The algorithm, typically recursive, that iterates over the tree
    2、算法,代表性递归,迭代遍历树

 

When thinking about a binary tree problem, it's often a good idea to draw a few little trees to think about the various cases.
当考虑一个二叉树问题时,通常有个好方法,就是画一个小型树以此来考虑多种情况。

 

Typical Binary Tree Code in C/C++
典型的C/C++二叉树代码
As an introduction, we'll look at the code for the two most basic binary search tree operations -- lookup() and insert(). The code here works for C or C++. Java programers can read the discussion here, and then look at the Java versions in Section 4.
作为一个入门,我们先看看这两个最基础的二分查询树操作代码--lookup() 和 insert()。这里的代码是C或C++写的。java程序员可以阅读这部分的讨论,然后看第四节的java版本。

 

In C or C++, the binary tree is built with a node type like this...
C或C++中,二叉树的节点类型像这样。。。

struct node {
    int data;
    struct node* left;
    struct node* right;
}

 

Lookup()
Given a binary search tree and a "target" value, search the tree to see if it contains the target. The basic pattern of the lookup() code occurs in many recursive tree algorithms: deal with the base case where the tree is empty, deal with the current node, and then use recursion to deal with the subtrees. If the tree is a binary search tree, there is often some sort of less-than test on the node to decide if the recursion should go left or right.
假设一个二分查询树及一个“目标”值,(然后)检索该树查看是否含有目标(值)。lookup()代码的基本模式在很多递归树算法里都有出现:处理树为空这样的基本情况,处理当前节点,然后使用递归处理子树。如果这个树是二分查询树,通常多少有一些对节点的局部测试以判定递归是否能左或右(运行)。
/*
 Given a binary tree, return true if a node
 with the target data is found in the tree. Recurs
 down the tree, chooses the left or right
 branch by comparing the target to each node.
*/
static int lookup(struct node* node, int target) {
  // 1. Base case == empty tree
  // in that case, the target is not found so return false
  if (node == NULL) {
    return(false);
  }
  else {
    // 2. see if found here
    if (target == node->data) return(true);
    else {
      // 3. otherwise recur down the correct subtree
      if (target < node->data) return(lookup(node->left, target));
      else return(lookup(node->right, target));
    }
  }
}

The lookup() algorithm could be written as a while-loop that iterates down the tree. Our version uses recursion to help prepare you for the problems below that require recursion.
lookup()算法(也)可以采用while循环向下迭代该树的写法。我们的版本使用了递归以便帮助你提前准备下面要求递归的问题。

 

Pointer Changing Code
指针变换节点
There is a common problem with pointer intensive code: what if a function needs to change one of the pointer parameters passed to it? For example, the insert() function below may want to change the root pointer. In C and C++, one solution uses pointers-to-pointers (aka "reference parameters"). That's a fine technique, but here we will use the simpler technique that a function that wishes to change a pointer passed to it will return the new value of the pointer to the caller. The caller is responsible for using the new value. Suppose we have a change() function that may change the the root, then a call to change() will look like this...
有一个常见的关于指针密集的节点问题:如果一个方法需要改变传递给该方法的指针参数中的一个参数该怎么办呢?例如,下面的insert()方法也许希望改变这个根指针。C和C++中,一个解决方案是使用指针的指针(“引用参数”)。这是一个好技术,但这里我们将使用更简单的技术,一个方法希望改变传递给该方法的指针将返回调用者指针的新值。调用者有义务使用这个新值。设想我们有一个change()方法也许要改变根(节点),然后调用change() ,看上去就像这样。。。
// suppose the variable "root" points to the tree
root = change(root);

 

We take the value returned by change(), and use it as the new value for root. This construct is a little awkward, but it avoids using reference parameters which confuse some C and C++ programmers, and Java does not have reference parameters at all. This allows us to focus on the recursion instead of the pointer mechanics. (For lots of problems that use reference parameters, see CSLibrary #105, Linked List Problems, http://cslibrary.stanford.edu/105/).
我们取得change() 返回的值,然后使用它作为根的新值。这个结构有一点笨拙,但它避免了使用会另某些C和C++开发者困惑的引用参数,而java根本没有引用参数(插一句:曾经看过一篇文章,说java传递参数时大部分都是“引用”参数--注意“引用”是加了双引号的,因为java是一门面向对象的语言,而它的对象参数就类似C或C++的引用)。这就使我们得以关注递归而不是指针技术。(关于更多使用引用参数的问题,请参见CS图书库#105号文章,《链表问题》, http://cslibrary.stanford.edu/105/

 

Insert()
Insert() -- given a binary search tree and a number, insert a new node with the given number into the tree in the correct place. The insert() code is similar to lookup(), but with the complication that it modifies the tree structure. As described above, insert() returns the new tree pointer to use to its caller. Calling insert() with the number 5 on this tree...
Insert()--假设一个二分查询树和一个数值,用这个假定的值插入到该树正确的位置。Insert()代码类似于lookup(),但是结合了树结构的变更。如上所述,insert() 返回了要应用的新树指针给它的调用者。下面的树调用了带有数值5的insert() 。。。
    2
   / /
  1   10

returns the tree...
返回(下面的)树。。。
    2
   / /
  1   10
     /
    5

The solution shown here introduces a newNode() helper function that builds a single node. The base-case/recursion structure is similar to the structure in lookup() -- each call checks for the NULL case, looks at the node at hand, and then recurs down the left or right subtree if needed.
下文显示的方案介绍了一个newNode()辅助方法用以构建一个单独的节点。基本情况/递归结构类似于lookup()的结构--每一个调用都要检查Null情况,考虑将要获取的节点,然后如有必要重复向下(调用)左右子树。
/*
 Helper function that allocates a new node
 with the given data and NULL left and right
 pointers.
*/
struct node* NewNode(int data) {
  struct node* node = new(struct node);    // "new" is like "malloc"
  node->data = data;
  node->left = NULL;
  node->right = NULL;

  return(node);
}
 
/*
 Give a binary search tree and a number, inserts a new node
 with the given number in the correct place in the tree.
 Returns the new root pointer which the caller should
 then use (the standard trick to avoid using reference
 parameters).
*/
struct node* insert(struct node* node, int data) {
  // 1. If the tree is empty, return a new, single node
  if (node == NULL) {
    return(newNode(data));
  }
  else {
    // 2. Otherwise, recur down the tree
    if (data <= node->data) node->left = insert(node->left, data);
    else node->right = insert(node->right, data);

    return(node); // return the (unchanged) node pointer
  }
}
 
The shape of a binary tree depends very much on the order that the nodes are inserted. In particular, if the nodes are inserted in increasing order (1, 2, 3, 4), the tree nodes just grow to the right leading to a linked list shape where all the left pointers are NULL. A similar thing happens if the nodes are inserted in decreasing order (4, 3, 2, 1). The linked list shape defeats the lg(N) performance. We will not address that issue here, instead focusing on pointers and recursion.
二分查询树的形状极大的依赖于要插入的节点的顺序。特别是,如果节点按递增顺序(1,2,3,4)插入,树节点只向右增长导致像链表形状一样而左边指针都是空的。类似的情况是按递减顺序(4,3,2,1)插入。链表形状优于lg(N)性能。这里我们将不会阐述这个问题,而是关注指针和递归。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值