数据结构(二)之二叉树

基础概念  

  二叉树(binary tree)是一棵树,其中每个结点都不能有多于两个儿子。

  二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

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

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

    (3)左、右子树也分别为二叉排序树;

二叉树的遍历

  二叉树的遍历是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。二叉树的遍历方式有很多,主要有前序遍历,中序遍历,后序遍历。

前序遍历

  前序遍历的规则是:若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树

中序遍历

  中序遍历的规则是:若树为空,则空操作返回;否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。可以看到,如果是二叉排序树,中序遍历的结果就是个有序序列。

后序遍历

  后序遍历的规则是:若树为空,则空操作返回;然后先遍历左子树,再遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。

 

删除结点

  对于二叉排序树的其他操作,比如插入,遍历等,比较容易理解;而删除操作相对复杂些。对于要删除的结点,有以下三种情况:

    1.叶子结点;

    2.仅有左子树或右子树的结点;

    3.左右子树都有结点;

  对于1(要删除结点为叶子结点)直接删除,即直接解除父节点的引用即可,对于第2种情况(要删除的结点仅有一个儿子),只需用子结点替换掉父节点即可;而对于要删除的结点有两个儿子的情况,比较常用处理逻辑为,在其子树中找寻一个结点来替换,而这个结点我们成为中序后继结点。

  可以看到,我们找到的这个用来替换的结点,可以是删除结点的右子树的最小结点(6),也可以是其左子树的最大结点(4),这样可以保证替换后树的整体结构不用发生变化。为什么称为中序后继结点呢?我们来看下这棵树的中序遍历结果 1-2-3-4-5-6-7-8-9。可以很清晰的看到,其实要找的这个结点,可以是结点5的前驱或者后继。

