学习日志---二叉树概念与遍历(递归)
2015-09-02 10:45
891 查看
概念:
树,简单来说这种结构每个节点至多只有一个父节点,父节点可以有多个子节点。
树的内部概念:
结点:结点由数据元素和构造数据元素之间关系的指针组成。
结点的度:结点所拥有的子树的个数称为该结点的度。
叶结点:度为0的结点称为叶结点,叶结点也称作终端结点。
分支结点:度不为0的结点称为分支结点,分支结点也称 作非终端结点。
孩子结点:树中一个结点的子树的根结点称作这个结点的孩子结点。
双亲结点:若树中某结点有孩子结点,则这个结点就称作它的孩子结点的双亲结点。
兄弟结点:具有相同的双亲结点的结点称为兄弟结点。
树的度:树中所有结点的度的最大值称为该树的度。
结点的层次:从根结点到树中某结点所经路径上的分支数。
树的深度:树中所有结点的层次的最大值称为该树的深度。
无序树:树中任意一个结点的各孩子结点的排列没有严格次序的树称为无序树。
有序树:树中任意一个结点的各孩子结点的排列有严格次序的树称为有序树。
森林:m(m≥0)棵树的集合称为森林。
树的存储结构:
双亲表示法:数组实现,map的value表示的是父节点情况,缺点就是不容易找其孩子节点
孩子表示法:链表实现,指针指向孩子节点,缺点就是不容易找其双亲节点
双亲孩子表示法:用数组记录父节点,后面的接着跟链表,记录孩子节点的下标,弥补了以上两种不
足
孩子兄弟表示法:每个节点的下一整层都用一个链表表示。
详见附件的ppt。
二叉树:
二叉树的定义
二叉树是n(n≥0)个结点构成的、每个结点最多只有两个子树的有序树。
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上。
完全二叉树:如果一棵具有n个结点的二叉树的逻辑结构与满二叉树的前n个结点的逻辑结构相同
![](http://s3.51cto.com/wyfs02/M00/72/87/wKioL1XmQMOAP6FiAADaAQnBbNo735.jpg)
左边是满二叉树,右边是完全二叉树。
二叉树的性质:
性质1 若规定根结点的层次为0,则一棵非空二叉树的第i层上最多有2i(i≥0)个结点。
性质2 若规定空二叉树树的深度为-1(即根结点的深度为0),则深度为k的二叉树的最大结点数是 2k+1-1(k≥-1)个。
性质3 具有n个结点的完全二叉树的深度k为不超过log2(n+1)-1的最大整数。
性质4 对于一棵非空的二叉树,如果叶结点个数为n0,度为2的结点数为n2,则有n0= n2+1。
性质5 对于具有n个结点的完全二叉树,如果按照从上至下和从左至右的顺序对所有结点从0开始顺序编号,则对于序号为i的结点,有:
(1)如果i>0,则序号为i结点的双亲结点的序号为 (i-1)/2(“/”表示整除);如果i=0,则序号为i结点为根结点,无双亲结点。
(2)如果2×i+1<n,则序号为i结点的左孩子结点的序号为2×i+1;如果2×i+1≥n,则序号为i结点无左孩子结点。
(3)如果2×i+2<n,则序号为i结点的右孩子结点的序号为2×i+2;如果2×i+2≥n,则序号为i结点无右孩子结点
二叉树的存储结构:
数组结构:存之前要先把二叉树补成完全二叉树,然后逐层编号。利用上述的性质找节点;
链表结构:分为有头指针和无头指针的,存左孩子和右孩子;
二叉树实现:
二叉树遍历
三种遍历算法分别为前序遍历(DLR)、中序遍历(LDR)和后序遍历(LRD)。
前中后可以记为根的位置;
还有一种是层次遍历,按每一层的由左至右依次遍历;
二叉树的层序遍历算法如下(使用队列的数据结构):
(1)初始化设置一个队列;
(2)把根结点指针入队列;
(3)当队列非空时,循环执行步骤(3.a)到步骤(3.c);
(3.a)出队列取得当前队头结点,访问该结点;
(3.b)若该结点的左孩子结点非空,则将该结点的左孩子结点指针入队列;
(3.c)若该结点的右孩子结点非空,则将该结点的右孩子结点指针入队列;
(4)结束。
递归算法实现遍历:
附件:http://down.51cto.com/data/2366090
树,简单来说这种结构每个节点至多只有一个父节点,父节点可以有多个子节点。
树的内部概念:
结点:结点由数据元素和构造数据元素之间关系的指针组成。
结点的度:结点所拥有的子树的个数称为该结点的度。
叶结点:度为0的结点称为叶结点,叶结点也称作终端结点。
分支结点:度不为0的结点称为分支结点,分支结点也称 作非终端结点。
孩子结点:树中一个结点的子树的根结点称作这个结点的孩子结点。
双亲结点:若树中某结点有孩子结点,则这个结点就称作它的孩子结点的双亲结点。
兄弟结点:具有相同的双亲结点的结点称为兄弟结点。
树的度:树中所有结点的度的最大值称为该树的度。
结点的层次:从根结点到树中某结点所经路径上的分支数。
树的深度:树中所有结点的层次的最大值称为该树的深度。
无序树:树中任意一个结点的各孩子结点的排列没有严格次序的树称为无序树。
有序树:树中任意一个结点的各孩子结点的排列有严格次序的树称为有序树。
森林:m(m≥0)棵树的集合称为森林。
树的存储结构:
双亲表示法:数组实现,map的value表示的是父节点情况,缺点就是不容易找其孩子节点
孩子表示法:链表实现,指针指向孩子节点,缺点就是不容易找其双亲节点
双亲孩子表示法:用数组记录父节点,后面的接着跟链表,记录孩子节点的下标,弥补了以上两种不
足
孩子兄弟表示法:每个节点的下一整层都用一个链表表示。
详见附件的ppt。
二叉树:
二叉树的定义
二叉树是n(n≥0)个结点构成的、每个结点最多只有两个子树的有序树。
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上。
完全二叉树:如果一棵具有n个结点的二叉树的逻辑结构与满二叉树的前n个结点的逻辑结构相同
![](http://s3.51cto.com/wyfs02/M00/72/87/wKioL1XmQMOAP6FiAADaAQnBbNo735.jpg)
左边是满二叉树,右边是完全二叉树。
二叉树的性质:
性质1 若规定根结点的层次为0,则一棵非空二叉树的第i层上最多有2i(i≥0)个结点。
性质2 若规定空二叉树树的深度为-1(即根结点的深度为0),则深度为k的二叉树的最大结点数是 2k+1-1(k≥-1)个。
性质3 具有n个结点的完全二叉树的深度k为不超过log2(n+1)-1的最大整数。
性质4 对于一棵非空的二叉树,如果叶结点个数为n0,度为2的结点数为n2,则有n0= n2+1。
性质5 对于具有n个结点的完全二叉树,如果按照从上至下和从左至右的顺序对所有结点从0开始顺序编号,则对于序号为i的结点,有:
(1)如果i>0,则序号为i结点的双亲结点的序号为 (i-1)/2(“/”表示整除);如果i=0,则序号为i结点为根结点,无双亲结点。
(2)如果2×i+1<n,则序号为i结点的左孩子结点的序号为2×i+1;如果2×i+1≥n,则序号为i结点无左孩子结点。
(3)如果2×i+2<n,则序号为i结点的右孩子结点的序号为2×i+2;如果2×i+2≥n,则序号为i结点无右孩子结点
二叉树的存储结构:
数组结构:存之前要先把二叉树补成完全二叉树,然后逐层编号。利用上述的性质找节点;
链表结构:分为有头指针和无头指针的,存左孩子和右孩子;
二叉树实现:
//二叉树结点类 public class BiTreeNode { private BiTreeNode leftChild; // 左孩子 private BiTreeNode rightChild; // 右孩子 private Object data; // 数据元素 BiTreeNode() { this.leftChild = null; this.rightChild = null; } BiTreeNode(Object data,BiTreeNode leftNode,BiTreeNode rightNode) { this.data = data; this.leftChild = leftNode; this.rightChild = rightNode; } public BiTreeNode getLeftChild() { return leftChild; } public void setLeftChild(BiTreeNode leftChild) { this.leftChild = leftChild; } public BiTreeNode getRightChild() { return rightChild; } public void setRightChild(BiTreeNode rightChild) { this.rightChild = rightChild; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } } //下面的是二叉树的初始化 public class BiTree { //一切的起源都是根节点 private BiTreeNode root;//根节点 BiTree() { this.root = null; } //传进来两个子树,有两个子树的根节点构成该树根节点的左右子孩子 //每个树最重要的是根节点,找到根节点即可以操作树 BiTree(Object data,BiTree left,BiTree right) { BiTreeNode l,r; if(left==null) { l = null; } else { l=left.root; } if(right==null) { r = null; } else { r = right.root; } this.root = new BiTreeNode(data,l,r); } }
二叉树遍历
三种遍历算法分别为前序遍历(DLR)、中序遍历(LDR)和后序遍历(LRD)。
前中后可以记为根的位置;
还有一种是层次遍历,按每一层的由左至右依次遍历;
二叉树的层序遍历算法如下(使用队列的数据结构):
(1)初始化设置一个队列;
(2)把根结点指针入队列;
(3)当队列非空时,循环执行步骤(3.a)到步骤(3.c);
(3.a)出队列取得当前队头结点,访问该结点;
(3.b)若该结点的左孩子结点非空,则将该结点的左孩子结点指针入队列;
(3.c)若该结点的右孩子结点非空,则将该结点的右孩子结点指针入队列;
(4)结束。
递归算法实现遍历:
import java.util.LinkedList; import java.util.Queue; public class Traverse { //前序遍历--递归算法 public static void preOrder(BiTreeNode root,Visit visit) { if(root!=null) { visit.print(root.getData()); preOrder(root.getLeftChild(),visit); preOrder(root.getRightChild(),visit); } } //中序遍历--递归算法 public static void inOrder(BiTreeNode root,Visit visit) { if(root!=null) { inOrder(root.getLeftChild(),visit); visit.print(root.getData()); inOrder(root.getRightChild(),visit); } } //后序遍历--递归算法 public static void postOrder(BiTreeNode root,Visit visit) { if(root!=null) { postOrder(root.getLeftChild(),visit); postOrder(root.getRightChild(),visit); visit.print(root.getData()); } } //层次遍历 public static void levOrder(BiTreeNode root,Visit visit) { //这里要使用队列Queue Queue<BiTreeNode> queue = new LinkedList<BiTreeNode>(); if(root==null) { return ; } BiTreeNode curr; queue.add(root); while(!queue.isEmpty()) { curr = queue.remove(); visit.print(curr.getData()); if(curr.getLeftChild()!=null) { queue.add(curr.getLeftChild()); } if(curr.getRightChild()!=null) { queue.add(curr.getRightChild()); } } } } //打印节点信息类 public class Visit { public void print(Object item) { System.out.print(item+" "); } }打印二叉树的方法
//打印二叉树 public static void printBiTree(BiTreeNode root,int level) { if(root!=null) { //先打印该层的下一层右侧的元素, printBiTree(root.getRightChild(),level+1); //打印完右侧之后,把当前的这个向后移动 if(level!=0) { //输出6*(level-1)个空格 for(int i=0;i<6*(level-1);i++) { System.out.print(" "); } System.out.print("-----"); } //打印这一层的数据 System.out.println(root.getData()); //再打印该层下一层左侧的数据 printBiTree(root.getLeftChild(),level+1); } } //打印出的额效果如下: -----F -----C -----E A -----B -----G -----D建立和查找指定元素的方法
//建立二叉树的结点 public static BiTreeNode getTreeNode(Object data,BiTreeNode left,BiTreeNode right) { BiTreeNode node = new BiTreeNode(data,left,right); return node; } //查找指定元素 public static BiTreeNode search(BiTreeNode root,Object obj) { BiTreeNode node=null; if(root==null) { return null; } if(root.getData().equals(obj)) { return root; } if(root.getLeftChild()!=null) { node = search(root.getLeftChild(),obj); } if(root.getRightChild()!=null) { node = search(root.getRightChild(),obj); } return node; } }测试类:
public class Test { public static BiTreeNode makeTree() { BiTreeNode b,c,d,e,f,g; g = BiTree.getTreeNode(new Character('G'), null, null); d = BiTree.getTreeNode(new Character('D'), null, g); b = BiTree.getTreeNode(new Character('B'), d, null); e = BiTree.getTreeNode(new Character('E'), null, null); f = BiTree.getTreeNode(new Character('F'), null, null); c = BiTree.getTreeNode(new Character('C'), e, f); return BiTree.getTreeNode(new Character('A'), b, c); } public static void main(String[] args) { BiTreeNode root; BiTreeNode temp; Visit visit = new Visit(); root = Test.makeTree(); System.out.println("二叉树为:"); BiTree.printBiTree(root, 0); System.out.println(); System.out.println("前序遍历的序列是:"); Traverse.preOrder(root, visit); System.out.println(); System.out.println("中序遍历的序列是:"); Traverse.inOrder(root, visit); System.out.println(); System.out.println("后序遍历的序列是:"); Traverse.postOrder(root, visit); System.out.println(); System.out.println("层次遍历的序列是:"); Traverse.levOrder(root, visit); System.out.println(); temp = BiTree.search(root, new Character('T')); if(temp!=null) { System.out.println(temp.getData()); } else { System.out.println("没有找到指定元素!"); } } }
附件:http://down.51cto.com/data/2366090