Spiga

树和森林(二):树的存储结构(上)

2012-06-21 12:58:28

一、多重链表表示法

  由于树中每个结点都有零个或多个孩子结点,因此,可以令每个结点包括一个结点信息域和多个指针域,每个指针域指向该结点的一个孩子结点,通过各个指针域值反映出树中各结点之间的逻辑关系。这种表示法中,树中每个结点有多个指针域,形成了多条链表,所以这种方法被称为多重链表法。

  一个树中各结点的度数各异,因此结点的指针域个数有两种设置方法。方法① 每个结点指针域个数等于该结点的度数;方法② 每个结点指针域个数等于该树的度数。对于方法①它虽然在一定程度上节约了存储空间,但由于树中各个结点的度数是不相同的,各种操作不容易实现,所以一般采用方法②来实现多重链表法。显然,该方法适用于各个结点的度相差不大的情况。
  下图为多重链表表示存储结构示意图

    

  双重链表实现树形结构的代码如下:

public class MLNode<T> {
	//存储结点的数据
	private T data;
	//存储子结点的位置
	private MLNode<T>[] childs;
   

	//初始化结点
	public MLNode(int max) {
		childs = new MLNode<T>[max];
		for (int i = 0; i < childs.Length; i++) {
			childs[i] = null;
		}
	}      
	public T Data
	{
		get { return data; }
		set { data = value; }
	}

	public MLNode<T>[] Childs{
		get { return childs; }
		set { childs = value; }
	}

}
public class MLTree<T>
{
	private MLNode<T> head;

	public MLNode<T> Head {
		get { return head; }
		set { head = value; }
	}
	public MLTree() {
		head = null;
	}
	public MLTree(MLNode<T> node) {
		head = node;
	}
	//求树的根结点,如果树非空,返回根结点,否则返回空
	public MLNode<T> Root()
	{
		return head;
	}

	//求结点t的双亲结点。如果t的双亲结点存在,返回双亲结点,否则返回空
	public MLNode<T> Parent(MLNode<T> t)
	{
		MLNode<T> tmp = head;
		if (IsEmpty() || t == null) return null;
		if (tmp.Data.Equals(t.Data)) return null;

		Queue que = new Queue();
		que.Enqueue(tmp);
		while (que.Count > 0)
		{
			tmp = (MLNode<T>)que.Dequeue();

			for (int i = 0; i < tmp.Childs.Length; i++)
			{
				if (tmp.Childs[i] != null)
				{
					if (tmp.Childs[i].Data.Equals(t.Data))
					{
						return tmp;
					}
					else
					{
						que.Enqueue(tmp.Childs[i]);
					}
				}
			}
		}
		return null;
	}

	//求结点t的第i个子结点。如果存在,返回第i个子结点,否则返回空
	//i=0时表示求第1个子结点
	public MLNode<T> Child(MLNode<T> t, int i)
	{
		if (t!=null && i < t.Childs.Length)
		{
			return t.Childs[i];
		}
		else {
			return null;
		}

	}

	//求结点t第一个右边兄弟结点。如果存在,返回第一个右边兄弟结点,否则返回空
	public MLNode<T> RightSibling(MLNode<T> t)
	{
	 
		MLNode<T> pn = Parent(t);
		if (pn != null)
		{
			//查找亲兄弟
		   int i = FindRank(t);
		   return  Child(pn, i + 1);      
	   }
		else
		{
			return null;
		}
	}

	//把以s为头结点的树插入到树中作为结点t的第i棵子树。成功返回true,否则返回false
	public bool Insert(MLNode<T> s, MLNode<T> t, int i)
	{
		if (IsEmpty()) head = t;

		t = FindNode(t);
		if (t != null && t.Childs.Length > i)
		{
			t.Childs[i] = s;
			return true;
		}
		else { 
			return false;
		}         
	}

	//删除结点t的第i棵子树。成功返回第i棵子树的根结点,否则返回空
	public MLNode<T> Delete(MLNode<T> t, int i)
	{
		t = FindNode(t);
		MLNode<T> node = null;
		if (t != null && t.Childs.Length >i) {
			node = t.Childs[i];
			t.Childs[i] = null;
		}
		return node;
	}

