二叉树及其遍历方式

一、数据结构分类

(一)按逻辑结构

  1. 集合(无辑关系)
  2. 线性结构(线性表):数组、链表、栈、队列
  3. 非线性结构:树、图、多维数组

(二)按存储结构

顺序(数组)储结构、链式储结构、索引储结构、散列储结构

二、二叉树相关性质

  • 结点的度:一个结点的子树的个数记为该结点的度.
  • 树的度:所有节点中度数最大的结节的度数,叶子节点的度为零。
  • 树的高度:一棵树的最大层次数记为树的高度(或深度)。
  • 有序(无序)树:若将树中结点的各子树看成是从左到右具有次序的,即不能交换,则称该树为有序树。否则称为无序树。
  • 二叉树第i层(i≥1)上至多有2^(i-1)个节点。
  • 深度为k的二叉树至多有2^k-1个节点(k≥1)。
  • 对任何一棵二叉,若叶子节点数为n0,度为2的节点数为n2,则n0=n2+1。
  • 具有n个节点的完全二叉树的深度为 (㏒2^n)(向下取整)+1。
  • 对一棵有n个节点的完全二叉树的节点按层次从上到下,自左至右进行编号,则对任一节点i(1≤i≤n)有:若 i=1,则节点i是二叉树的根,无双亲;若 i>1,则其双亲为 i/2(向下取整)。若2i>n,则节点i没有孩子节点,否则其左孩子为2i。若2i+1>n,则节点i没有右孩子,否则其右孩子为2i+1。
  • 若深度为k的二叉树有2^k-1个节点,则称其为满二叉树。满二叉树是一棵完全二叉树。

  • 对于完全二叉树中,度为1的节点个数只可能为1个或0个。
  • 对于二叉树,如果叶子节点数为n0,度为1的节点数为n1,度为2的节点数为n2,则节点总数n = n0 + n1 + n2。
  • 对于任意树,总节点数 = 每个节点度数和 + 1
  • 二叉树的高度等于根与最远叶节点(具有最多祖先的节点)之间分支数目。空树的高度是-1。只有单个元素的二叉树,其高度为0。

.

三、二叉树的遍历

遍历是按某种策略访问树中的每个节点,且仅访问一次。

(一) 二叉树结构实现

