C++实现模版树

一,模版树需求

1,树的结点数据进行泛型处理;

2,树的增删改查;

3,增加父结点,目的是在树的遍历算法中不依赖栈结构;

4,解耦树的信息与树结点信息,定义两个class。

二,模版树class结构设计

1,树的结点数据

template
struct treenode {
	T val;
	treenode *parent;
	deque  *> child;
}

deque *> child; 思考了很多stl容器类,vector,list,unordered_map,最终使用deque较为合适,对比如下:

1)vector没有push_front接口;

2)list不能随机访问,也就是不能使用operator[ ];

3)unordered_map,在之后重新排序显得很难处理。

对比使用C语言构建树时,是转换二叉指针表示,左指针指向第一个孩子结点,右指针指向兄弟结点,该树的特点 根节点无右子树。表示如下:

struct treenode {
    struct treenode *parent;
    struct treenode *child;
    struct treenode *sibling;
}

对比C使用二叉指针,C++使用容器类存储孩子指针实现起来变得很简单。

2,树

template 
class tree {
public:
	tree() : m_root(NULL),m_size(0) {}
	tree(T rootval) {
		m_root = new treenode(rootval);
	}
	~tree();
	treenode *get_root() {
		return m_root;
	}
	size_t size() {
		return m_size;
	}
	treenode *find(T &val);
	void insert(T parent, T child);
	int high();
	void level_travel(void (*visit_callback)(T &val));
private:
	treenode *m_root;
	size_t m_size;
};

对于整棵树,我们只要有root 指针即可。
 

三,模版树api设计

1,树结点

treenode() : parent(NULL) {}
treenode(T val) : parent(NULL) {this->val = val;}
~treenode() {remove_childs();}
void remove_childs() {
	auto it = child.begin();
	if (parent) {
		for (it=parent->child.begin(); it!=child.end(); it++) {
			if (*it == this)
				break;
		}
		parent->child.erase(it);
		if (parent->child.size() > 0) {
			for (it=child.begin(); it != child.end(); ++it) {
				parent->child.front()->push_back_child(*it);
			}
		} else { 
			for (it=child.begin(); it != child.end(); ++it) {
				parent->push_back_child(*it);
			}
		}
	} else {
		if (child.size() > 0) {
			auto elder = child.front(); 
			elder->parent = NULL;
			for (it=child.begin()+1; it != child.end(); ++it) {
				elder->push_back_child(*it);
			}
		}
	}
	deque *>().swap(child);
}

关键是析构函数,要给它的孩子结点要寻个父节点。策略如下:

1)先判断this结点是否存在父节点,若存在,则需要删除this->parent保留this信息。

再判断this->parent->child.size() > 0,相当于给this->child寻址叔父结点;若无,由parent收养。

2)若this为root结点,则选用this第一个孩子为根结点,其余孩子结点加入到第一个孩子的孩子容器类中。

2,树

1)find

treenode *tree::find(T &val)
{
	treenode *result = NULL;
	int qlen = 0;
	queue*> q;
	if (!m_root)
		return result;
	q.push(m_root);
	while (!q.empty()) {
		qlen = q.size();
		for (; qlen > 0; qlen--) {
			auto front = q.front();
			q.pop();
			if (front->val == val) {
				result = front;
				goto ret;
			}
			for (auto child : front->child) {
				q.push(child);
			}
		}
	}
ret:
	return result;
}

采用层次遍历搜索。

2)insert

template 
void tree::insert(T parent, T child)
{
	treenode *pparent = find(parent);
	pparent->push_back_child(child);
}

3)树的析构

template 
tree::~tree()
{
	if (!m_root)
		return;
	treenode *p = m_root;
	m_root = NULL;
	while (p != NULL) {
		if (p->child.size() > 0) {
			m_root = p->child[0];
		}
		delete p;
		p = m_root;
		m_root = NULL;
	}
}

4)层次遍历

template 
void tree::level_travel(void (*visit_callback)(T &val))
{
	int qlen = 0;
	queue*> q;
	if (!m_root || !visit_callback)
		return ;
	q.push(m_root);
	while (!q.empty()) {
		qlen = q.size();
		for (; qlen > 0; qlen--) {
			auto front = q.front();
			q.pop();
			visit_callback(front->val);
			for (auto child : front->child) {
				q.push(child);
			}
		}
		printf("\n");
	}
}

四,验证

 如何验证数据结构写的是否正确很重要,通常验证目标有两点:

1,需求通路是否正常,编写测试用例;

2,内存是否泄漏,可通过编译开ASAN。

测试用例如下:

#include 

void visit_print(int &val)
{
	printf(" %d ", val);
}

int main()
{
	tree mytree(1);
	mytree.insert(1,2);
	mytree.insert(1,32);
	mytree.insert(2,31);
	mytree.level_travel(visit_print);
	return 0;
}

简易绘制测试用例所构建的树:

C++实现模版树_第1张图片

运行结果:

 1 
 2  32 
 31 

运行结果符合预期,且 无"==125916==ERROR: LeakSanitizer: detected memory leaks"这类报错。这下可以松口气啦~~。

你可能感兴趣的:(c++,数据结构,开发语言)