数据结构 各种树的定义及遍历方法

Evelyn

QQ: 1809335179

假设树有n层,

有序树:子树有序,不可交换

完全二叉树:除了第n层和n-1外,其他各层的子结点数都达到最大值,即2^(n-1),且第n-1层的子结点数都集中在最左边。一般采用顺序存储结构。

满二叉树:除了第n层外,所有层的子结点数都达到最大值,即2^(n-1)个

二叉排序树:若左子树不为空时,左子树结点的值都小于根结点;右子树不为空时,右子树的值都大于跟节点;

二叉树的第 n层最多有2^(n-1)个子结点,整个二叉树最多有2^-1个子结点。任何一颗二叉树,度为 2 的子结点个数为k个,则度为 0 的子结点个数为k+1个。

红黑树:一种自平衡二叉查找树(平衡二叉树包括红黑树,AVL,SBT,伸展树,Treap),主要用来实现关联数组,他的查找、插入、删除操作时间复杂度均为log(n),进行这些操作后需要对数进行旋转,保证其平衡性,任何不平衡都会在三次旋转之内解决,但红黑树不追求完全平衡,典型的JDK 提供的集合类 TreeMap 本身就是一个红黑树的实现。 

AVL树:是一种绝对的平衡二叉树,查找、插入、删除操作性能与红黑树一样。

一棵AVL树满足以下的条件: 
1>它的左子树和右子树都是AVL树 
2>左子树和右子树的高度差不能超过1 
性质: 
1>一棵n个结点的AVL树的其高度保持在0(log(n)),不会超过3/2log(n+1) 
2>一棵n个结点的AVL树的平均搜索长度保持在0(log(n)). 
3>一棵n个结点的AVL树删除一个结点做平衡化旋转所需要的时间为0(log(n)). 

堆排序:大根堆和小根堆(要求是完全二叉树),大根堆(小根堆)任何一非叶节点的关键字不小于(不大于)其左右孩子节点的关键字。

前序遍历、中序遍历,后序遍历

前序遍历:根结点->左子树->右子树

中序遍历:左子树->根结点->右子树

后续遍历:左子树->右子树->根结点

void CreatTree(TreeNode* &tree, char *data,int length,int index) {//创建二叉树
	if (index>=length)
		return ;
	tree = new TreeNode;
	tree->data = data[index];
	tree->left = NULL;
	tree->right = NULL;
	CreatTree(tree->left, data, length, index*2+1 );//每一个子结点的index是根节点的2倍加1(左结点),2倍加2(右结点)
	CreatTree(tree->right, data, length, index*2+2);
}
 
 

 
 

<pre name="code" class="cpp">typedef struct TreeNode {  //结点结构
	struct TreeNode *left;
	struct TreeNode *right;
	char data;
}TreeNode;
void preOrder(TreeNode *node) {  // 前序遍历
	if (node == NULL)
		return;
	printf("%c", node->data);
	preOrder(node->left);
	preOrder(node->right);
}

void inOrder(TreeNode *node) {  // 中序遍历
	if (node == NULL)
		 return;
	inOrder(node->left);
	printf("%c", node->data);
	inOrder(node->right);
}
void postOrder(TreeNode *node) {  // 后序遍历
	if (node == NULL)
		 return;
	postOrder(node->left);
	postOrder(node->right);
	printf("%c", node->data);
}
 
 

非递归遍历方式;

void PreOrderNoRecurse(TreeNode* root) {
	if (root == NULL)
		return;
	stack<TreeNode*> s;
	TreeNode* p = root;
	while (p != NULL || !s.empty()) {
		while (p) {
			s.push(p);
			cout << p->data; //输出子树的根结点
			p = p->left;
		}
		if(!s.empty()) { //这个用if而不用while,因为遍历完左节点直到为空后,先搜索当前结点的右结点,再判断这个右结点是否还存在左结点
			p = s.top();
			s.pop();
			p= p ->right;
		}
	}
}