Java代码 复制代码
  1. package tree.bintree;   
  2.   
  3. /**  
  4.  * 创建 非完全二叉树、完全二叉树、满二叉树  
  5.  *  
  6.  * 由于二叉树的节点增加没有什么规则,所以这里只是简单的使用了递一  
  7.  * 次性把整棵树创建出来,而没有设计出一个一个添加节点的方法与删除  
  8.  *   
  9.  * @author jzj  
  10.  * @date 2009-12-23  
  11.  */  
  12. public class BinTree {// Bin=Binary(二进位的, 二元的)   
  13.   
  14.     protected Entry root;//根   
  15.     private int size;//树的节点数   
  16.   
  17.     /**  
  18.      * 树的节点结构  
  19.      * @author jzj  
  20.      * @date 2009-12-23  
  21.      */  
  22.     protected static class Entry {   
  23.         int elem;//数据域,这里我们作为编号   
  24.         Entry left;//左子树   
  25.         Entry right;//右子树   
  26.   
  27.         public Entry(int elem) {   
  28.             this.elem = elem;   
  29.         }   
  30.   
  31.         public String toString() {   
  32.             return " number=" + elem;   
  33.         }   
  34.     }   
  35.   
  36.     /**  
  37.      * 根据给定的节点数创建一个完全二叉树或是满二叉树  
  38.      * @param nodeCount 要创建节点总数  
  39.      */  
  40.     public void createFullBiTree(int nodeCount) {   
  41.         root = recurCreateFullBiTree(1, nodeCount);   
  42.     }   
  43.   
  44.     /**  
  45.      * 递归创建完全二叉树  
  46.      * @param num 节点编号  
  47.      * @param nodeCount 节点总数  
  48.      * @return TreeNode 返回创建的节点  
  49.      */  
  50.     private Entry recurCreateFullBiTree(int num, int nodeCount) {   
  51.         size++;   
  52.         Entry rootNode = new Entry(num);//根节点   
  53.         //如果有左子树则创建左子树   
  54.         if (num * 2 <= nodeCount) {   
  55.             rootNode.left = recurCreateFullBiTree(num * 2, nodeCount);   
  56.             //如果还可以创建右子树,则创建   
  57.             if (num * 2 + 1 <= nodeCount) {   
  58.                 rootNode.right = recurCreateFullBiTree(num * 2 + 1, nodeCount);   
  59.             }   
  60.         }   
  61.         return (Entry) rootNode;   
  62.     }   
  63.   
  64.     /**  
  65.      * 根据给定的数组创建一棵树,这个棵树可以是完全二叉树也可是普通二叉树  
  66.      * 数组中为0的表示不创建该位置上的节点  
  67.      * @param nums 数组中指定了要创建的节点的编号,如果为0,表示不创建  
  68.      */  
  69.     public void createBinTree(int[] nums) {   
  70.         root = recurCreateBinTree(nums, 0);   
  71.     }   
  72.   
  73.     /**  
  74.      * 递归创建二叉树  
  75.      * @param nums 数组中指定了要创建的节点的编号,如果为0,表示不创建  
  76.      * @param index 需要使用数组中的哪个元素创建节点,如果为元素为0,则不创建  
  77.      * @return TreeNode 返回创建的节点,最终会返回树的根节点  
  78.      */  
  79.     private Entry recurCreateBinTree(int[] nums, int index) {   
  80.         //指定索引上的编号不为零上才需创建节点   
  81.         if (nums[index] != 0) {   
  82.             size++;   
  83.             Entry rootNode = new Entry(nums[index]);//根节点   
  84.             //如果有左子树则创建左子树   
  85.             if ((index + 1) * 2 <= nums.length) {   
  86.                 rootNode.left = (Entry) recurCreateBinTree(nums, (index + 1) * 2 - 1);   
  87.                 //如果还可以创建右子树,则创建   
  88.                 if ((index + 1) * 2 + 1 <= nums.length) {   
  89.                     rootNode.right = (Entry) recurCreateBinTree(nums, (index + 1) * 2);   
  90.                 }   
  91.             }   
  92.             return (Entry) rootNode;   
  93.         }   
  94.         return null;   
  95.   
  96.     }   
  97.   
  98.     public int size() {   
  99.         return size;   
  100.     }   
  101.   
  102.     //取树的最左边的节点   
  103.     public int getLast() {   
  104.         Entry e = root;   
  105.         while (e.right != null) {   
  106.             e = e.right;   
  107.         }   
  108.         return e.elem;   
  109.     }   
  110.   
  111.     //测试   
  112.     public static void main(String[] args) {   
  113.   
  114.         //创建一个满二叉树   
  115.         BinTree binTree = new BinTree();   
  116.         binTree.createFullBiTree(15);   
  117.         System.out.println(binTree.size());//15   
  118.         System.out.println(binTree.getLast());//15   
  119.   
  120.         //创建一个完全二叉树   
  121.         binTree = new BinTree();   
  122.         binTree.createFullBiTree(14);   
  123.         System.out.println(binTree.size());//14   
  124.         System.out.println(binTree.getLast());//7   
  125.   
  126.         //创建一棵非完全二叉树   
  127.         binTree = new BinTree();   
  128.         int[] nums = new int[] { 123400506000078 };   
  129.         binTree.createBinTree(nums);   
  130.         System.out.println(binTree.size());//8   
  131.         System.out.println(binTree.getLast());//8   
  132.   
  133.     }   
  134. }   
package tree.bintree;

/**
 * 创建 非完全二叉树、完全二叉树、满二叉树
 *
 * 由于二叉树的节点增加没有什么规则,所以这里只是简单的使用了递一
 * 次性把整棵树创建出来,而没有设计出一个一个添加节点的方法与删除
 * 
 * @author jzj
 * @date 2009-12-23
 */
