树的学习——(递归构建二叉树、递归非递归前序中序后序遍历二叉树、根据前序序列、中序序列构建二叉树)

前言

最近两个星期一直都在断断续续的学习二叉树的数据结构,昨晚突然有点融汇贯通的感觉,这里记录一下吧

题目要求

给定前序序列,abc##de#g##f###,构建二叉树,并且用递归和非递归两种方法去做前序,中序和后序遍历

二叉树的数据结构

/**
 * 定义二叉树的数据结构
 */
struct btree
{
	char item;
	struct btree *lchild;
	struct btree *rchild;
};
typedef struct btree* bt;

/**
 * 定义栈数据结构(用于非递归遍历)
 */
struct seqstack
{
	bt data[maxsize];
	int top;
};
typedef struct seqstack* stack;

递归构建二叉树

全局数组、全局变量构建二叉树

char str[101] = "abc##de#g##f###";
int count = 0;
bt createBintree()
{
	bt t;
	if(str[count ++] == '#')
	{
		//回溯条件
		t = NULL;
	}else if(str[count - 1] == '\0')
	{
		//终止条件
		t = NULL;
	}
	else
	{
		t = (bt)malloc(sizeof(struct btree));
		t->item = str[count - 1];
		t->lchild = createBintree();
		t->rchild = createBintree();
	}
	return t;
}

递归的前序、中序、后序算法(c)

/**
 * Description:递归前序遍历
 */
void recPreorder(bt root)
{
	if(root)
	{
		printf("%c ", root->item);
		recPreorder(root->lchild);
		recPreorder(root->rchild);
	}else
	{
		return;
	}
}

/**
 * Description:递归中序遍历
 */
void recInorder(bt root)
{
	if(root)
	{
		recInorder(root->lchild);
		printf("%c ", root->item);
		recInorder(root->rchild);
	}else
	{
		return;
	}
}

/**
 * Description:递归后序遍历
 */
void recPostorder(bt root)
{
	if(root)
	{
		recPostorder(root->lchild);
		recPostorder(root->rchild);
		printf("%c ", root->item);
	}else
	{
		return;
	}
}

非递归遍历算法(前序,中序)

void initStack(stack s)
{
	s->top = -1;
}

void pushStack(stack s, const bt t)
{
	if(s->top <= maxsize)
	{
		s->data[++ (s->top)] = t;
	}
}

bt popStack(stack s)
{
	if(s->top >= 0)
	{
		return s->data[(s->top) --];
	}else
	{
		printf("栈空了\n");
	}
}

/**
 * Description:非递归前序遍历
 */
void preorderTraverse(bt root)
{
	stack s = (struct seqstack *)malloc(sizeof(struct seqstack));
	initStack(s);
	bt p = root;
	while(s->top >= 0 || p)
	{
		if(p)
		{
			printf("%c ", p->item);
			pushStack(s, p);
			p = p->lchild;
		}else
		{
			p = popStack(s);
			p = p->rchild;
		}
	}
	printf("\n");
}

/**
 * Description:非递归中序遍历
 */
void inorderTraverse(bt root)
{
	stack s = (struct seqstack *)malloc(sizeof(struct seqstack));
	initStack(s);
	bt p  = root;

	while(s->top >=0 || p)
	{
		if(p)
		{
			pushStack(s, p);
			p = p->lchild;
		}else
		{
			p = popStack(s);
			printf("%c ", p->item);
			p = p->rchild;
		}
	}
}

非递归遍历算法(后序)

算法思想:

  1. 首先,也是找个最左边的叶子结点并把路上遇到的节点依次入栈
  2. 然后,弹出栈顶元素(该元素为最左边的叶子),判断(1)它是否有右节点(2)如果有右节点,是否被访问过。如果满足(1)有右节点并且(2)右节点没有访问过,说明这是后序遍历的相对根节点,因此需要将这个节点再次入栈,并且它的右节点入栈,然后重新执行第一步。否则,就访问该节点,并且设置pre为此节点,同时把将遍历节点附空值,访问进入无限循环