代码实现

  1 package treeDemo;
  2
  3 /**
  4  * Created by chengxiao on 2017/02/12.
  5  */
  6 public class BinaryTree {
  7     //根节点
  8     private Node root;
  9     /**
 10      * 树的结点
 11      */
 12     private static class Node{
 13         //数据域
 14         private long data;
 15         //左子结点
 16         private Node leftChild;
 17         //右子结点
 18         private Node rightChild;
 19         Node(long data){
 20             this.data = data;
 21         }
 22     }
 23
 24     /**
 25      * 插入结点
 26      * @param data
 27      */
 28     public void insert(long data){
 29         Node newNode = new Node(data);
 30         Node currNode = root;
 31         Node parentNode;
 32         //如果是空树
 33         if(root == null){
 34             root = newNode;
 35             return;
 36         }
 37         while(true){
 38             parentNode = currNode;
 39             //向右搜寻
 40             if(data > currNode.data){
 41                 currNode = currNode.rightChild;
 42                 if(currNode == null){
 43                     parentNode.rightChild = newNode;
 44                     return;
 45                 }
 46             }else{
 47                 //向左搜寻
 48                 currNode = currNode.leftChild;
 49                 if(currNode == null){
 50                     parentNode.leftChild = newNode;
 51                     return;
 52                 }
 53             }
 54         }
 55
 56     }
 57
 58     /**
 59      * 前序遍历
 60      * @param currNode
 61      */
 62     public void preOrder(Node currNode){
 63         if(currNode == null){
 64             return;
 65         }
 66         System.out.print(currNode.data+" ");
 67         preOrder(currNode.leftChild);
 68         preOrder(currNode.rightChild);
 69     }
 70
 71     /**
 72      * 中序遍历
 73      * @param currNode
 74      */
 75     public void inOrder(Node currNode){
 76         if(currNode == null){
 77             return;
 78         }
 79         inOrder(currNode.leftChild);
 80         System.out.print(currNode.data+" ");
 81         inOrder(currNode.rightChild);
 82
 83     }
 84
 85     /**
 86      * 后序遍历
 87      * @param currNode
 88      */
 89     public void postOrder(Node currNode){
 90         if(currNode == null){
 91             return;
 92         }
 93         postOrder(currNode.leftChild);
 94         postOrder(currNode.rightChild);
 95         System.out.print(currNode.data+" ");
 96     }
 97
 98     /**
 99      * 查找结点
100      * @param data
101      * @return
102      */
103     public Node find(long data){
104         Node currNode = root;
105         while(currNode!=null){
106             if(data>currNode.data){
107                 currNode = currNode.rightChild;
108             }else if(data<currNode.data){
109                 currNode = currNode.leftChild;
110             }else{
111                 return currNode;
112             }
113         }
114         return null;
115     }
116
117     /**
118      * 删除结点 分为3种情况
119      * 1.叶子结点
120      * 2.该节点有一个子节点
121      * 3.该节点有二个子节点
122      * @param data
123      */
124     public boolean delete(long data) throws Exception {
125         Node curr = root;
126         //保持一个父节点的引用
127         Node parent = curr;
128         //删除结点是左子结点还是右子结点,
129         boolean isLeft = true;
130         while(curr != null && curr.data!=data){
131             parent = curr;
132             if(data > curr.data){
133                 curr = curr.rightChild;
134                 isLeft = false;
135             }else{
136                 curr = curr.leftChild;
137                 isLeft = true;
138             }
139         }
140         if(curr==null){
141             throw new Exception("要删除的结点不存在");
142         }
143         //第一种情况,要删除的结点为叶子结点
144         if(curr.leftChild == null && curr.rightChild == null){
145             if(curr == root){
146                 root = null;
147                 return true;
148             }
149             if(isLeft){
150                 parent.leftChild = null;
151             }else{
152                 parent.rightChild = null;
153             }
154         }else if(curr.leftChild == null){
155             //第二种情况,要删除的结点有一个子节点且是右子结点
156             if(curr == root){
157                 root = curr.rightChild;
158                 return true;
159             }
160             if(isLeft){
161                 parent.leftChild = curr.rightChild;
162             }else{
163                 parent.rightChild = curr.rightChild;
164             }
165         }else if(curr.rightChild == null){
166             //第二种情况,要删除的结点有一个子节点且是左子结点
167             if(curr == root){
168                 root = curr.leftChild;
169                 return true;
170             }
171             if(isLeft){
172                 parent.leftChild = curr.leftChild;
173             }else{
174                 parent.rightChild = curr.leftChild;
175             }
176         }else{
177             //第三种情况,也是最复杂的一种情况,要删除的结点有两个子节点,需要找寻中序后继结点
178             Node succeeder = getSucceeder(curr);
179             if(curr == root){
180                 root = succeeder;
181                 return  true;
182             }
183             if(isLeft){
184                 parent.leftChild = succeeder;
185             }else{
186                 parent.rightChild = succeeder;
187             }
188             //当后继结点为删除结点的右子结点
189             succeeder.leftChild = curr.leftChild;
190
191         }
192         return true;
193     }
194     public Node getSucceeder(Node delNode){
195         Node succeeder = delNode;
196         Node parent = delNode;
197         Node currNode = delNode.rightChild;
198         //寻找后继结点
199         while(currNode != null){
200             parent = succeeder;
201             succeeder = currNode;
202             currNode = currNode.leftChild;
203         }
204         //如果后继结点不是要删除结点的右子结点
205         if(succeeder != delNode.rightChild){
206             parent.leftChild = succeeder.rightChild;
207             //将后继结点的左右子结点分别指向要删除结点的左右子节点
208             succeeder.leftChild = delNode.leftChild;
209             succeeder.rightChild = delNode.rightChild;
210         }
211         return succeeder;
212
213     }
214     public static void main(String []args) throws Exception {
215         BinaryTree binaryTree = new BinaryTree();
216         //插入操作
217         binaryTree.insert(5);
218         binaryTree.insert(2);
219         binaryTree.insert(8);
220         binaryTree.insert(1);
221         binaryTree.insert(3);
222         binaryTree.insert(6);
223         binaryTree.insert(10);
224         //前序遍历
225         System.out.println("前序遍历:");
226         binaryTree.preOrder(binaryTree.root);
227         System.out.println();
228         //中序遍历
229         System.out.println("中序遍历:");
230         binaryTree.inOrder(binaryTree.root);
231         System.out.println();
232         //后序遍历
233         System.out.println("后序遍历:");
234         binaryTree.postOrder(binaryTree.root);
235         System.out.println();
236         //查找结点
237         Node node = binaryTree.find(10);
238         System.out.println("找到结点,其值为:"+node.data);
239         //删除结点
240         binaryTree.delete(8);
241         System.out.print("删除结点8,中序遍历:");
242         binaryTree.preOrder(binaryTree.root);
243     }
244 }

二叉树的基本操作

执行结果

前序遍历:
5 2 1 3 8 6 10
中序遍历:
1 2 3 5 6 8 10
后序遍历:
1 3 2 6 10 8 5
找到结点,其值为:10
删除结点8,中序遍历:5 2 1 3 10 6 
时间: 2024-10-19 17:01:29

数据结构(二)之二叉树的相关文章

SDUT 3341 数据结构实验之二叉树二:遍历二叉树

