剑指Offer——二叉树

前言

数据结构通常是编程面试中考察的重点。在参加面试之前,应聘者需要熟练掌握链表、树、栈、队列和哈希表等数据结构,以及它们的操作。本片博文主要讲解二叉树操作的相关知识,主要包括二叉树的建立、遍历方法的循环和递归写法。

二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。

二叉树的java实现

首先创建一棵二叉树如下图,然后对这颗二叉树进行遍历操作(遍历操作的实现分为递归实现和非递归实现),同时还提供一些方法如获取双亲结点、获取左孩子、右孩子等。

  1. package cn.edu.ujn.nk;
  2.  
  3. import java.util.Stack;
  4.  
  5. /**
  6. * 二叉树的链式存储
  7. * @author WWX
  8. */
  9. public class BinaryTree {
  10.  
  11. private TreeNode root=null;
  12.  
  13. public BinaryTree(){
  14. root=new TreeNode(1,"rootNode(A)");
  15. }
  16.  
  17. /**
  18. * 创建一棵二叉树
  19. * <pre>
  20. * A
  21. * B C
  22. * D E F
  23. * </pre>
  24. * @param root
  25. * @author WWX
  26. */
  27. public void createBinTree(TreeNode root){
  28. TreeNode newNodeB = new TreeNode(2,"B");
  29. TreeNode newNodeC = new TreeNode(3,"C");
  30. TreeNode newNodeD = new TreeNode(4,"D");
  31. TreeNode newNodeE = new TreeNode(5,"E");
  32. TreeNode newNodeF = new TreeNode(6,"F");
  33. root.leftChild=newNodeB;
  34. root.rightChild=newNodeC;
  35. root.leftChild.leftChild=newNodeD;
  36. root.leftChild.rightChild=newNodeE;
  37. root.rightChild.rightChild=newNodeF;
  38. }
  39.  
  40. public boolean isEmpty(){
  41. return root==null;
  42. }
  43.  
  44. //树的高度
  45. public int height(){
  46. return height(root);
  47. }
  48.  
  49. //节点个数
  50. public int size(){
  51. return size(root);
  52. }
  53.  
  54. private int height(TreeNode subTree){
  55. if(subTree == null)
  56. return 0; // 递归结束:空树高度为0
  57. else{
  58. int i = height(subTree.leftChild);
  59. int j = height(subTree.rightChild);
  60. return (i < j) ? (j + 1) : (i + 1);
  61. }
  62. }
  63.  
  64. private int size(TreeNode subTree){
  65. if(subTree == null){
  66. return 0;
  67. }else{
  68. return 1 + size(subTree.leftChild) + size(subTree.rightChild);
  69. }
  70. }
  71.  
  72. //返回双亲结点
  73. public TreeNode parent(TreeNode element){
  74. return (root == null|| root == element) ? null : parent(root, element);
  75. }
  76.  
  77. public TreeNode parent(TreeNode subTree,TreeNode element){
  78. if(subTree == null)
  79. return null;
  80. if(subTree.leftChild == element || subTree.rightChild == element)
  81. //返回父结点地址
  82. return subTree;
  83. TreeNode p;
  84. // 先在左子树中找,如果左子树中没有找到,才到右子树去找
  85. if((p = parent(subTree.leftChild, element)) != null)
  86. //递归在左子树中搜索
  87. return p;
  88. else
  89. //递归在右子树中搜索
  90. return parent(subTree.rightChild, element);
  91. }
  92.  
  93. public TreeNode getLeftChildNode(TreeNode element){
  94. return (element != null) ? element.leftChild : null;
  95. }
  96.  
  97. public TreeNode getRightChildNode(TreeNode element){
  98. return (element != null) ? element.rightChild : null;
  99. }
  100.  
  101. public TreeNode getRoot(){
  102. return root;
  103. }
  104.  
  105. //在释放某个结点时,该结点的左右子树都已经释放,
  106. //所以应该采用后续遍历,当访问某个结点时将该结点的存储空间释放
  107. public void destroy(TreeNode subTree){
  108. //删除根为subTree的子树
  109. if(subTree!=null){
  110. //删除左子树
  111. destroy(subTree.leftChild);
  112. //删除右子树
  113. destroy(subTree.rightChild);
  114. //删除根结点
  115. subTree=null;
  116. }
  117. }
  118.  
  119. public void traverse(TreeNode subTree){
  120. System.out.println("key:"+subTree.key+"--name:"+subTree.data);;
  121. traverse(subTree.leftChild);
  122. traverse(subTree.rightChild);
  123. }
  124.  
  125. //前序遍历
  126. public void preOrder(TreeNode subTree){
  127. if(subTree!=null){
  128. visted(subTree);
  129. preOrder(subTree.leftChild);
  130. preOrder(subTree.rightChild);
  131. }
  132. }
  133.  
  134. //中序遍历
  135. public void inOrder(TreeNode subTree){
  136. if(subTree!=null){
  137. inOrder(subTree.leftChild);
  138. visted(subTree);
  139. inOrder(subTree.rightChild);
  140. }
  141. }
  142.  
  143. //后续遍历
  144. public void postOrder(TreeNode subTree) {
  145. if (subTree != null) {
  146. postOrder(subTree.leftChild);
  147. postOrder(subTree.rightChild);
  148. visted(subTree);
  149. }
  150. }
  151.  
  152. //前序遍历的非递归实现
  153. public void nonRecPreOrder(TreeNode p){
  154. Stack<TreeNode> stack=new Stack<TreeNode>();
  155. TreeNode node=p;
  156. while(node!=null||stack.size()>0){
  157. while(node!=null){
  158. visted(node);
  159. stack.push(node);
  160. node=node.leftChild;
  161. }
  162. while(stack.size()>0){
  163. node=stack.pop();
  164. node=node.rightChild;
  165. }
  166. }
  167. }
  168.  
  169. //中序遍历的非递归实现
  170. public void nonRecInOrder(TreeNode p){
  171. Stack<TreeNode> stack =new Stack<BinaryTree.TreeNode>();
  172. TreeNode node =p;
  173. while(node!=null||stack.size()>0){
  174. //存在左子树
  175. while(node!=null){
  176. stack.push(node);
  177. node=node.leftChild;
  178. }
  179. //栈非空
  180. if(stack.size()>0){
  181. node=stack.pop();
  182. visted(node);
  183. node=node.rightChild;
  184. }
  185. }
  186. }
  187.  
  188. //后序遍历的非递归实现
  189. public void noRecPostOrder(TreeNode p){
  190. Stack<TreeNode> stack=new Stack<BinaryTree.TreeNode>();
  191. TreeNode node =p;
  192. while(p!=null){
  193. //左子树入栈
  194. for(;p.leftChild!=null;p=p.leftChild){
  195. stack.push(p);
  196. }
  197. //当前结点无右子树或右子树已经输出
  198. while(p!=null&&(p.rightChild==null||p.rightChild==node)){
  199. visted(p);
  200. //纪录上一个已输出结点
  201. node =p;
  202. if(stack.empty())
  203. return;
  204. p=stack.pop();
  205. }
  206. //处理右子树
  207. stack.push(p);
  208. p=p.rightChild;
  209. }
  210. }
  211. public void visted(TreeNode subTree){
  212. subTree.isVisted=true;
  213. System.out.println("key:"+subTree.key+"--name:"+subTree.data);;
  214. }
  215.  
  216. /**
  217. * 二叉树的节点数据结构
  218. * @author WWX
  219. */
  220. private class TreeNode{
  221. private int key = 0;
  222. private String data = null;
  223. private boolean isVisted = false;
  224. private TreeNode leftChild = null;
  225. private TreeNode rightChild = null;
  226.  
  227. public TreeNode(){}
  228.  
  229. /**
  230. * @param key 层序编码
  231. * @param data 数据域
  232. */
  233. public TreeNode(int key,String data){
  234. this.key = key;
  235. this.data = data;
  236. this.leftChild = null;
  237. this.rightChild = null;
  238. }
  239. }
  240.  
  241. //测试
  242. public static void main(String[] args) {
  243. BinaryTree bt = new BinaryTree();
  244. bt.createBinTree(bt.root);
  245. System.out.println("the size of the tree is " + bt.size());
  246. System.out.println("the height of the tree is " + bt.height());
  247.  
  248. System.out.println("***递归实现****(前序遍历)[ABDECF]遍历*****************");
  249. bt.preOrder(bt.root);
  250.  
  251. System.out.println("***递归实现****(中序遍历)[DBEACF]遍历*****************");
  252. bt.inOrder(bt.root);
  253.  
  254. System.out.println("***递归实现****(后序遍历)[DEBFCA]遍历*****************");
  255. bt.postOrder(bt.root);
  256.  
  257. System.out.println("***非递归实现****(前序遍历)[ABDECF]遍历*****************");
  258. bt.nonRecPreOrder(bt.root);
  259.  
  260. System.out.println("***非递归实现****(中序遍历)[DBEACF]遍历*****************");
  261. bt.nonRecInOrder(bt.root);
  262.  
  263. System.out.println("***非递归实现****(后序遍历)[DEBFCA]遍历*****************");
  264. bt.noRecPostOrder(bt.root);
  265. }
  266. }