	//后序遍历树结点
	public void PostOrder(MLNode<T> root) {
	   
		if (root == null)
		{            
			return;
		}
		//按先序访问子树结点 
		for (int i = 0; i < root.Childs.Length; i++)
		{
			if (root.Childs[i] != null)
			{
				PostOrder(root.Childs[i]);
			}
		}
		//访问根结点
		Console.Write(root.Data + "  ");
	}      
	//先序遍历树结点
	public void PreOrder(MLNode<T> root)
	{

		if (root == null)
		{
			return;
		}
		//访问根结点
		Console.Write(root.Data + "  ");
		//按先序访问子树结点 
		for (int i = 0; i < root.Childs.Length; i++)
		{
		   
			 PreOrder(root.Childs[i]);
		   
		}
	
	} 
   
	//层序遍历
	public void BroadOrder(MLNode<T> root)
	{
		Console.WriteLine("遍历开始:");
		if (root == null)
		{
			Console.WriteLine("没有结点!");
			return;
		}

		MLNode<T> tmp = root;

		Queue que = new Queue();
		//根结点入队列
		que.Enqueue(tmp);
		while (que.Count > 0)
		{
			//结点出队列并访问
			tmp = (MLNode<T>)que.Dequeue();
		
			Console.Write(tmp.Data + "  ");
			for (int i = 0; i < tmp.Childs.Length; i++)
			{
				if (tmp.Childs[i] != null)
				{
					//各个子结点入队列
					que.Enqueue(tmp.Childs[i]);
				}
			}
		}
		Console.WriteLine("遍历结束.");
	}
	//按某种方式遍历树
	//0:先序
	//1:后序
	//2:层序
	public void Traverse(int TraverseType)
	{
		if (TraverseType == 0) PreOrder(head);
		else if (TraverseType == 1) PostOrder(head);
		else BroadOrder(head);

	}

	//清空树
	public void Clear()
	{
		head = null;
	}

	//判断树是否为空树。如果是空树,返回true,否则返回false
	public bool IsEmpty()
	{
		return head == null;
	}

	//求树的深度。如果树不为空,返回树的层次,否则返回0。
	public int GetDepth()
	{
		return 0;
	}

	//查找结点t在兄弟中的排行,成功时返回位置,否则返回-1
	public int FindRank(MLNode<T> t)
	{
		MLNode<T> pn = Parent(t);
	   for (int i = 0; i < pn.Childs.Length; i++)
		{
			MLNode<T> tmp = pn.Childs[i];
			if (tmp != null && tmp.Data.Equals(t.Data))
			{
				return i;
			}
		}
		return -1;
	}

	//查找在树中的结点t,成功时返回位置,否则返回null
	public MLNode<T> FindNode(MLNode<T> t)
	{
		if (head.Data.Equals(t.Data)) return head;
		MLNode<T> pn = Parent(t);
		foreach(MLNode<T> tmp in pn.Childs)
		{
			 if (tmp != null && tmp.Data.Equals(t.Data))
			{
				return tmp;
			}
		}
		return null;
	}

	public static void TestMLTree() {
		MLTree<string> tr = new MLTree<string>();
		char ch;
		do
		{
			Console.WriteLine("1. 添加结点");
			Console.WriteLine("2. 删除结点");
			Console.WriteLine("3. 遍历树");
			Console.WriteLine("5. 退出");
			Console.WriteLine();
			ch = Convert.ToChar(Console.ReadLine());
			switch (ch)
			{
				case '1':
					Console.WriteLine("输入父结点:");
					string str = Convert.ToString(Console.ReadLine());
					MLNode<string> pn = new MLNode<string>(4);
					pn.Data = str;
					Console.WriteLine("输入子结点:");
					str = Convert.ToString(Console.ReadLine());
					MLNode<string> cn = new MLNode<string>(4);
					cn.Data = str;
					Console.WriteLine("输入插入子结点的位置:");
					int i = Convert.ToInt32(Console.ReadLine());
					bool ok = tr.Insert(cn, pn, i);
					if (ok) Console.WriteLine("插入{0}成功",cn.Data);
					break;
				case '2':
					Console.WriteLine("输入要删除的结点:");
					 str = Convert.ToString(Console.ReadLine());
					 pn = new MLNode<string>(4);
					 pn.Data = str;
					 tr.Delete(pn, 0);
					break;
				 case '3':
				   tr.Traverse(2);
				   break;
			}
		} while (ch != '5');
	}
}