public class BinTree {// Bin=Binary(二进位的, 二元的)

	protected Entry root;//根
	private int size;//树的节点数

	/**
	 * 树的节点结构
	 * @author jzj
	 * @date 2009-12-23
	 */
	protected static class Entry {
		int elem;//数据域,这里我们作为编号
		Entry left;//左子树
		Entry right;//右子树

		public Entry(int elem) {
			this.elem = elem;
		}

		public String toString() {
			return " number=" + elem;
		}
	}

	/**
	 * 根据给定的节点数创建一个完全二叉树或是满二叉树
	 * @param nodeCount 要创建节点总数
	 */
	public void createFullBiTree(int nodeCount) {
		root = recurCreateFullBiTree(1, nodeCount);
	}

	/**
	 * 递归创建完全二叉树
	 * @param num 节点编号
	 * @param nodeCount 节点总数
	 * @return TreeNode 返回创建的节点
	 */
	private Entry recurCreateFullBiTree(int num, int nodeCount) {
		size++;
		Entry rootNode = new Entry(num);//根节点
		//如果有左子树则创建左子树
		if (num * 2 <= nodeCount) {
			rootNode.left = recurCreateFullBiTree(num * 2, nodeCount);
			//如果还可以创建右子树,则创建
			if (num * 2 + 1 <= nodeCount) {
				rootNode.right = recurCreateFullBiTree(num * 2 + 1, nodeCount);
			}
		}
		return (Entry) rootNode;
	}

	/**
	 * 根据给定的数组创建一棵树,这个棵树可以是完全二叉树也可是普通二叉树
	 * 数组中为0的表示不创建该位置上的节点
	 * @param nums 数组中指定了要创建的节点的编号,如果为0,表示不创建
	 */
	public void createBinTree(int[] nums) {
		root = recurCreateBinTree(nums, 0);
	}

	/**
	 * 递归创建二叉树
	 * @param nums 数组中指定了要创建的节点的编号,如果为0,表示不创建
	 * @param index 需要使用数组中的哪个元素创建节点,如果为元素为0,则不创建
	 * @return TreeNode 返回创建的节点,最终会返回树的根节点
	 */
	private Entry recurCreateBinTree(int[] nums, int index) {
		//指定索引上的编号不为零上才需创建节点
		if (nums[index] != 0) {
			size++;
			Entry rootNode = new Entry(nums[index]);//根节点
			//如果有左子树则创建左子树
			if ((index + 1) * 2 <= nums.length) {
				rootNode.left = (Entry) recurCreateBinTree(nums, (index + 1) * 2 - 1);
				//如果还可以创建右子树,则创建
				if ((index + 1) * 2 + 1 <= nums.length) {
					rootNode.right = (Entry) recurCreateBinTree(nums, (index + 1) * 2);
				}
			}
			return (Entry) rootNode;
		}
		return null;

	}

	public int size() {
		return size;
	}

	//取树的最左边的节点
	public int getLast() {
		Entry e = root;
		while (e.right != null) {
			e = e.right;
		}
		return e.elem;
	}

	//测试
	public static void main(String[] args) {

		//创建一个满二叉树
		BinTree binTree = new BinTree();
		binTree.createFullBiTree(15);
		System.out.println(binTree.size());//15
		System.out.println(binTree.getLast());//15

		//创建一个完全二叉树
		binTree = new BinTree();
		binTree.createFullBiTree(14);
		System.out.println(binTree.size());//14
		System.out.println(binTree.getLast());//7

		//创建一棵非完全二叉树
		binTree = new BinTree();
		int[] nums = new int[] { 1, 2, 3, 4, 0, 0, 5, 0, 6, 0, 0, 0, 0, 7, 8 };
		binTree.createBinTree(nums);
		System.out.println(binTree.size());//8
		System.out.println(binTree.getLast());//8

	}
} 

(二)利用二叉树本身特点进行递归遍历(属内部遍历)

