C++ | Tree的实现(适用于一般的树)

29 篇文章 5 订阅
27 篇文章 1 订阅
  • 其实主要是采用类似c这种面向过程的语言的接口实现办法,用c++更多只是为了调用队列,栈等一些接口
  • 网上资料大部分树的实现都是二叉树,而实际常见的树一般是普通的树
  • 所以我们要学会通过二叉树的思想来转换到普通的树来使用
  • 本文采用的存储结构为(孩子-兄弟)结点表示法,也叫二叉链表,有不懂得地方可以查看我的另外一篇关于树的存储结构的文章
  • 本文重点讨论三个函数,其他实现函数都大同小异
    • 求树的深度
    • 打印树的路径
    • 如何创建一颗普通的树

树的存储结构:

typedef char ElemType;

typedef struct CSNode {
	ElemType data;
	struct CSNode *firstchild, *nextsibling;	
}CSNode, *CSTree;

求树的深度:

unsigned int TreeDepth(CSTree T) {
	if (T) {
		return (1+TreeDepth(T->firstchild))>TreeDepth(T->nextsibling)?(1+TreeDepth(T->firstchild)):TreeDepth(T->nextsibling);
	}		
	return 0;
}

方法基本类似二叉树,只是一点打印兄弟结点的时候因为兄弟结点的根节点是他的兄弟,所以深度应该去掉那个根节点

树的路径打印

void AllPath(CSTree T, stack<ElemType> *S1) {
	//stack<ElemType> S1;
	
	while (T != NULL) {
		S1->push(T->data);
		if (T->firstchild == NULL) {
			stack<ElemType> S2;
			S2 = (*S1);
			while(!S2.empty()) {
				printf("%c\n",S2.top());
				S2.pop();
			}
		}
		else {
			AllPath(T->firstchild, S1);
		}
	S1->pop();
	T=T->nextsibling;	
	}	
}

这里要说一下,虽然思路不难想,自己画一下图便很容易想通,但是我debug de了很久,而且一度坚持认为自己没错,归根到底还是递归用得不熟练,一开始把栈作为形参只是在函数内创建,所以每一遍递归栈都会刷新一遍而不会保留下来,所以要创建一个指向栈的指针,使得每次递归栈的信息保存下来

创建树

  • 虽然这个内容是前面连个的基础,但第一步往往是最难的,这个也不例外
  • 采取的思路是根节点孩子节点对来创建
    这里写图片描述
  • 运行时直接输入#AABACADCECFFG##即可,注意每一对之间不要加空格,因为空格会被识别为其他的字符
  • 因为这种存储结构较为复杂,故采用递归思路可能会导致创建混乱,利用队列来创建
void CreateTree(CSTree *T) {
	(*T) = NULL;
	ElemType Felem;
	ElemType Selem;
	CSNode *r = NULL; //r为指向当前结点的指针
	CSNode *p = NULL; //p为指向队列front的指针
	queue<CSNode*> Q;
	for (scanf("%c%c",&Felem,&Selem); Selem != '#'; scanf("%c%c",&Felem,&Selem)) {
		CSNode *node;
		node = (CSNode*)malloc(sizeof(CSNode));
		node -> data= Selem;
		node -> firstchild = NULL;
		node -> nextsibling = NULL;
		Q.push(node);
		if (Felem == '#') {
			(*T) = node;
			r = node;
		}
		else {
			while(Q.front()->data != Felem) {
				Q.pop();
			}
			p = Q.front();
			if (!(p->firstchild)) {
				p->firstchild = node;
				r = node;
			}
			else {
				r->nextsibling = node;
				r = node;
			}
		}
	}
}

所有代码参考如下:

Tree.hpp

// - - - - - 采用二叉链表的表示方法 - - - - -
#ifndef _TREE_H_
#define _TREE_H_
//#include <stdbool.h>
#include <iostream>
#include <stack>
using namespace std;

typedef char ElemType;

typedef struct CSNode {
	ElemType data;
	struct CSNode *firstchild, *nextsibling;	
}CSNode, *CSTree;

void CreateTree(CSTree *T);