**二、双亲表示法
**  双亲表示法利用树中每个结点的双亲唯一性,在存储结点信息的同时,为每个结点附设一个指向其双亲的指针parent,惟一地表示任何-棵树。
  下图的双亲表示如下面数组所示。
  
  分析:
  E和F所在结点的双亲域是1,它们的双亲结点在向量中的位置是1,即B是它们的双亲。
  注意:
  ① 根无双亲,其parent域为-1。
  ② 双亲表示法中指针parent向上链接,适合求指定结点的双亲或祖先(包括根);求指定结点的孩子或其它后代时,可能要遍历整个数组。

  双亲实现树形结构的代码如下:

public class PNode<T>
{
	private T data;
	private int parent;
	public PNode(T val,int pos) {
		data = val;
		parent = pos;
	}
	public PNode(PNode<T> node)
	{
		data = node.data;
		parent = node.parent;
	}
	//结点的数据
	public T Data {
		get { return data; }
		set { data = value; }
	}
	//指向结点的父结点位置
	public int Parent {
		get { return parent; }
		set { parent = value; }
	}
}


public class PTree<T>
{

	//用于存储树的结点信息
	private PNode<T>[] nodes;
	private int maxsize;
	private int num;    //指示树中已存储的结点数
	//构造器
	public PTree(int size)
	{
		nodes = new PNode<T>[size];
		maxsize = size;
		num = 0;
	}
	//索引器
	public PNode<T> this[int index]
	{
		get
		{
			return nodes[index];
		}
		set
		{
			nodes[index] = value;
		}
	} 
   int Find(PNode<int>[] a,int x){
		/*在数组a中查找值为x的元素所属的集合,*/
		/*若找到,返回树根结点在数组a中的序号;否则,返回-1*/

		int i,j;
		i=0;
		while (i<a.Length && a[i].Data!=x) i++;
		if (i>=a.Length) return -1;    /*值为x的元素不属于该组集合,返回-1*/
		j=i;
		while (a[j].Parent!=-1) j=a[j].Parent;
		return j;       /*j为该集合的树根结点在数组a中的序号*/
	}


	//求树的根结点,如果树非空,返回根结点,否则返回空
	public PNode<T> Root()
	{
		if (!IsEmpty())
		{
			PNode<T> t = Parent(this[0]);
			while (t.Parent != -1) {
				t = Parent(t);
			}
			return t;
		}
		else
		{
			return null;
		}
	}

	//求结点t的双亲结点。如果t的双亲结点存在,返回双亲结点,否则返回空
	public PNode<T> Parent(PNode<T> t)
	{
		if (t != null && t.Parent != -1)
		{
			return nodes[t.Parent];
		}
		else {
			return null;
		}
	}
	//求结点t的第1个子结点。如果存在,返回第1个子结点,否则返回空
	public PNode<T> Child(PNode<T> t)
	{
		//查找结点t的存储位置
		int pos = FindNode(t);
		if (pos != -1)
		{
			//查找Parent值为pos的结点              
			for (int k = 0; k < nodes.Length; k++)
			{
				if (pos == nodes[k].Parent)
				{
					return nodes[k];
				}
			}
		}
	
		return null;          
	}