由于二叉树所具有的递归性质,一棵非空的二叉树可以看作是由根节点、左子树和右子树3部分构成,因为若能依次遍历这3部分的信息,也就遍历了整个二叉树。按照左子树的遍历在右子树的遍历之前进行的约定,根据访问根节点位置的不同,可以得到二叉的前序、中序、后序3种遍历方法。

Java代码 复制代码
  1. package tree.bintree;   
  2.   
  3. /**  
  4.  * 二叉树的三种 内部 遍历:前序、中序、后序  
  5.  * 但不管是哪种方式,左子树的遍历在右子树的遍历之前遍历是这有三种遍历方式都  
  6.  * 必须遵循的约定  
  7.  * @author jzj  
  8.  * @date 2009-12-23  
  9.  */  
  10. public class BinTreeInOrder extends BinTree {   
  11.   
  12.     /**  
  13.      * 节点访问者,可根据需要重写visit方法  
  14.      */  
  15.     static abstract class Visitor {   
  16.         void visit(Object ele) {   
  17.             System.out.print(ele + " ");   
  18.         }   
  19.     }   
  20.   
  21.     public void preOrder(Visitor v) {   
  22.         preOrder(v, root);   
  23.     }   
  24.   
  25.     /**  
  26.      * 树的前序递归遍历 pre=prefix(前缀)  
  27.      * @param node 要遍历的节点  
  28.      */  
  29.     private void preOrder(Visitor v, Entry node) {   
  30.         //如果传进来的节点不为空,则遍历,注,叶子节点的子节点为null   
  31.         if (node != null) {   
  32.             v.visit(node.elem);//先遍历父节点   
  33.             preOrder(v, node.left);//再遍历左节点   
  34.             preOrder(v, node.right);//最后遍历右节点   
  35.         }   
  36.     }   
  37.   
  38.     public void inOrder(Visitor v) {   
  39.         inOrder(v, root);   
  40.     }   
  41.   
  42.     /**  
  43.      * 树的中序递归遍历 in=infix(中缀)  
  44.      * @param node 要遍历的节点  
  45.      */  
  46.     private void inOrder(Visitor v, Entry node) {   
  47.         //如果传进来的节点不为空,则遍历,注,叶子节点的子节点为null   
  48.         if (node != null) {   
  49.             inOrder(v, node.left);//先遍历左节点   
  50.             v.visit(node.elem);//再遍历父节点   
  51.             inOrder(v, node.right);//最后遍历右节点   
  52.         }   
  53.     }   
  54.   
  55.     public void postOrder(Visitor v) {   
  56.         postOrder(v, root);   
  57.     }   
  58.   
  59.     /**  
  60.      * 树的后序递归遍历 post=postfix(后缀)  
  61.      * @param node 要遍历的节点  
  62.      */  
  63.     private void postOrder(Visitor v, Entry node) {   
  64.         //如果传进来的节点不为空,则遍历,注,叶子节点的子节点为null   
  65.         if (node != null) {   
  66.             postOrder(v, node.left);//先遍历左节点   
  67.             postOrder(v, node.right);//再遍历右节点   
  68.             v.visit(node.elem);//最后遍历父节点   
  69.         }   
  70.     }   
  71.   
  72.     //测试   
  73.     public static void main(String[] args) {   
  74.   
  75.         //创建二叉树   
  76.         int[] nums = new int[] { 123400506000078 };   
  77.         BinTreeInOrder treeOrder = new BinTreeInOrder();   
  78.         treeOrder.createBinTree(nums);   
  79.         System.out.print("前序遍历 - ");   
  80.         treeOrder.preOrder(new Visitor() {   
  81.         });   
  82.         System.out.println();   
  83.         System.out.print("中序遍历 - ");   
  84.         treeOrder.inOrder(new Visitor() {   
  85.         });   
  86.         System.out.println();   
  87.         System.out.print("后序遍历 - ");   
  88.         treeOrder.postOrder(new Visitor() {   
  89.         });   
  90.         /*  
  91.          * output:  
  92.          * 前序遍历 - 1 2 4 6 3 5 7 8   
  93.          * 中序遍历 - 4 6 2 1 3 7 5 8   
  94.          * 后序遍历 - 6 4 2 7 8 5 3 1   
  95.          */  
  96.     }   
  97. }   