void DestroyTree(CSTree *T);

unsigned int TreeDepth(CSTree T);

void AllPath(CSTree T, stack<ElemType> *S1);

void PrintTree(CSTree T);

#endif

Tree.cpp

#include <iostream>
#include "Tree.hpp"
#include <stdio.h>
#include <stack>
#include <queue>

#include <stdlib.h>
using namespace std;

void CreateTree(CSTree *T) {
	(*T) = NULL;
	ElemType Felem;
	ElemType Selem;
	CSNode *r = NULL;
	CSNode *p = NULL;
	queue<CSNode*> Q;
	for (scanf("%c%c",&Felem,&Selem); Selem != '#'; scanf("%c%c",&Felem,&Selem)) {
		CSNode *node;
		node = (CSNode*)malloc(sizeof(CSNode));
		node -> data= Selem;
		node -> firstchild = NULL;
		node -> nextsibling = NULL;
		Q.push(node);
		if (Felem == '#') {
			(*T) = node;
			r = node;
		}
		else {
			while(Q.front()->data != Felem) {
				Q.pop();
			}
			p = Q.front();
			if (!(p->firstchild)) {
				p->firstchild = node;
				r = node;
			}
			else {
				r->nextsibling = node;
				r = node;
			}
		}
	}
}

void DestroyTree(CSTree *T) {
	if (*T) {
		DestroyTree(&((*T)->firstchild));
		DestroyTree(&((*T)->nextsibling));
		free(*T);
		(*T) = NULL;
	}
}

unsigned int TreeDepth(CSTree T) {
	if (T) {
		return (1+TreeDepth(T->firstchild))>TreeDepth(T->nextsibling)?(1+TreeDepth(T->firstchild)):TreeDepth(T->nextsibling);
	}		
	return 0;
}

void AllPath(CSTree T, stack<ElemType> *S1) {
	//stack<ElemType> S1;
	
	while (T != NULL) {
		S1->push(T->data);
		if (T->firstchild == NULL) {
			stack<ElemType> S2;
			S2 = (*S1);
			while(!S2.empty()) {
				printf("%c\n",S2.top());
				S2.pop();
			}
		}
		else {
			AllPath(T->firstchild, S1);
		}
	S1->pop();
	T=T->nextsibling;	
	}	
}

void PrintTree(CSTree T) {
if (T) {
	cout << T->data << endl;
	PrintTree(T->firstchild);
	PrintTree(T->nextsibling);
}	
}

main.cpp

