二叉树

来源:互联网 发布:淘宝换购 编辑:程序博客网 时间:2024/06/07 01:34

一、二叉树的定义

1.二叉树的特点

⑴每个结点最多有两棵子树,没有子树或者有一棵子树都是可以的

⑵左子树和右子树是有顺序的,即使某结点只有一棵子树,也要区分它是左子树还是右子树

2.特殊二叉树

⑴斜树

所有的结点都只有左子树的二叉树叫左斜树,所有的结点都只有右子树的二叉树叫右斜树。统称为斜树。

⑵满二叉树

在一棵二叉树中,所有的分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树叫满二叉树。


⑶完全二叉树

对一棵具有n个结点的二叉树按层序编号,如果编号i(0=<i<=n)的结点与同样深度的满二叉树编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树


满二叉树一定是完全二叉树,但完全二叉树不一定是满的,同样结点数的二叉树,完全二叉树的深度最小。

二、二叉树的性质

在二叉树的第i层上最多有2 i-1 个节点 。(i>=1)

⑵二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)

n0=n2+1  n0表示度数为0的节点 n2表示度数为2的节点

在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[X]是向下取整。

⑸若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:

① 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;  

② 若 2i>n,则该结点无左孩子,  否则,编号为 2i 的结点为其左孩子结点;

③ 若 2i+1>n,则该结点无右孩子结点,  否则,编号为2i+1 的结点为其右孩子结点。

三、二叉树的存储结构

⑴顺序存储结构(一般只用于完全二叉树)

完全二叉树的顺序存储结构:


非完全二叉树:


⑵二叉链表


data是数据域,lchild指向左孩子,rchild指向右孩子


还可以增加一个指向其双亲的指针域,那样就成了三叉链表。

四、二叉树的遍历

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

用几种遍历方式时,若二叉树为空,则空操作返回。

⑴前序遍历

先访问根结点,然后前序遍历左子树,再前序遍历右子树。


⑵中序遍历

先中序遍历左子树,根结点,最后中序遍历右子树


⑶后序遍历

从左到右先叶子后结点的方式遍历左右子树,最后是访问根结点


⑷层序遍历

从上到下,从左到右按层遍历


具体算法:


先建一个二叉树原型:

public class Node { private int data;      private Node leftNode;      private Node rightNode;      public Node(int data, Node leftNode, Node rightNode){          this.data = data;          this.leftNode = leftNode;          this.rightNode = rightNode;      }        public int getData() {          return data;      }      public void setData(int data) {          this.data = data;      }      public Node getLeftNode() {          return leftNode;      }      public void setLeftNode(Node leftNode) {          this.leftNode = leftNode;      }      public Node getRightNode() {          return rightNode;      }      public void setRightNode(Node rightNode) {          this.rightNode = rightNode;      }  }
遍历:

public class BinaryTree {  public Node init() {//注意必须逆序建立,先建立子节点,再逆序往上建立,因为非叶子结点会使用到下面的节点,而初始化是按顺序初始化的,不逆序建立会报错          Node J = new Node(8, null, null);          Node H = new Node(4, null, null);          Node G = new Node(2, null, null);          Node F = new Node(7, null, J);          Node E = new Node(5, H, null);          Node D = new Node(1, null, G);          Node C = new Node(9, F, null);          Node B = new Node(3, D, E);          Node A = new Node(6, B, C);          return A;   //返回根节点      }        public void printNode(Node node){          System.out.print(node.getData());      }      public void theFirstTraversal(Node root) {  //先序遍历          printNode(root);          if (root.getLeftNode() != null) {  //使用递归进行遍历左孩子              theFirstTraversal(root.getLeftNode());          }          if (root.getRightNode() != null) {  //递归遍历右孩子              theFirstTraversal(root.getRightNode());          }      }      public void theInOrderTraversal(Node root) {  //中序遍历          if (root.getLeftNode() != null) {              theInOrderTraversal(root.getLeftNode());          }          printNode(root);          if (root.getRightNode() != null) {              theInOrderTraversal(root.getRightNode());          }      }    public void thePostOrderTraversal(Node root) {  //后序遍历          if (root.getLeftNode() != null) {              thePostOrderTraversal(root.getLeftNode());          }          if(root.getRightNode() != null) {              thePostOrderTraversal(root.getRightNode());          }          printNode(root);      }         public void levelTraverse(Node root) {        Queue<Node> queue = new LinkedList<Node>();        queue.offer(root);// 从根节点入队列        while (!queue.isEmpty()) {// 在队列为空前反复迭代        Node bitNode = queue.poll();// 取出队列首节点         printNode(bitNode);              if (bitNode.getLeftNode() != null)                queue.offer(bitNode.getLeftNode());// 左孩子入列            if (bitNode.getRightNode() != null)                queue.offer(bitNode.getRightNode());// 右孩子入列        }    }   public static void main(String[] args) {          BinaryTree tree = new BinaryTree();          Node root = tree.init();          System.out.println("先序遍历");          tree.theFirstTraversal(root);          System.out.println("");          System.out.println("中序遍历");          tree.theInOrderTraversal(root);          System.out.println("");          System.out.println("后序遍历");          tree.thePostOrderTraversal(root);          System.out.println("");         System.out.println("层序遍历");          tree.levelTraverse(root);          System.out.println("");     }  }

五、赫夫曼树


树的路径长度就是从树根到每一个结点的路径长度之和,a是1+1+2+2+3+3+4+4=20,b是1+2+3+3+2+1+2+2=16

树的带权路径长度就是所有叶子结点的带权路径长度之和,带权路径长度WPL最小的二叉树称为赫夫曼树,又称最优二叉树。

a WPL=5x1+15x2+40x3+30x4+10x4=315

b WPL=5x3+15x3+40x2+30x2+10x2=220

如何构建赫夫曼树:

【5、8、4、11、9、13】为例来画出哈夫曼树(数字大小代码权重大小,越大的权重越大)

先排序,4,5,8,9,11,13  

取最小的4,5 组成9 再排序 8,9(4,5),9,11,13

递归,最终变为、















原创粉丝点击