void inOrderNoRecurse(TreeNode* root) {
	if (root == NULL)
		return;
	stack<TreeNode*> s;
	TreeNode* p = root;
	while (p != NULL || !s.empty()) {
		while (p) {
			s.push(p);
			p = p->left;
		}
		
		if (!s.empty()) {
			p = s.top();
			s.pop();  //这一部分要写在这个循环内部,因为在根结点pop出以后栈为空,会是一个死循环。
			cout << p->data; //跟前序遍历的区别,遍历完左结点后输出最后一个左结点,当前结点的左右节点都为空后从栈中回退节点
			p = p->right;
		}
	}
}

void postOrderNoRecurse(TreeNode* root) {
	if (root == NULL)
		return;
	stack<TreeNode*> s;
	TreeNode* p = root;
	s.push(root);
	TreeNode* pre = NULL;
	TreeNode* temp;
	while ( !s.empty()) {
		temp = s.top();
		if ((temp->left == NULL && temp->right == NULL) || (pre != NULL && (pre == temp->left || pre == temp->right))) {
			//当前结点左右结点为NULL或pre结点为当前结点的左结点或右结点时输出此结点
			//因为是按右结点左结点的顺序压入栈的,所以这个判断说明已经没有子节点或者子节点已遍历完成并输出
			cout << temp->data;
			pre = temp;
			s.pop();
		}
		else
		{			
			if (temp->right)
				s.push(temp->right);//这里是先压入右结点再压入左结点,因为栈是先入后出
			if (temp->left)
				s.push(temp->left);
		}
	}
}



不同遍历方式之间的相互转换

int PostOrderingfromPreMid(char* pre, char* mid, int length) { 
/*已知前序遍历和中序遍历,得到后序遍历,在中序遍历中找到与前序遍历的头结点相等的结点 i ,则该结点的左边为左子树,结点个数为 i个, 右边为右子树,结点个数为 length-i-1个,此时在前序遍历中位置[1,i]为左子树,[i+1,length-1]为右子树,然后递归,因为后续遍历最后输出头节点,所以cout在最后。*\
int PostOrderingfromPreMid(char* pre, char* mid, int length) {
	if (length <= 0)
		return 0;
	char temp = pre[0];
	int i = 0;
	for (; i < length; i++) {
		if (mid[i] == temp)
			break;
	}
	PostOrderingfromPreMid(pre + 1, mid, i);
	PostOrderingfromPreMid(pre + i + 1, mid + i + 1, length - i - 1);
	cout << temp;
	return 1;
}

int PreOrderingfromMidPost(char* mid, char* post, int length) {
/*已知中序遍历和后续遍历,求出前序遍历,在中序遍历中找到与后序遍历的尾结点相等的结点  i (此结点为当前子树的根节点),则该结点的左边为左子树,结点个数为 i个, 右边为右子树,结点个数为 length-i-1个,此时在后序遍历中位置[0,i-1]为左子树,[i,length-1-1]为右子树,然后递归,因为前续遍历最先输出头节点,所以cout在最前<*/
           if (length <= 0)
		return 0;
	char temp = post[length-1];
	int i = 0;
	for (; i < length; i++) {
		if (mid[i] == temp)
			break;
	}
	cout << temp;
	PreOrderingfromMidPost(mid, post,  i);
	PreOrderingfromMidPost(mid +i+1, post +  i, length - i - 1);
	return 1;
}

已知前序遍历和中序遍历可以唯一确定一个二叉树;

已知后续遍历和中序遍历可以唯一确定一个二叉树;

但是已知前序遍历和后序遍历不能唯一确定一个二叉树;

 
 
 
 

不要特意记结点的元素,只要记清楚三个遍历的顺序,根据前后遍历跟结点在两端的特点,找出其在中序遍历的位置,然后可以知道左右子树的结点个数,然后递归就可以了。



void TraversebyLayer(TreeNode* tree) {//层序遍历
	if (tree == NULL)
		return;
	vector<TreeNode*> node;
	node.push_back(tree);
	int count = 0; //记录当前遍历的节点在vector中的位置
	//int t = node.size();
	while (count < node.size()) {
		if (node[count]->left)
			node.push_back(node[count]->left);
		if (node[count]->right)
			node.push_back(node[count]->right);
		count++;
	}
	for (int i = 0; i < node.size(); i++)
		cout << node[i]->data;
	cout << endl;
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值