算法代码(c语言):

/**
 * Description:非递归后序遍历
 */
void postorderTraverse(bt root)
{
	stack s = (struct seqstack *)malloc(sizeof(struct seqstack));
	initStack(s);
	bt pre, p;
	p = root;
	//记录前一个访问节点
	pre = NULL;

	while(s->top >= 0 || p)
	{
		if(p)
		{
			pushStack(s, p);
			p = p->lchild;
		}else
		{
			p = popStack(s);
			if(p->rchild != NULL && p->rchild != pre)
			{
				//如果右子树非空且没有被访问过,则p为相对根节点
				pushStack(s, p);
				p = p->rchild;
			}else
			{
				printf("%c ", p->item);
				pre = p;
				//设置p为null,才能将上层节点出栈,访问无限循环
				p = NULL;
			}
		}
	}
}


注意:

严蔚敏的<<数据结构>>上有一段话很经典,摘录如下:”从二叉树遍历的定义可知,三种遍历算法之不同处仅在于访问根节点和遍历左、右子树的先后关系。如果在算法中暂且抹去和递归无关的visit语句,则三个遍历算法完全相同。因此,从递归执行过程的角度来看,前序、中序、后序遍历也完全相同。“ 这段话给我们的提示就是,前序、中序、后序遍历的算法相同,只是printf()语句位置而已。

根据前序序列、中序序列构建二叉树

函数定义

bt rebuildTree(char *pre, char *in, int len);

参数:
* pre:前序遍历结果的字符串数组
* in:中序遍历结果的字符串数组
len : 树的长度
例如:
前序遍历结果: a b c d e f g h
中序遍历结果: c b e d f a g h

算法思想

  1. 递归思想,递归的终止条件是树的长度len == 0
  2. 在中序遍历的数组中找到前序数组的第一个字符,记录在中序数组中的位置index.如果找不到,说明前序遍历数组和中序遍历数组有问题,提示错误信息,退出程序即可;找到index后,新建一个二叉树节点t,t->item = *pre,然后递归的求t的左孩子和有孩子
  3. 递归的左孩子:void rebuildTree(pre + 1, in, index)
  4. 递归的右孩子:void rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1))

实现代码(c语言版)

/**
 * Description:根据前序和中序构建二叉树
 */
bt rebuildTree(char *pre, char *in, int len)
{
	bt t;
	if(len <= 0)
	{ 
		//递归终止
		t = NULL;
	}else
	{ 
		//递归主体
		int index = 0;
		
		while(index < len && *(pre) != *(in + index))
		{
			index ++;
		}
		
		if(index >= len)
		{
			printf("前序遍历或者中序遍历数组有问题!\n");
			exit(-1);
		}
		
		t = (struct bintree *)malloc(sizeof(struct bintree));
		t->item = *pre;
		t->lchild = rebuildTree(pre + 1, in, index);
		t->rchild = rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1));
	}
	return t;
}

递归清理二叉树

复习了c语言的内存分配,参考链接:http://blog.csdn.net/zinss26914/article/details/8687859, 要点就是malloc分配的堆内存一定要判断是否可以,并且用完后应该手动释放,这里写一下递归清理二叉树的代码

/**
 * Description:递归清理二叉树结点
 */
void clean_tree(struct btree *t)
{
	if (t) {
		clean_tree(t->lchild);
		clean_tree(t->rchild);
		free(t);
	}
}


后记

2012年今天是最后一天了,哈哈,终于把二叉树该掌握的部分都掌握了,还是不错的,期待新的一年2013年有更多的收获,2013年可能又是我人生发生抉择和变化的一年,我依然会坚持自己的价值观,踏踏实实的走下去,现在我学会最多的就是坚持,坚忍,光说不做是没有的,写程序如此,做人亦如此!
posted @ 2012-12-29 18:46  java程序员填空  阅读(450)  评论(1编辑  收藏  举报