美文美图

 


剑指Offer——二叉树的更多相关文章

  1. 剑指offer 二叉树中和为某一个值的路径

    剑指offer 牛客网 二叉树中和为某一个值的路径 # -*- coding: utf-8 -*- """ Created on Tue Apr 9 15:53:58 2 ...

  2. 剑指offer 二叉树的层序遍历

    剑指offer 牛客网 二叉树的层序遍历 # -*- coding: utf-8 -*- """ Created on Tue Apr 9 09:33:16 2019 @ ...

  3. JS数据结构与算法 - 剑指offer二叉树算法题汇总

    ❗❗ 必看经验 在博主刷题期间,基本上是碰到一道二叉树就不会碰到一道就不会,有时候一个下午都在搞一道题,看别人解题思路就算能看懂,自己写就呵呵了.一气之下不刷了,改而先去把二叉树的基础算法给搞搞懂,然 ...

  4. 剑指offer——二叉树的镜像

    题目:操作给定的二叉树,将其变换为源二叉树的镜像. 思路:前序(根左右的顺序)遍历一棵树,在存储的时候将其左右树进行交换,最后按照处理后的树还原,即得到其镜像. /** public class Tr ...

  5. 剑指Offer 二叉树中和为某一值的路径(dfs)

    题目描述 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.     思路: 递归,然后深搜,因为题目定义的, ...

  6. 剑指Offer 二叉树的镜像

    题目描述 操作给定的二叉树,将其变换为源二叉树的镜像. 输入描述: 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ ...

  7. 剑指Offer——二叉树的下一个结点

    题目描述: 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 分析: 如果该结点存在右子树,那么返回右子树的最左结 ...

  8. 剑指Offer——二叉树的深度

    题目描述: 输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. 分析: 二叉树的深度等于其左子树的深度和右子树的深度两个中最大的深 ...

  9. 剑指Offer——二叉树中和为某一值的路径

    题目描述: 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径. 分析: 先序遍历二叉树,找到二叉树中结点值的和 ...

随机推荐

  1. bzoj2753[SCOI2012]滑雪与时间胶囊 最小生成树

    Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 2843  Solved: 993[Submit][Status][Discuss] Descripti ...

  2. Sql Server 镜像相关

    http://blog.csdn.net/dba_huangzj/article/details/35995083

  3. JavaScript实现简单的双向数据绑定

    什么是双向数据绑定 双向数据绑定简单来说就是UI视图(View)与数据(Model)相互绑定在一起,当数据改变之后相应的UI视图也同步改变.反之,当UI视图改变之后相应的数据也同步改变. 双向数据绑定 ...

  4. 如何使用 TeamViewer 配置QuickConnect按钮?

    QuickConnect作为TeamViewer中一个比较重要的部分,得到了很多用户的认可.那么在实际运用中,怎么才能设置网页或单个程序的QuickConnect呢?所以小编以此问题为例,教大家如何配 ...

  5. windows server 2008 R2 禁用ipv6和隧道适配器

    在windows server 2008 R2操作系统下部署weblogic web application,部署完成后进行测试,发现测试页的地址使用的是隧道适配器的地址,而不是静态的ip地址,而且所 ...

  6. Spring中<context:annotation-config/>

    最近在研究Spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Sp ...

  7. 使用 OpenCV 与 Face++ 人脸识别

    今天看到一篇文章<使用 OpenCV 与 Face++ 实现人脸解锁>,感觉挺好玩,就照着作者的讲解,写了一下.详细内容还请看原作者文章. # *^_^* coding:utf-8 *^_ ...

  8. CRM客户关系管理系统(六)

    第六章.排序和搜索功能开发  6.1.排序功能开发 (1)kingadmin_tags.py @register.simple_tag def get_sorted_column(column,sor ...

  9. ACM Where is the Marble?

    Description   Raju and Meena love to play with Marbles. They have got a lot of marbles with numbers ...

  10. 如何恢复Initial commit之前的源文件

    在github新建了一个空的库,然后到本地文件夹下,git init了一下,将remote和本地的关联起来了,然后git pull了一下,本地的项目源码全没了,用以下命令可以帮你恢复 git refl ...