数据结构实验之二叉树二:遍历二叉树 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 已知二叉树的一个按先序遍历输入的字符序列,如abc,,de,g,,f,,, (其中,表示空结点).请建立二叉树并按中序和后序的方式遍历该二叉树. Input 连续输入多组数据,每组数据输入一个长度小于50个字符的字符串. Output 每组输入数据对应输出2行:第1行输出中序遍历序列:第2行输出后序遍历序列

SDUT-3441_数据结构实验之二叉树二:遍历二叉树

数据结构实验之二叉树二:遍历二叉树 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 已知二叉树的一个按先序遍历输入的字符序列,如abc,,de,g,,f,,, (其中,表示空结点).请建立二叉树并按中序和后序的方式遍历该二叉树. Input 连续输入多组数据,每组数据输入一个长度小于50个字符的字符串. Output 每组输入数据对应输出2行: 第1行输出中序遍历序列: 第2行输出后序遍历序列. Sample Inp

数据结构(二十二)二叉树的顺序存储结构

一.顺序存储结构对数这种一对多的关系结构实现起来是比较困难的.但是二叉树是一种特殊的树,由于它的特殊性,使得用顺序存储结构也可以实现. 二.二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现结点之间的逻辑关系,比如双亲与孩子的关系,左右兄弟的关系等. 三.完全二叉树可以将相应下标的结点存到数组的相应下标的位置上,对于一般的二叉树来说,完全可以将其按完全二叉树编号,只不过,把不存在的结点设置为"null"而已.这显然是对存储空间的浪费,所以

javascript实现数据结构:线索二叉树

遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作.如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 设一棵二叉树有n个结点,则有n-1条边(指针连线) , 而n个结点共有2n个指针域(Lchild和Rchild) ,显然有n+1个空闲指针域未用.则可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息. 对结点的指针域做如下规定: 1.若结点有左子树,则其leftChild域指示其左孩子,否则令leftC

数据结构快速回顾——二叉树

二叉树(Binary Tree)是个有限元素的集合,该集合或者为空.或者由一个称为根(root)的元素及两个不相交的.被分别称为左子树和右子树的二叉树组成.当集合为空时,称该二叉树为空二叉树.在二叉树中,一个元素也称作一个结点. 基本概念: (1)结点的度.结点所拥有的子树的个数称为该结点的度. (2)叶结点.度为0的结点称为叶结点,或者称为终端结点. (3)分枝结点.度不为0的结点称为分支结点,或者称为非终端结点.一棵树的结点除叶结点外,其余的都是分支结点. (4)左孩子.右孩子.双亲.树中一

[数据结构与算法] 二叉树及其遍历方式

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289830.html 一.数据结构分类 (一)按逻辑结构 集合(无辑关系) 线性结构(线性表):数组.链表.栈.队列 非线性结构:树.图.多维数组 (二)按存储结构 顺序(数组)储结构.链式储结构.索引储结构.散列储结构 二.二叉树相关性质

python数据结构树和二叉树简介

一.树的定义 树形结构是一类重要的非线性结构.树形结构是结点之间有分支,并具有层次关系的结构.它非常类似于自然界中的树.树的递归定义:树(Tree)是n(n≥0)个结点的有限集T,T为空时称为空树,否则它满足如下两个条件:(1)有且仅有一个特定的称为根(Root)的结点:(2)其余的结点可分为m(m≥0)个互不相交的子集Tl,T2,…,Tm,其中每个子集本身又是一棵树,并称其为根的子树(Subree). 二.二叉树的定义 二叉树是由n(n≥0)个结点组成的有限集合.每个结点最多有两个子树的有序树

SDUT 3343 数据结构实验之二叉树四:还原二叉树

数据结构实验之二叉树四:还原二叉树 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度. Input 输入数据有多组,每组数据第一行输入1个正整数N(1 <= N <= 50)为树中结点总数,随后2行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区分大小写)的字符串. Output 输出一个整数,即该二叉树的

数据结构——二叉搜索树、B树、B-树

数据结构——二叉搜索树.B树.B-树 1. 综述 二叉排序树(Binary Sort Tree),又叫二叉查找树(Binary Search Tree),也叫二叉排序树. 二叉搜索树满足以下性质: 1. 若根节点左子树不为空,则左子树上的所有节点均小于根节点: 2. 若根节点右子树不为空,则右子树上的所有节点均大于根节点: 3. 其左右子树也是二叉搜索树(递归定义): 4. 没有键值相等的点. B树就是B-树.B树/B-树英文叫B-Tree,可能被不小心翻译成了B-树.