#include <iostream>
#include "Tree.hpp"
using namespace std;
int main () {
	CSTree T;
	CreateTree(&T);
	//cout << T->nextsibling->data << endl;
	//PrintTree(T);
	stack<ElemType> s;
	AllPath(T, &s);
	/*while (!s.empty()) {
		cout << s.top()	<< endl;
		s.pop();
	}*/
	cout << TreeDepth(T) << endl;
	DestroyTree(&T);
	if (!T) {
		cout << "delete successfully" << endl;
	}
	return 0;	
}
### 回答1: 好的,我可以给你一些关于红黑的信息。 红黑是一种自平衡二叉搜索,具有以下性质: 1. 节点是红色或黑色。 2. 根节点是黑色。 3. 每个叶子节点(NIL)是黑色。 4. 每个红色节点的两个子节点都是黑色。 5. 从任一节点到其叶子节点的所有路径都包含相同数目的黑色节点。 以下是一个用 C 语言实现的红黑的示例代码: ``` #include <stdio.h> #include <stdlib.h> typedef struct _rbtree { int key; char color; struct _rbtree *left; struct _rbtree *right; struct _rbtree *parent; } rbtree; #define NIL &sentinel /* all leafs are sentinels */ rbtree sentinel = { 0, 'B', NIL, NIL, NIL }; void insert(rbtree *tree, int key); void delete(rbtree *tree, int key); void search(rbtree *tree, int key); void rbtree_print(rbtree *tree); int main(int argc, char **argv) { rbtree *root = NIL; insert(root, 5); insert(root, 10); insert(root, 15); insert(root, 20); insert(root, 25); rbtree_print(root); delete(root, 15); delete(root, 20); rbtree_print(root); search(root, 25); search(root, 30); return 0; } void insert(rbtree *tree, int key) { rbtree *current, *parent, *x; /* find future parent */ current = tree; parent = 0; while (current != NIL) { if (key == current->key) return; parent = current; current = key < current->key ? current->left : current->right; } /* setup new node */ if ((x = malloc (sizeof(*x))) == 0) return; x->key = key; ### 回答2: 红黑是一种自平衡的二叉查找,其在插入和删除节点时会通过一系列的旋转和着色操作来保持的平衡。下面是用C语言实现红黑的基本步骤: 1. 首先,定义节点的结构体,包括一个值变量、一个指向父节点的指针、左子节点和右子节点的指针,以及一个表示节点颜色的标志。 2. 定义红黑的结构体,包括一个指向根节点的指针和一个表示叶子节点的指针(叶子节点为黑色的空节点)。 3. 实现一个函数来创建一个新的节点,并进行初始化,包括设置节点的值、设置其颜色为红色,以及将父节点、左子节点和右子节点都指向空。 4. 实现一个函数来插入节点,首先进行普通的二叉查找插入操作,将新节点插入到中的正确位置,然后通过旋转和着色操作,来保持红黑的平衡。 5. 实现一个函数来删除节点,也是先进行普通的二叉查找删除操作,然后通过旋转和着色操作,来保持红黑的平衡。 6. 实现一个函数来进行节点插入后的颜色修正操作,包括处理新节点祖父节点的情况,以及处理新节点叔叔节点的情况。 7. 实现一个函数来进行节点删除后的颜色修正操作,包括处理删除节点的子节点为红色的情况,以及处理删除节点的子节点为黑色的情况。 8. 最后,实现一个函数来打印出红黑的结构,可以采用中序遍历的方式,输出中的所有节点值。 通过以上步骤,就可以实现一个基本的红黑。在实际编程中,还需要注意边界情况的处理、特殊情况的考虑等,以确保红黑的正确性和稳定性。红黑实现相对复杂,但是它具有良好的平衡性、插入删除操作的时间复杂度保持在O(logN),使其成为一种经常用于查找和排序的数据结构。 ### 回答3: 红黑(Red-Black Tree)是一种自平衡的二叉搜索,它在数据结构中有着广泛的应用。下面是用C语言实现红黑的基本思路: 首先,我们需要定义红黑的节点结构,包括键值、颜色、以及左右子节点等信息。代码如下: ``` typedef struct Node { int key; struct Node* left; struct Node* right; struct Node* parent; int color; } Node; ``` 接下来,我们需要实现红黑的插入、删除、搜索等基本操作。对于插入操作,我们首先按照二叉搜索的规则将节点插入到合适的位置,然后再进行颜色的调整和节点的旋转,以保持红黑的平衡和性质。插入操作的代码如下: ``` void insert(Node** root, int key) { // 创建新节点 Node* new_node = (Node*)malloc(sizeof(Node)); new_node->key = key; new_node->left = NULL; new_node->right = NULL; new_node->color = RED; // 插入新节点到合适位置 insert_node(root, new_node); // 调整颜色和节点的位置 insert_fixup(root, new_node); } ``` 对于删除操作,我们首先按照二叉搜索的规则找到要删除的节点,然后根据不同的情况进行不同的处理,包括颜色的调整和节点的旋转。删除操作的代码如下: ``` void delete(Node** root, int key) { // 根据键值搜索要删除的节点 Node* node = search(*root, key); // 删除节点并调整红黑 delete_node(root, node); delete_fixup(root, node); } ``` 以上只是红黑的一些基本操作,还可以根据实际需求进行进一步的扩展,例如实现前序遍历、中序遍历、后序遍历等。需要注意的是,红黑实现需要考虑各种情况的平衡与性质维护,代码的细节会比较复杂。 综上所述,这是一个使用C语言实现红黑的基本思路和部分代码示例。通过实现红黑,我们可以在O(log n)的时间复杂度下进行高效的搜索、插入和删除操作,适用于需要频繁的动态数据操作的场景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值