	//求结点t的第i个子结点。如果存在,返回第i个子结点,否则返回空
	//i=0时表示求第1个子结点
	public PNode<T> Child(PNode<T> t, int i)
	{
		/*
		 * 在用数组存储的双亲法表示中,
		 * 假定按树的层次结构从左到右的顺序编号并存储  
		 * 因此第i个子结点为第1个子结点的位置+i
		 */

		//求t的第1个子结点
		PNode<T> cn1 = Child(t);
	
		//查找结点t第一个子结点的存储位置

		int pos = FindNode(cn1);
		if (pos != -1)
		{
			//第i个子结点为第一个子结点的位置+i
			if (this[pos + i].Parent == cn1.Parent)
				return this[pos + i];              
		}
	
		return null;         
	
	}

	//求结点t第一个右边兄弟结点。如果存在,返回第一个右边兄弟结点,否则返回空
	public PNode<T> RightSibling(PNode<T> t) 
	{
		/*
		 * 在用数组存储的双亲法表示中,
		 * 假定按树的层次结构从左到右的顺序编号并存储  
		 * 因此第一个右边兄弟结点的位置即为t结点的位置+1
		 */
		int pos = FindNode(t);
		if (pos != -1)
		{
			if (nodes[pos + 1].Parent == t.Parent)
			{
				return nodes[pos + 1];
			}
		}

		return null;          
	}

	//把以s为头结点的树插入到树中作为结点t的第i棵子树。成功返回true,否则返回false
	public bool Insert(PNode<T> s,PNode<T> t,int i)
	{
		//如果为空,则将s作为树的根结点
		if (IsEmpty())
		{
			s.Parent = -1;
			nodes[num] = s;
		}
		else {
			PNode<T> cn = Child(t, i);
			if (cn != default(PNode<T>))
			{
				//存在第i个子结点
				int pos = FindNode(cn);                  
				MoveBack(pos);
				pos = FindNode(t);
				if (pos == -1) return false;
				s.Parent = FindNode(t);
				nodes[pos] = s;
				return true;
			}
			else { 
		
			}
		}

		return true;
	}

	//删除结点t的第i棵子树。成功返回第i棵子树的根结点,否则返回空
	public PNode<T> Delete(PNode<T> t,int i)
	{
		PNode<T> cn = Child(t, i);
				
		int n = GetNumOfChild(cn);
		if (n != 0)
		{
			for (; n > 0; n--)
			{
				Delete(cn, n - 1);
			}
		}
		else
		{            
			//是叶子结点
			int pos = FindNode(cn);
			if (pos != -1)
			{
				PNode<T> node = new PNode<T>(nodes[pos]);
				//删除结点时,将结点后的结点前移一个位置
				for (; pos < num; pos++)
				{
					nodes[pos] = nodes[pos + 1];
				}
				num--;
				return node;
			}             
		}
		return null;
	
	}

	//按某种方式遍历树
	//0:先序
	//1:中序
	//2:后序
	public void Traverse(int TraverseType)
	{
	}

	//清空树
	public void Clear()
	{
		num = 0;
	}

	//判断树是否为空树。如果是空树,返回true,否则返回false
	public bool IsEmpty()
	{
		return num == 0;
	}

	//求树的深度。如果树不为空,返回树的层次,否则返回0。
	public int GetDepth()
	{
		return 0;
	}

	//查找结点t在存储中的位置,成功时返回位置,否则返回-1
	public int FindNode(PNode<T> t) 
	{
		if (t != null) //判断结点是否为空
		{
			for (int i = 0; i < nodes.Length; i++)
			{
				//结点数据相同且父结点相同
				if (this[i].Data.Equals(t.Data) && this[i].Parent == t.Parent)
				{
					return i;
				}
			}
		}
		return -1;
	}
	//获取结点t的子结点数量
	public int GetNumOfChild(PNode<T> t) {
		int pos = FindNode(Child(t));
		int n=0;
		if (pos != -1) { 
			n=1;
			while (nodes[pos].Parent == nodes[++pos].Parent) n++;
		}
		return n;
	}

	//前移一个位置
	private void MoveForward(int pos) {
		for (; pos < num; pos++) {
			nodes[pos] = nodes[pos + 1];
		}
	}
	//后移一个位置
	private void MoveBack(int pos)
	{
		for (int i=num; i>pos; i--)
		{
			nodes[i] = nodes[i-1];
		}
	}
}