package tree.bintree;

/**
 * 二叉树的三种 内部 遍历:前序、中序、后序
 * 但不管是哪种方式,左子树的遍历在右子树的遍历之前遍历是这有三种遍历方式都
 * 必须遵循的约定
 * @author jzj
 * @date 2009-12-23
 */
public class BinTreeInOrder extends BinTree {

	/**
	 * 节点访问者,可根据需要重写visit方法
	 */
	static abstract class Visitor {
		void visit(Object ele) {
			System.out.print(ele + " ");
		}
	}

	public void preOrder(Visitor v) {
		preOrder(v, root);
	}

	/**
	 * 树的前序递归遍历 pre=prefix(前缀)
	 * @param node 要遍历的节点
	 */
	private void preOrder(Visitor v, Entry node) {
		//如果传进来的节点不为空,则遍历,注,叶子节点的子节点为null
		if (node != null) {
			v.visit(node.elem);//先遍历父节点
			preOrder(v, node.left);//再遍历左节点
			preOrder(v, node.right);//最后遍历右节点
		}
	}

	public void inOrder(Visitor v) {
		inOrder(v, root);
	}

	/**
	 * 树的中序递归遍历 in=infix(中缀)
	 * @param node 要遍历的节点
	 */
	private void inOrder(Visitor v, Entry node) {
		//如果传进来的节点不为空,则遍历,注,叶子节点的子节点为null
		if (node != null) {
			inOrder(v, node.left);//先遍历左节点
			v.visit(node.elem);//再遍历父节点
			inOrder(v, node.right);//最后遍历右节点
		}
	}

	public void postOrder(Visitor v) {
		postOrder(v, root);
	}

	/**
	 * 树的后序递归遍历 post=postfix(后缀)
	 * @param node 要遍历的节点
	 */
	private void postOrder(Visitor v, Entry node) {
		//如果传进来的节点不为空,则遍历,注,叶子节点的子节点为null
		if (node != null) {
			postOrder(v, node.left);//先遍历左节点
			postOrder(v, node.right);//再遍历右节点
			v.visit(node.elem);//最后遍历父节点
		}
	}

	//测试
	public static void main(String[] args) {

		//创建二叉树
		int[] nums = new int[] { 1, 2, 3, 4, 0, 0, 5, 0, 6, 0, 0, 0, 0, 7, 8 };
		BinTreeInOrder treeOrder = new BinTreeInOrder();
		treeOrder.createBinTree(nums);
		System.out.print("前序遍历 - ");
		treeOrder.preOrder(new Visitor() {
		});
		System.out.println();
		System.out.print("中序遍历 - ");
		treeOrder.inOrder(new Visitor() {
		});
		System.out.println();
		System.out.print("后序遍历 - ");
		treeOrder.postOrder(new Visitor() {
		});
		/*
		 * output:
		 * 前序遍历 - 1 2 4 6 3 5 7 8 
		 * 中序遍历 - 4 6 2 1 3 7 5 8 
		 * 后序遍历 - 6 4 2 7 8 5 3 1 
		 */
	}
} 

(三)二叉树的非递归遍历(属外部遍历)

1、利用栈与队列对二叉树进行非递归遍历

