北京邮电大学数据结构实验二题目1

题目1

根据二叉树的抽象数据类型的定义,使用二叉链表实现一个二叉树。

    二叉树的基本功能:

1、二叉树的建立

2、前序遍历二叉树

3、中序遍历二叉树

4、后序遍历二叉树

5、按层序遍历二叉树

6、求二叉树的深度

7、求指定结点到根的路径

8、二叉树的销毁

9、其他:自定义操作

编写测试main()函数测试线性表的正确性。

参考代码:

源.cpp

#include<iostream>
#include"标头.h"
using namespace std;
int main()
{

	int a[13] = { 1,2,3,9,5,6,0,0,0,7 ,0,0,8 };

	BiTree<int>tree;
    tree.Create(tree.Root(), a, 1, 13);


	cout << "前序遍历:" << endl;
	tree.PreOrder(tree.Root());
	cout << endl;
	cout << "中序遍历:" << endl;
	tree.InOrder(tree.Root());
	cout << endl;
	cout << "后序遍历:" << endl;
	tree.PostOrder(tree.Root());
	cout << endl;
	cout << "层序遍历:" << endl;
	tree.LevelOrder(tree.Root());
	cout << endl;
	int d = 0, pp;
	cout << "树的深度:" << endl;
	pp = tree.GetDepth(tree.Root(), d);
	cout << pp;
	cout << endl;
	cout << "指定节点到根的路径:" << endl;
	tree.Path(tree.Root(),5);
	cout << endl;
	pp=tree.Count(tree.Root());
	cout << "二叉树的总结点数:" << endl;
	cout << pp << endl;


	system("pause");
	return 0;

标头.h

template <class T>
struct BiNode
{
	T shuju;
	BiNode<T>  *lchild, *rchild;
};

template <class T>
class BiTree
{
protected:

	BiNode<T> *root;

	void Release(BiNode<T> *R);
public:

	BiTree() :root(NULL) {}
	BiNode<T>*& Root();
	void Create(BiNode<T> *&R, T data[], int i, int n);
	void PreOrder(BiNode<T> *R);
	void InOrder(BiNode<T> *R);
	void PostOrder(BiNode<T> *R);
	void LevelOrder(BiNode<T> *R);
	int GetDepth(BiNode<T> *R, int d);
	~BiTree();
	bool Path(BiNode<T> *R, T x);
	int Count(BiNode<T> *R);


};
template <class T>
BiNode<T>*& BiTree<T>::Root()
{
	return root;
}

template <class T>
void  BiTree<T>::Create(BiNode<T> *&R, T data[], int i, int n)
{
	if (i <= n && data[i - 1] != 0)   //i表示位置,从1开始计数 
	{
		R = new BiNode<T>;
		R->shuju = data[i - 1];
		Create(R->lchild, data, 2 * i, n);
		Create(R->rchild, data, 2 * i + 1, n);
	}
	else
		R = NULL;
}

template < class T >
void BiTree<T>::PreOrder(BiNode<T>  *Root)
{
	if (Root != NULL)
	{
		cout << Root->shuju;    // 访问结点
		PreOrder(Root->lchild); // 遍历左子树
		PreOrder(Root->rchild); // 遍历右子树
	}
}
template <class T>
bool BiTree<T>::Path(BiNode<T> *root, T data)
{
	if (root == NULL)
		return false;


	if (root->shuju == data || Path(root->lchild, data) || Path(root->rchild, data))
	{
		cout << root->shuju;
		return true;
	}
	return false;
}
template < class T >
void BiTree<T>::InOrder(BiNode<T>  *Root)
{
	if (Root != NULL)
	{
		InOrder(Root->lchild);     // 遍历左子树
		cout << Root->shuju;            // 访问结点
		InOrder(Root->rchild);    // 遍历右子树
	}
}
template < class T >
void BiTree<T>::PostOrder(BiNode<T>  *Root)
{
	if (Root != NULL)
	{
		PostOrder(Root->lchild);     	// 遍历左子树
		PostOrder(Root->rchild);    	// 遍历右子树
		cout << Root->shuju;               	// 访问结点
	}
}

template <class T>
int BiTree<T>::GetDepth(BiNode<T> *R, int d)
{
	if (R == NULL)
		return d;
	if ((R->lchild == NULL) && (R->rchild == NULL))
		return d + 1;
	else {
		int m = GetDepth(R->lchild, d + 1);
		int n = GetDepth(R->rchild, d + 1);
		return n>m ? n : m;
	}
}


template <class T>
void BiTree<T>::LevelOrder(BiNode<T> *R)
{
	Queue<BiNode<T>*> Q;
	while (!Q.IsEmpty() || (R != NULL))
	{
		if (R != NULL)
		{
			cout << R->shuju;
			Q.EnQueue(R->lchild);
			Q.EnQueue(R->rchild);
		}
		R = Q.DeQueue();
	}
}
template < class T >
BiTree<T>::~BiTree()
{
	Release(root);
}
template < class T >
int BiTree<T>::Count(BiNode<T> *R)
{
	if (R == NULL) return 0;
	else
	{
		int m = Count(R->lchild);
		int n = Count(R->rchild);
		return m + n + 1;
	}

}
template < class T >
void BiTree<T>::Release(BiNode<T>  *Root)
{
	if (Root != NULL)
	{
		Release(Root->lchild);     	// 释放左子树
		Release(Root->rchild);    	// 释放右子树
		delete Root;                      	// 释放根结点
	}
}



template <class T>
class Queue {
public:
	Queue();
	int IsEmpty();
	void EnQueue(T x);
	T DeQueue();

	~Queue();
	T data;
protected:
	struct Node
	{
		T data;
		Node *next;
	};
	Node *Front;
	Node *Rear;
};

template <class T>
Queue<T>::Queue()
{
	Front = new Node;
	Front->next = NULL;
	Rear = Front;
}
template <class T>
int Queue<T>::IsEmpty()
{
	if (Front == Rear)
		return 1;
	else
		return 0;
}

template <class T>
void Queue<T>::EnQueue(T t)
{
	Node *s = new Node;
	s->data = t;
	s->next = NULL;
	Rear->next = s;
	Rear = s;
}
template <class T>
T Queue<T>::DeQueue()
{
	T t = NULL;
	if (Front == Rear)
		cout << "队列空,无元素出队!" << endl;
	else
	{
		Node *s = Front->next;
		t = s->data;
		Front->next = s->next;
		delete s;
		if (Front->next == NULL)
			Rear = Front;
	}
	return t;
}

template <class T>
Queue<T>::~Queue()
{
	while (Front != Rear)
	{
		cout << DeQueue() << endl;
	}
}

Q1:为什么Create函数(即创建二叉树的函数)要用指针的引用作为形参?

A1:这要回归到值传递和地址传递的基本问题。我们在实际编程中会遇到这样的情景,传递指针,在形参中修改指针所指向的内容,实参也得到了相应改变。

示例代码1:

#include <iostream>
using namespace std;
struct Value {
	int a;
	int b;
};

void modify1(Value *v) 
{
	v->a = 10086;
	v->b = 10010;
}

int main() {
	Value *v1 = new Value;
	v1->a = v1->b = 1;
	cout << v1->a << " " << v1->b << endl;
	modify1(v1);
	cout << v1->a << " " <<  v1->b<< endl;

	return 0;
}

输出结果:

1 1

10086 10010

但,如果我们想在被调函数中修改指针所存储的内容,即所存地址。而传递的形参是所要修改的指针本身,看看会出现什么错误。

示例代码2:

#include <iostream>
using namespace std;


struct Value {
	int a;
	int b;
};


void create(Value *v)
{
	Value *v2 = new Value;
        v2->a = 10086;
	v2->b = 10010;
	v = v2;
}


int main() {
	Value *v1 = new Value;
	v1->a = 1;
	v1->b = 1;
	create(v1);
	cout << v1->a << " " << v1->b << endl;
	delete v1;
	return 0;
}

输出结果:

1 1

我们可以看到,v1并没有进行相应的赋值操作。

其实回到最根本,原因在于,指针和其他变量一样,拥有自身的地址。与其他变量不同的是,指针变量所存放的数据是地址。指针作为参数进行传递本质上是值传递,只是这个值是地址。通过值传递的方式传递参数,形参的改变不影响实参。示例代码1中,我们传递指针,在被调函数中对指针所指向的内容进行修改,实参中的相应参数成功被修改。示例代码2中,我们传递指针,在被调函数中希望对指针本身进行修改,实参并没有进行相应的操作。这是我们在写代码时应注意的。

将示例代码2修改,得到示例代码3.

示例代码3:

#include <iostream>
using namespace std;

struct Value 
{
	int a;
	int b;
};

void create(Value **v)
{
	
    (*v)->a = 10086;
	(*v)->b = 10010;
	
}

int main() {
	Value *v1 = new Value;
	v1->a = 1;
	v1->b = 1;
	create(&v1);
	cout << v1->a << " " << v1->b << endl;
	delete v1;
	return 0;
}

输出结果:

10086 10010

总结:将指针归到基本数据类型来理解,和int,char等一样。(这在《mooc浙大数据结构PTA习题之一元多项式的乘法与加法运算》其实有与本问题直接相关的操作。)

回到我们的问题,Create函数的形参用了指针的引用(对指针的引用进行改变能直接改变实参的值,与传递指针的指针有类似的效果),目的是我们在Create中对R的操作能直接改变到实参中的R。对指针本身进行操作,而不是对指针所指向的内容进行操作,要注意形参的形式。

Q2:为什么tree.Root函数获取的永远是指向根节点的指针?

A2:我们看看在创建二叉树时的操作。

源.cpp第10行。

 tree.Create(tree.Root(), a, 1, 13);  

我们向Create函数传递了还是空指针的root。

标头.h第39行开始

void  BiTree<T>::Create(BiNode<T> *&R, T data[], int i, int n)  
{  
    if (i <= n && data[i - 1] != 0)   //i表示位置,从1开始计数   
    {  
        R = new BiNode<T>;  
        R->shuju = data[i - 1];  
        Create(R->lchild, data, 2 * i, n);  
        Create(R->rchild, data, 2 * i + 1, n);  
    }  
    else  
        R = NULL;  
}  
此时i=1,R指向了根节点,并且在递归函数全部进行完之后,并没有指向根节点的指针R(即root)的操作,因为下面很多的递归中的R指向其他节点,是对其他节点的创建与赋值。root在程序第一次进入Create时(i=1),运行到第43,44行,就指向了根节点。

猜你喜欢

转载自blog.csdn.net/wss123wsj/article/details/80304338