数据结构二叉树定义 数据结构与算法(1)

概述

二叉树是树的特殊一种,具有如下特点:

数据结构二叉树定义 数据结构与算法(2)

二叉树五种基本形态

  1. 空二叉树。
  2. 只有一个根结点。
  3. 根结点只有左子树。
  4. 根结点只有右子树。
  5. 根结点既有左子树又有右子树。

数据结构二叉树定义 数据结构与算法(3)

特殊二叉树

斜树

数据结构二叉树定义 数据结构与算法(4)

满二叉树

数据结构二叉树定义 数据结构与算法(5)

完全二叉树

数据结构二叉树定义 数据结构与算法(6)

满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的 。

完全二叉树的所有结点与同样深度的满二叉树,它们按层序编号相同的结点是一 一对应的

数据结构二叉树定义 数据结构与算法(7)

完全二叉树的特点:

二叉树的性质

数据结构二叉树定义 数据结构与算法(8)

  1. 如果i=1 ,则结点 i 是二叉树的棍,无双亲;如果i> 1,则其双亲是结点 $\frac{i}{2}$。
  2. 如果2*i>n,则结点 i 左孩子(结点i为叶子结点) ;否则其左孩子是结点2*i。
  3. 如果2*i 1>0,则结点i无右孩子;否则其右孩子是结点2*i 1.

数据结构二叉树定义 数据结构与算法(9)

二叉树的顺序存储结构