Java代码 复制代码
  1. package tree.bintree;   
  2.   
  3. import java.util.Iterator;   
  4. import java.util.LinkedList;   
  5. import java.util.Stack;   
  6.   
  7. /**  
  8.  * 二叉树的外部遍历:深度优先(先根)、广度(层次)优先遍历  
  9.  *   
  10.  * @author jzj  
  11.  * @date 2009-12-23  
  12.  */  
  13. public class BinTreeOutOrder extends BinTree {   
  14.   
  15.     /**  
  16.      * 二叉树深序优先遍历(即二叉树的先根遍历)迭代器,外部可以使用该迭代器  
  17.      * 进行非递归的遍历,这是一种在二叉树结构外部的一种遍历算法,它没有使用  
  18.      * 二叉树本身的结构特点(左右子树递归)进行递归遍历  
  19.      * @author jzj  
  20.      */  
  21.     private class DepthOrderIterator implements Iterator {   
  22.         //栈里存放的是每个节点   
  23.         private Stack stack = new Stack();   
  24.   
  25.         public DepthOrderIterator(Entry node) {   
  26.   
  27.             //根入栈,但在放入左右子节点前会马上出栈,即根先优于左右子节点访问   
  28.             stack.push(node);   
  29.   
  30.         }   
  31.   
  32.         //是否还有下一个元素   
  33.         public boolean hasNext() {   
  34.             if (stack.isEmpty()) {   
  35.                 return false;   
  36.             }   
  37.             return true;   
  38.         }   
  39.   
  40.         //取下一个元素   
  41.         public Entry next() {   
  42.             if (hasNext()) {   
  43.                 //取栈顶元素   
  44.                 Entry treeNode = (Entry) stack.pop();//先访问根   
  45.   
  46.                 if (treeNode.right != null) {   
  47.                     stack.push(treeNode.right);//再放入右子节点   
  48.                 }   
  49.   
  50.                 if (treeNode.left != null) {   
  51.                     stack.push(treeNode.left);//最后放入左子节点,但访问先于右节点   
  52.                 }   
  53.   
  54.                 // 返回遍历得到的节点   
  55.                 return treeNode;   
  56.   
  57.             } else {   
  58.                 // 如果栈为空   
  59.                 return null;   
  60.             }   
  61.         }   
  62.   
  63.         public void remove() {   
  64.             throw new UnsupportedOperationException();   
  65.         }   
  66.   
  67.     }   
  68.   
  69.     /**  
  70.      * 向外界提供先根遍历迭代器  
  71.      * @return Iterator 返回先根遍历迭代器  
  72.      */  
  73.     public Iterator createPreOrderItr() {   
  74.         return new DepthOrderIterator(root);   
  75.     }   
  76.   
  77.     /**  
  78.      * 二叉树广度优先遍历迭代器,外部可以使用该迭代器  
  79.      * 进行非递归的遍历,这是一种在二叉树结构外部的一种遍历算法,它没有使用  
  80.      * 二叉树本身的结构特点(左右子树递归)进行递归遍历  
  81.      * @author jzj  
  82.      */  
  83.     private class LevelOrderIterator implements Iterator {   
  84.         //使用队列结构实现层次遍历,队列里存储的为节点   
  85.         private LinkedList queue = new LinkedList();   
  86.   
  87.         public LevelOrderIterator(Entry node) {   
  88.   
  89.             if (node != null) {   
  90.                 //将根入队   
  91.                 queue.addLast(node);   
  92.             }   
  93.         }   
  94.   
  95.         //是否还有下一个元素   
  96.         public boolean hasNext() {   
  97.             if (queue.isEmpty()) {   
  98.                 return false;   
  99.             }   
  100.             return true;   
  101.         }   
  102.   
  103.         //取下一个元素   
  104.         public Entry next() {   
  105.             if (hasNext()) {   
  106.                 //取栈顶迭元素   
  107.                 Entry treeNode = (Entry) queue.removeFirst();   
  108.   
  109.                 if (treeNode.left != null) {//如果有左子树,加入有序列表中   
  110.                     queue.addLast(treeNode.left);   
  111.                 }   
  112.                 if (treeNode.right != null) {//如果有右子树,加入有序列表中   
  113.                     queue.addLast(treeNode.right);   
  114.                 }   
  115.   
  116.                 // 返回遍历得到的节点   
  117.                 return treeNode;   
  118.   
  119.             } else {   
  120.                 // 如果队列为空   
  121.                 return null;   
  122.             }   
  123.         }   
  124.   
  125.         public void
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值