标签:
数据结构一直都是断断续续的看,总是觉得理解的不够深入,特别是对树的理解,一直都很浅显,今儿又看了一遍,来做个总结吧。
首先,树中的一些概念:
1、树的节点包含一个数据元素,以及若干指向其子树的分支。节点拥有的子树的数量称为节点的度。节点的最大层次称为树的深度或高度。
2、二叉树是一种树形结构,其特点是每个节点至多有两棵子树,且子树有左右之分,次序不能随意颠倒。
3、满二叉树:一棵深度为k且有2^k - 1个节点的二叉树,称之为满二叉树。
4、完全二叉树:对一个深度为k,节点个数为n的二叉树,当且仅当每个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时,称之为完全二叉树。
5、哈夫曼树:又称最优树,是一类带权路径长度最短的树。
6、二叉查找树(Binary Search Tree):对任一个节点,左子树的所有值都小于它,右子树的所有值都大于它。
7、AVL树:带有平衡条件的二叉查找树。
8、伸展树:任一个节点被访问后,它就要经过一系列的AVL树的旋转,将该节点放到根上。
9、B-树:另外一种常用的查找树。有几个特点,对于M阶的B树,首先,根节点如果不是叶子,则其子节点的数量在2-M之间。其次,除根之外,所有的非叶子节点的子节点数在M/2 - M之间。最后,所有的叶子节点有相同的深度。
概念讲到这儿,下面来实战几种树。这里主要讲三种,分别是:
1、普通二叉树
2、二叉搜索树
3、AVL树
详细的代码下载地址:https://github.com/BLYang7/DataStructure/tree/master/BinNode
普通二叉树:讲一些树结构的定义,普通生成、前序中序后序遍历的实现(递归实现和用栈来实现)、层次遍历、获取二叉树的深度、判断是否平衡、寻找两个节点最近的共同父节点。
package com.BinNode; import java.util.LinkedList; import java.util.Queue; import java.util.Stack; /** * 树结构 */ public class TreeNode { /** * 定义一棵二叉树 */ private static class BinNode{ public int Element; public BinNode Left; public BinNode Right; public BinNode(int element){ this.Element = element; } public BinNode(int element, BinNode left, BinNode right){ this.Element = element; this.Left = left; this.Right = right; } public boolean isLeaf(){ return this.Left == null && this.Right == null; } } /** * 测试数据的构建 */ public static BinNode root = new BinNode(0); public static BinNode left = new BinNode(1); public static BinNode right = new BinNode(2); public static BinNode leftLeft = new BinNode(3); public static BinNode leftRight = new BinNode(4); public static BinNode rightLeft = new BinNode(5); public static BinNode rightRight = new BinNode(6); static{ root.Left = left; root.Right = right; left.Left = leftLeft; left.Right = leftRight; right.Left = rightLeft; right.Right = rightRight; } /** * 前序遍历,先遍历跟节点,再遍历左子树,后遍历右子树 * @param root 待遍历的根节点 */ private static void preOrder(BinNode root){ if( root == null ){ return; } System.out.print(root.Element + " "); //遍历打印输出 preOrder(root.Left); preOrder(root.Right); } /** * 中序遍历,先遍历左节点,再遍历跟节点,后遍历右子树 * @param root 待遍历的根节点 */ private static void middleOrder(BinNode root){ if( root == null ){ return; } middleOrder(root.Left); System.out.print( root.Element + " " ); middleOrder(root.Right); } /** * 后序遍历, 先遍历左节点,再遍历右节点,最后遍历根节点 * @param root 待遍历的根节点 */ private static void postOrder(BinNode root){ if( root == null ){ return; } postOrder(root.Left); postOrder(root.Right); System.out.print( root.Element + " " ); } /** * 前序遍历,不用递归实现,使用栈来实现 */ private static void preOrder1(BinNode root){ Stack stack = new Stack(); BinNode temp = root; //入栈 while( temp != null ){ System.out.print( temp.Element + " " ); if( temp.Right != null ){ stack.push(temp.Right); } temp = temp.Left; } //出栈 while( stack.size() > 0 ){ temp = (BinNode) stack.pop(); while( temp != null ){ System.out.print(temp.Element + " " ); if( temp.Right != null ){ stack.push(temp.Right); } temp = temp.Left; } } } /** * 中序遍历,不用递归实现,使用栈区来实现 * @param root */ private static void middleOrder1( BinNode root ){ Stack stack = new Stack(); BinNode temp = root; //入栈 while( temp != null ){ if( temp != null ){ stack.push(temp); } temp = temp.Left; } //出栈 while( stack.size() > 0 ){ temp = (BinNode) stack.pop(); System.out.print( temp.Element + " "); if( temp.Right != null ){ temp = temp.Right; stack.push(temp); while( temp != null ){ if( temp.Left != null){ stack.push(temp.Left); } temp = temp.Left; } } } } /** * 后序遍历,不用递归实现,使用栈来记录。这里有点繁,根节点还是要重新装载进栈 * @param root */ private static void postOrder1( BinNode root ){ Stack stack = new Stack(); BinNode temp = root; //入栈 while(temp != null){ if(temp != null){ stack.push(temp); } temp = temp.Left; } //出栈 while( stack.size() > 0 ){ BinNode lastVisit = temp; temp = (BinNode) stack.pop(); if(temp.Right == null || temp.Right == lastVisit){ System.out.print( temp.Element + " " ); } else if ( temp.Left == lastVisit ){ stack.push(temp); //再装载进去 temp = temp.Right; stack.push(temp); while( temp != null ){ if( temp.Left != null ){ stack.push(temp.Left); } temp = temp.Left; } } } } /** * 逐层打印二叉树的每一层 层次遍历 * 采用广度搜索优先的遍历方法,利用队列的方式实现 */ private static void printTree( BinNode root ){ if( root == null ){ return; } BinNode tmp = null; Queue<BinNode> queue = new LinkedList<BinNode>(); //队列中把根节点加进去 queue.add(root); //遍历整棵二叉树 while( !queue.isEmpty() ){ tmp = (BinNode) queue.poll(); System.out.print( tmp.Element + " "); //将左节点加入到队列中 if( tmp.Left != null ){ queue.add(tmp.Left); } //将右节点加入到队列中 if( tmp.Right != null ){ queue.add(tmp.Right); } } } /** * 获取二叉树的深度 */ private static int getDepth(BinNode root){ if( root == null ){ return 0; } int leftLength = getDepth(root.Left); int rightLength = getDepth(root.Right); return (leftLength > rightLength ? leftLength : rightLength) + 1; } /** * 判断是否为平衡二叉树 * 平衡的条件是:左右子树的深度相差不超过1 */ private static boolean isBalanceTree(BinNode root){ if( root == null ){ return true; } int leftLength = getDepth(root.Left); int rightLength = getDepth(root.Right); int distance = (leftLength > rightLength) ? (leftLength - rightLength) : (rightLength - leftLength); if( distance > 1 ){ return false; } else { return isBalanceTree(root.Left) && isBalanceTree(root.Right); } } /** * 寻找两个节点最近的共同父节点 * 将两个节点的父节点栈进行对比,一次弹出相同的节点,最后一次弹出的相同节点即为最近的公共父节点 */ private static BinNode findParentNode(BinNode root, BinNode node1, BinNode node2){ java.util.Stack stack1 = new java.util.Stack(); getPositionByNode(root, node1, stack1); java.util.Stack stack2 = new java.util.Stack(); getPositionByNode(root, node2, stack2); BinNode tempNode = null; while( stack1.peek() == stack2.peek() ){ tempNode = (BinNode) stack1.pop(); stack2.pop(); } return tempNode; } //把某个节点的所有父节点依次添加到栈中 private static boolean getPositionByNode(BinNode root, BinNode node, java.util.Stack stack){ if( root == null ){ return false; } else if( root == node ){ stack.push(root); return true; } else if( getPositionByNode(root.Left, node, stack) || getPositionByNode(root.Right, node, stack) ){ stack.push(root); return true; } return false; } //测试 public static void main(String[] args) { //前序遍历,递归调用 preOrder(root); System.out.println(); //中序遍历,递归调用 middleOrder(root); System.out.println(); //后序遍历,递归调用 postOrder(root); System.out.println(); //前序遍历,栈来实现 preOrder1(root); System.out.println(); //中序遍历,栈来实现 middleOrder1(root); System.out.println(); //后序遍历,栈来实现 postOrder1(root); System.out.println(); //层次遍历 printTree(root); System.out.println(); //获取二叉树的深度 System.out.println(getDepth(root)); //判断是否为平衡二叉树 System.out.println(isBalanceTree(root)); //寻找两个节点最近的共同父节点 System.out.println(findParentNode(root, leftLeft, leftRight).Element); } }
二叉搜索树:这里重点是节点的插入和节点的删除,请看代码:
package com.BinNode; /** * 二叉搜索树 Binary Search Tree * * 此程序实现一个二叉查找树的功能,可以进行动态插入、删除关键字; * 查询给定关键字、最小关键字、最大关键字;转换为有序列表(用于排序) */ import java.util.ArrayList; import java.util.List; public class BinarySearchTree { // 树的根结点 private TreeNode root = null; // 遍历结点列表 private List<TreeNode> nodelist = new ArrayList<TreeNode>(); // 树的节点类 private class TreeNode { private int key; private TreeNode leftChild; private TreeNode rightChild; private TreeNode parent; public TreeNode(int key, TreeNode leftChild, TreeNode rightChild, TreeNode parent) { this.key = key; this.leftChild = leftChild; this.rightChild = rightChild; this.parent = parent; } public int getKey() { return key; } public String toString() { String leftkey = (leftChild == null ? "" : String .valueOf(leftChild.key)); String rightkey = (rightChild == null ? "" : String .valueOf(rightChild.key)); return "(" + leftkey + " , " + key + " , " + rightkey + ")"; } } /** * isEmpty: 判断二叉查找树是否为空;若为空,返回 true ,否则返回 false . * */ public boolean isEmpty() { if (root == null) { return true; } else { return false; } } /** * TreeEmpty: 对于某些二叉查找树操作(比如删除关键字)来说,若树为空,则抛出异常。 */ public void TreeEmpty() throws Exception { if (isEmpty()) { throw new Exception("树为空!"); } } /** * search: 在二叉查找树中查询给定关键字 * * @param key 给定关键字 * @return 匹配给定关键字的树结点 */ public TreeNode search(int key) { TreeNode pNode = root; while (pNode != null && pNode.key != key) { if (key < pNode.key) { pNode = pNode.leftChild; } else { pNode = pNode.rightChild; } } return pNode; } /** * minElemNode: 获取二叉查找树中的最小关键字结点 * * @return 二叉查找树的最小关键字结点 * @throws Exception 若树为空,则抛出异常 */ public TreeNode minElemNode(TreeNode node) throws Exception { if (node == null) { throw new Exception("树为空!"); } TreeNode pNode = node; while (pNode.leftChild != null) { pNode = pNode.leftChild; } return pNode; } /** * maxElemNode: 获取二叉查找树中的最大关键字结点 * * @return 二叉查找树的最大关键字结点 * @throws Exception 若树为空,则抛出异常 */ public TreeNode maxElemNode(TreeNode node) throws Exception { if (node == null) { throw new Exception("树为空!"); } TreeNode pNode = node; while (pNode.rightChild != null) { pNode = pNode.rightChild; } return pNode; } /** * successor: 获取给定结点在中序遍历顺序下的后继结点 * * @param node 给定树中的结点 * @return 若该结点存在中序遍历顺序下的后继结点,则返回其后继结点;否则返回 null * @throws Exception */ public TreeNode successor(TreeNode node) throws Exception { if (node == null) { return null; } // 若该结点的右子树不为空,则其后继结点就是右子树中的最小关键字结点 if (node.rightChild != null) { return minElemNode(node.rightChild); } // 若该结点右子树为空 TreeNode parentNode = node.parent; while (parentNode != null && node == parentNode.rightChild) { node = parentNode; parentNode = parentNode.parent; } return parentNode; } /** * precessor: 获取给定结点在中序遍历顺序下的前趋结点 * * @param node 给定树中的结点 * @return 若该结点存在中序遍历顺序下的前趋结点,则返回其前趋结点;否则返回 null * @throws Exception */ public TreeNode precessor(TreeNode node) throws Exception { if (node == null) { return null; } // 若该结点的左子树不为空,则其前趋结点就是左子树中的最大关键字结点 if (node.leftChild != null) { return maxElemNode(node.leftChild); } // 若该结点左子树为空 TreeNode parentNode = node.parent; while (parentNode != null && node == parentNode.leftChild) { node = parentNode; parentNode = parentNode.parent; } return parentNode; } /** * insert: 将给定关键字插入到二叉查找树中 * * @param key 给定关键字 */ public void insert(int key) { TreeNode parentNode = null; TreeNode newNode = new TreeNode(key, null, null, null); TreeNode pNode = root; if (root == null) { root = newNode; return; } while (pNode != null) { parentNode = pNode; if (key < pNode.key) { pNode = pNode.leftChild; } else if (key > pNode.key) { pNode = pNode.rightChild; } else { // 树中已存在匹配给定关键字的结点,则什么都不做直接返回 return; } } if (key < parentNode.key) { parentNode.leftChild = newNode; newNode.parent = parentNode; } else { parentNode.rightChild = newNode; newNode.parent = parentNode; } } /** * insert: 从二叉查找树中删除匹配给定关键字相应的树结点 * * @param key 给定关键字 */ public void delete(int key) throws Exception { TreeNode pNode = search(key); if (pNode == null) { throw new Exception("树中不存在要删除的关键字!"); } delete(pNode); } /** * delete: 从二叉查找树中删除给定的结点. * * @param pNode 要删除的结点。前置条件是给定结点在二叉查找树中已经存在 * @throws Exception */ private void delete(TreeNode pNode) throws Exception { if (pNode == null) { return; } if (pNode.leftChild == null && pNode.rightChild == null) { // 该结点既无左孩子结点,也无右孩子结点 TreeNode parentNode = pNode.parent; if (pNode == parentNode.leftChild) { parentNode.leftChild = null; } else { parentNode.rightChild = null; } return; } if (pNode.leftChild == null && pNode.rightChild != null) { // 该结点左孩子结点为空,右孩子结点非空 TreeNode parentNode = pNode.parent; if (pNode == parentNode.leftChild) { parentNode.leftChild = pNode.rightChild; pNode.rightChild.parent = parentNode; } else { parentNode.rightChild = pNode.rightChild; pNode.rightChild.parent = parentNode; } return; } if (pNode.leftChild != null && pNode.rightChild == null) { // 该结点左孩子结点非空,右孩子结点为空 TreeNode parentNode = pNode.parent; if (pNode == parentNode.leftChild) { parentNode.leftChild = pNode.leftChild; pNode.rightChild.parent = parentNode; } else { parentNode.rightChild = pNode.leftChild; pNode.rightChild.parent = parentNode; } return; } // 该结点左右孩子结点均非空,则删除该结点的后继结点,并用该后继结点取代该结点 TreeNode successorNode = successor(pNode); delete(successorNode); pNode.key = successorNode.key; } /** * inOrderTraverseList: 获得二叉查找树的中序遍历结点列表 * * @return 二叉查找树的中序遍历结点列表 */ public List<TreeNode> inOrderTraverseList() { if (nodelist != null) { nodelist.clear(); } inOrderTraverse(root); return nodelist; } /** * inOrderTraverse: 对给定二叉查找树进行中序遍历 * * @param root 给定二叉查找树的根结点 */ private void inOrderTraverse(TreeNode root) { if (root != null) { inOrderTraverse(root.leftChild); nodelist.add(root); inOrderTraverse(root.rightChild); } } /** * toStringOfOrderList: 获取二叉查找树中关键字的有序列表 * * @return 二叉查找树中关键字的有序列表 */ public String toStringOfOrderList() { StringBuilder sbBuilder = new StringBuilder(" [ "); for (TreeNode p : inOrderTraverseList()) { sbBuilder.append(p.key); sbBuilder.append(" "); } sbBuilder.append("]"); return sbBuilder.toString(); } /** * 获取该二叉查找树的字符串表示 */ public String toString() { StringBuilder sbBuilder = new StringBuilder(" [ "); for (TreeNode p : inOrderTraverseList()) { sbBuilder.append(p); sbBuilder.append(" "); } sbBuilder.append("]"); return sbBuilder.toString(); } public TreeNode getRoot() { return root; } public static void testNode(BinarySearchTree bst, TreeNode pNode) throws Exception { System.out.println("本结点: " + pNode); System.out.println("前趋结点: " + bst.precessor(pNode)); System.out.println("后继结点: " + bst.successor(pNode)); } public static void testTraverse(BinarySearchTree bst) { System.out.println("二叉树遍历:" + bst); System.out.println("二叉查找树转换为有序列表: " + bst.toStringOfOrderList()); } // 测试 public static void main(String[] args) { try { BinarySearchTree bst = new BinarySearchTree(); System.out.println("查找树是否为空? " + (bst.isEmpty() ? "是" : "否")); int[] keys = new int[] { 15, 6, 18, 3, 7, 13, 20, 2, 9, 4 }; for (int key : keys) { bst.insert(key); } System.out.println("查找树是否为空? " + (bst.isEmpty() ? "是" : "否")); TreeNode minkeyNode = bst.minElemNode(bst.getRoot()); System.out.println("最小关键字: " + minkeyNode.getKey()); testNode(bst, minkeyNode); TreeNode maxKeyNode = bst.maxElemNode(bst.getRoot()); System.out.println("最大关键字: " + maxKeyNode.getKey()); testNode(bst, maxKeyNode); System.out.println("根结点关键字: " + bst.getRoot().getKey()); testNode(bst, bst.getRoot()); testTraverse(bst); System.out.println("****************************** "); testTraverse(bst); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } }
AVL树:平衡二叉树,对于这种树,涉及到旋转。当新插入一个节点时,要判断节点插入之后当前树是否平衡,如果不平衡,肯定是当前插入的节点破坏了原来节点的平衡关系。需要讲三层的子树旋转成两层。具体的几种旋转如图示:
上代码:
package com.BinNode; /** * AVL树 * */ public class AVLTree<T extends Comparable<? super T>> { /** * AVL树节点的定义 */ private static class AvlNode<T> { AvlNode(T theElement) { this(theElement, null, null); } AvlNode(T theElement, AvlNode<T> lt, AvlNode<T> rt) { element = theElement; left = lt; right = rt; height = 0; } T element; // 节点中的数据 AvlNode<T> left; // 左儿子 AvlNode<T> right; // 右儿子 int height; // 节点的高度 } // avl树根 private AvlNode<T> root; public AVLTree() { root = null; } // 在avl树中插入数据,重复数据复略 public void insert(T x) { root = insert(x, root); } // 在avl中删除数据,没有实现 public void remove(T x) { System.out.println("Sorry, remove unimplemented"); } // 在avl树中找最小的数据 public T findMin() throws Exception { if (isEmpty()) throw new Exception("空"); return findMin(root).element; } // 在avl树中找最大的数据 public T findMax() throws Exception { if (isEmpty()) throw new Exception("空"); return findMax(root).element; } // 搜索 public boolean contains(T x) { return contains(x, root); } public void makeEmpty() { root = null; } public boolean isEmpty() { return root == null; } // 排序输出avl树 public void printTree() { if (isEmpty()) System.out.println("Empty tree"); else printTree(root); } /** * AVL树的插入,重点就是这儿了 * @param x 要插入的元素 * @param t 树的根节点 * @return */ private AvlNode<T> insert(T x, AvlNode<T> t) { if (t == null){ return new AvlNode<T>(x, null, null); } int compareResult = x.compareTo(t.element); if (compareResult < 0) { t.left = insert(x, t.left);// 将x插入左子树中 // 打破平衡 if (height(t.left) - height(t.right) == 2){ // LL型(左左型) if (x.compareTo(t.left.element) < 0){ t = rotateWithLeftChild(t); } // LR型(左右型) else{ t = doubleWithLeftChild(t); } } } else if (compareResult > 0) { // 将x插入右子树中 t.right = insert(x, t.right); // 打破平衡 if (height(t.right) - height(t.left) == 2){ // RR型(右右型) if (x.compareTo(t.right.element) > 0){ t = rotateWithRightChild(t); } else{ // RL型 t = doubleWithRightChild(t); } } } else{ ; // 重复数据,什么也不做 } t.height = Math.max(height(t.left), height(t.right)) + 1;// 更新高度 return t; } // 带左子树旋转,适用于LL型 private AvlNode<T> rotateWithLeftChild(AvlNode<T> k2) { AvlNode<T> k1 = k2.left; k2.left = k1.right; k1.right = k2; k2.height = Math.max(height(k2.left), height(k2.right)) + 1; k1.height = Math.max(height(k1.left), k2.height) + 1; return k1; } // 带右子树旋转,适用于RR型 private AvlNode<T> rotateWithRightChild(AvlNode<T> k1) { AvlNode<T> k2 = k1.right; k1.right = k2.left; k2.left = k1; k1.height = Math.max(height(k1.left), height(k1.right)) + 1; k2.height = Math.max(height(k2.right), k1.height) + 1; return k2; } // 双旋转,适用于LR型 private AvlNode<T> doubleWithLeftChild(AvlNode<T> k3) { k3.left = rotateWithRightChild(k3.left); return rotateWithLeftChild(k3); } // 双旋转,适用于RL型 private AvlNode<T> doubleWithRightChild(AvlNode<T> k1) { k1.right = rotateWithLeftChild(k1.right); return rotateWithRightChild(k1); } // 找最小 private AvlNode<T> findMin(AvlNode<T> t) { if (t == null) return t; while (t.left != null) t = t.left; return t; } // 找最大 private AvlNode<T> findMax(AvlNode<T> t) { if (t == null) return t; while (t.right != null) t = t.right; return t; } // 搜索(查找) private boolean contains(T x, AvlNode<T> t) { while (t != null) { int compareResult = x.compareTo(t.element); if (compareResult < 0) t = t.left; else if (compareResult > 0) t = t.right; else return true; // Match } return false; // No match } // 前序遍历avl树 private void printTree(AvlNode<T> t) { if (t != null) { System.out.println(t.element); printTree(t.left); printTree(t.right); } } // 求高度 private int height(AvlNode<T> t) { return t == null ? -1 : t.height; } // Test program public static void main(String[] args) { AVLTree<Integer> t = new AVLTree<Integer>(); final int NUMS = 100; final int GAP = 37; for (int i = GAP; i != 0; i = (i + GAP) % NUMS){ t.insert(i); } t.printTree(); } }
标签:
原文地址:http://blog.csdn.net/sinat_22013331/article/details/51235151