package com.example.qxw.tree; import java.util.Arrays; /** * 二叉树的顺序存储结构: * * 二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,井且结点的存储位 置, * 也就是数组的下标要能体现结点之间的逻辑关系,比如双亲与孩子的关系,左右 兄弟的关系等 * * 顺序存储结构一般只用于完全二叉树 * * @author qinxuewu * @create 19/2/9下午6:12 * @since 1.0.0 */ public class TreeTest01<T> { //二叉树的默认深度 private final int DEFAULT_DEEP=16; //二叉树的深度 private int k; //用来存储数组的长 private int length; //数据区域 private Object[] data; //初始化二叉树 public TreeTest01(){ k=DEFAULT_DEEP; //深度为k的二叉树至多有2的k次方减1个结点 length=(int) Math.pow(2, k); data=new Object[length]; } public TreeTest01(int deep){ this.k=deep; length=(int) Math.pow(2, deep); data=new Object[length]; } //初始化二叉树 指定根结点 public TreeTest01(T element,int deep){ this.k=deep; length=(int) Math.pow(2, deep); data=new Object[length]; data[0]=element; } //根据元素查找在二叉树出现的第一个位置 public int indexOf(T element){ for(int i=0;i<data.length;i ){ if(data[i].equals(element)){ return i; } } return -1; } //根节点 public T getRoot(){ return (T) data[0]; } // /** * 查找指定结点的父节点 * * 根据二叉树的性质: * 如果i=1 ,则结点i是二叉树的根,无双亲;如果i> 1,则其双亲是结点(i/2) * 所以得出求父节点公式:(index-1)/2 因为数据下标从0开始所以index-1。 */ public T getParent(int index){ if(index==0){ throw new RuntimeException("该节点不存在父节点"); } return (T) data[(index-1)/2]; } //判断二叉树是否为空 public boolean isEmpty(){ return data[0]==null; } //获取指定结点 public T get(int index){ if(index>length||index<0){ throw new RuntimeException("超出底层数组范围"); } return (T) data[index]; } /** * 为指定结点添加子节点 * @param index * @param element * @param left 是否是左结点 */ public void add(int index,T element,boolean left){ if(data[index]==null){ throw new RuntimeException("该节点为空,不能添加子节点"); } if(index*2 1>length||index*2 2>length){ throw new RuntimeException("超出底层数组范围"); } if(left){ /** * 根据二叉树的性质: * 如果对一棵有 n 个结点的完全二叉树的结点按层序编号(每层从左到右))对任一结点i * 如果 2*i>n(i=结点编号即下标 n=结点总数),则结点i无左孩子 * 所以得出data[index*2 1]下标处如果为空就不存在左子节点可以进行插入 */ if(data[index*2 1]!=null){ throw new RuntimeException("该节点已经存在左子节点"); }else{ data[index*2 1]=element; } }else{ /** * 根据二叉树的性质: * 如果对一棵有 n 个结点的完全二叉树的结点按层序编号(每层从左到右))对任一结点i * 如果 2*i 1>n(i=结点编号即下标 n=结点总数),则结点i无右孩子 * 所以得出data[index*2 1 ]下标处如果为空就不存在右子节点可以进行插入 */ if(data[index*2 2]!=null){ throw new RuntimeException("该节点已经存在右子节点"); }else{ data[index*2 2]=element; } } } //获取指定结点的右节点 public T getRight(int index){ if(index*2 1>length||index*2 2>length){ throw new RuntimeException("超出底层数组范围"); } return (T) data[index*2 2]; } //获取指定结点的左结点 public T getLeft(int index){ if(index*2 1>length||index*2 2>length){ throw new RuntimeException("超出底层数组范围"); } return (T) data[index*2 1]; } public String toString(){ if(data[0]==null){ return "[]"; }else{ return Arrays.toString(data); } } public static void main(String[] args) { //完全二叉树 TreeTest01<String> tree=new TreeTest01<>("A",4); tree.add(0,"B",true); //左结点 tree.add(0,"C",false);//右结点 tree.add(1,"D",true); tree.add(1,"E",false); tree.add(2,"F",true); tree.add(2,"G",false); tree.add(3,"H",true); tree.add(3,"I",false); tree.add(4,"J",true); System.out.println(tree.toString()); System.out.println(tree.get(2)); // 获取下标2的结点:C System.out.println(tree.getParent(2));// 获取下标2的双亲结结点:A System.out.println(tree.getRight(2));// 获取下标2的右子结点:G System.out.println(tree.getLeft(2));// 获取下标2的左子结点:F } }

二叉树的链式存储结构

数据结构二叉树定义 数据结构与算法(10)

data是数据区域。lchild和 民rchild都是指针域分别存放指向左孩子和右孩子的指针

数据结构二叉树定义 数据结构与算法(11)

遍历二叉树

二叉树遍历方法

如果我们限制了从左到右的习惯方式,那么主要就分为四种

  1. 前序遍历

数据结构二叉树定义 数据结构与算法(12)

  1. 中序遍历

数据结构二叉树定义 数据结构与算法(13)

  1. 后序遍历

数据结构二叉树定义 数据结构与算法(14)

  1. 层序遍历

数据结构二叉树定义 数据结构与算法(15)

JAVA实现二叉树的链式存储以及遍历

public class Treenode<E> { private E data; //数据域 private TreeNode<E> lchild; //左孩子 private TreeNode<E> rchild; //右孩子 TreeNode(){} TreeNode(E e){ this.data = e; } TreeNode(E data,TreeNode<E> lchild, TreeNode<E> rchild){ this.data = data; this.lchild = lchild; this.rchild = rchild; } public void setData(E data){ this.data = data; } public E getData(){ return this.data; } public void setLchild(TreeNode<E> lchild){ this.lchild = lchild; } public TreeNode<E> getLchild(){ return this.lchild; } public void setRchild(TreeNode<E> rchild){ this.rchild = rchild; } public TreeNode<E> getRchild(){ return this.rchild; } } public class BinaryTree<E> { private TreeNode<E> root; //根节点 private List<TreeNode> nodeList = null; //二叉树节点的链式结构 public BinaryTree(){ } public BinaryTree(TreeNode<E> root){ this.root = root; } //把一个数组转化为一颗完全二叉树 public TreeNode<E> buildTree(E[] array){ nodeList = new LinkedList<TreeNode>(); //将数组中的元素依次转换为TreeNode节点,存放于链表中 for(int i=0; i< array.length; i ){ nodeList.add(new TreeNode(array[i])); } //对前(array.length / 2 - 1)个父节点,按照父节点与孩子节点的数字关系建立完全二叉树 for(int j=0; j < (array.length/2-1);j ){ /** * 根据二叉树的性质: * 如果对一棵有 n 个结点的完全二叉树的结点按层序编号(每层从左到右))对任一结点i * 如果 2*i>n(i=结点编号即下标 n=结点总数),则结点i无左孩子 * 如果 2*i 1>n(i=结点编号即下标 n=结点总数),则结点i无右孩子 */ //左孩子 (2*i 1) nodeList.get(j).setLchild(nodeList.get(j*2 1)); //右孩子 2*i 2) nodeList.get(j).setRchild(nodeList.get(j*2 2)); } //最后一个父节点:因为最后一个父节点可能没有右孩子,所以单独处理 int index = array.length/2 -1; //左孩子 nodeList.get(index).setLchild(nodeList.get(index*2 1)); //右孩子:如果数组的长度为奇数才有右孩子 if(array.length % 2 == 1){ nodeList.get(index).setRchild(nodeList.get(index*2 2)); } root=nodeList.get(0); //设置根节点 return root; } //得到树的高度 public int height(TreeNode<E> node){ if(node == null){ return 0; }else{ int i = height(node.getLchild()); int j = height(node.getRchild()); return (i<j)?(j 1):(i 1); } } //递归计算节点的总个数 public int size(TreeNode<E> node){ if(node == null){ return 0; }else{ //根节点 左子节点 右子节点 return 1 size(node.getLchild()) size(node.getRchild()); } } /** * 递归实现先序遍历 * 先访问根结点,然后前序遍历左子 树 , 再前序遍历右子树 * @param node */ public void preOrder(TreeNode<E> node){ if(node != null){ System.out.print(node.getData() " "); preOrder(node.getLchild()); preOrder(node.getRchild()); } } /** * 递归实现中序遍历 * 从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树, * 然后是访问根结点,最后中序遍历右子树 * @param node */ public void inOrder(TreeNode<E> node){ if(node != null){ inOrder(node.getLchild()); System.out.print(node.getData() " "); inOrder(node.getRchild()); } } /** * 递归实现后序遍历 * 从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点 * @param node */ public void postOrder(TreeNode<E> node){ if(node != null){ postOrder(node.getLchild()); postOrder(node.getRchild()); System.out.print(node.getData() " "); } } /** * 层序遍历 * * 从树的第一层,也就是根结点开始访问, 从上而下逐层遍历, * 在同一层中, 按从左到右的颇用才结点逐个访问 * @param root */ public void levelOrder(TreeNode<E> root){ Queue<TreeNode<E>> nodeQueue = new LinkedList<TreeNode<E>>(); TreeNode<E> node = null; nodeQueue.add(root); //将根节点入队 while(!nodeQueue.isEmpty()){ //队列不空循环 node = nodeQueue.peek(); System.out.print(node.getData() " "); nodeQueue.poll(); //队头元素出队 if(node.getLchild() != null){ //左子树不空,则左子树入队列 nodeQueue.add(node.getLchild()); } if(node.getRchild() != null){ //右子树不空,则右子树入队列 nodeQueue.add(node.getRchild()); } } } public static void main(String[] args) { //将一个数组转化为一颗完全二叉树 //将一个数组转化为一颗完全二叉树 Object[] array = {1,2,3,4,5,6,7,8}; BinaryTree bt = new BinaryTree(); TreeNode root = bt.buildTree(array); System.out.println("height: " bt.height(root)); System.out.println("size: " bt.size(root)); System.out.println("先序遍历:"); bt.preOrder(root); System.out.println(); System.out.println("中序遍历:"); bt.inOrder(root); System.out.println(); System.out.println("中序遍历:"); bt.levelOrder(root); System.out.println(); System.out.println("层次遍历:"); bt.levelOrder(root); } }

,