数据结构入门系列——树和二叉树(2)

深入递归

设计一个递归算法求一个整数数组中所有元素之和。

设f(i)为整数数组a中a[0]~a[i-1]这i个元素之和,这是原问题。
小问题为f(i-1),它为a[0]~a[i-2]这i-1个元素之和。
假设f(i-1)已求出,显然有f(i)=f(i-1)+a[i-1],另外,f[1]=a[0]。对应的递归模型如下:
f ( 1 ) = a [ 0 ] f ( i ) = f ( i − 1 ) + a [ i − 1 ] f(1)=a[0] \quad f(i)=f(i-1)+a[i-1] f(1)=a[0]f(i)=f(i1)+a[i1]

int fun(int a[], int i)
{
     
	if (i == 1) return a[0];
	else return (fun(a, i - 1) + a[i - 1]);
}

二叉树存储结构

完全二叉树或满二叉树采用顺序存储结构比较合适,既能够最大可能地节省存储空间,又可以利用数组元素的下标确定结点在二叉树中的位置以及结点之间的关系。
若需要增加很多结点补做满二叉树,则用链式结构比较合适。
顺序存储
用一维数组按从上到下、从左到右的顺序存储树中所有结点值,通过数组元素的下标关系反映完全二叉树或满二叉树中结点之间的逻辑关系。
数据结构入门系列——树和二叉树(2)_第1张图片

#include 
using namespace std;
#define MAXNODE 1000
typedef char SBiTree[MAXNODE];

通常将下标为0的位置空着,值为‘#’的结点为空结点。 
链式结构
对于一般的二叉树,通常采用二叉链表示。

typedef struct tnode
{
       ElemType data;			//数据域
   struct tnode *lchild,*rchild;	//指针域
} BTNode;
//如果是叶子结点,则左右孩子指针为NULL

二叉链算法

#include 
#include
using namespace std;
#define maxsize 25
typedef struct node
{
     
	char data;
	struct node *lchild, *rchild;
}btnode;
/*利用递归创建二叉树*/
/*ABC##DE#G##F###*/
void CreateBtnode(btnode *&bt)
{
     
	char ch;
	ch = getchar();
	if (ch == '#')
		bt == NULL;
	else {
     
		bt->data = ch;
		CreateBtnode(bt->lchild);
		CreateBtnode(bt->rchild);
	}
}
/*用顺序栈创建二叉树*/
/*A(B(C,D(E(,G),F)))*/
void CreateBtnode2(btnode *&bt, char *str)
{
     
	btnode *st[maxsize], *p = NULL; bt = NULL;
	char ch = str[0];
	int k, j = 0, top = -1;
	while (ch != '*')
	{
     
		switch (ch)
		{
     
		case '(':top++; st[top] = p;k=1; break;//首先把父结点入栈,下面要进来的是左孩子结点
		case ')':top--; break;//把下标移动到上一级。
		case ',':k = 2; break;//下面要进来的是左孩子结点
		default:
			p = new btnode;
			p->data = ch;
			p->lchild = p->rchild = NULL;
			if (bt == NULL)
				bt = p;
			else {
     
				switch (k)
				{
     
				case 1:st[top]->lchild = p; break;
				case 2:st[top]->rchild = p; break;
				}
			}
		}
		j++; ch = str[j];
	}
}
/*销毁二叉树*/
void DestroyBTree(btnode *&bt)
{
     
	if (bt != NULL)
	{
     
		DestroyBTree(bt->lchild);
		DestroyBTree(bt->rchild);
		delete bt;
	}
}
/*求二叉树高度运算算法*/
int BTHeight(btnode *bt)
{
     
	int lchilddep, rchilddep;
	if (bt == NULL) return(0); 		   //空树的高度为0
	else
	{
     
		lchilddep = BTHeight(bt->lchild);  //求左子树的高度
		rchilddep = BTHeight(bt->rchild);  //求右子树的高度
		return (lchilddep > rchilddep) ?
			(lchilddep + 1) : (rchilddep + 1);
	}
}
/*求二叉树结点个数运算算法*/
int NodeCount(btnode *bt)		//求二叉树bt的结点个数
{
     
	int num1, num2;
	if (bt == NULL)			//为空树时返回0
		return 0;
	else
	{
     
		num1 = NodeCount(bt->lchild);	//求左子树结点个数
		num2 = NodeCount(bt->rchild);	//求右子树结点个数
		return (num1 + num2 + 1);		//返回和加上1
	}
}
/*求二叉树叶子结点个数运算算法*/
int LeafCount(btnode *bt)	//求二叉树bt的叶子结点个数
{
     
	int num1, num2;
	if (bt == NULL)		//空树返回0
		return 0;
	else if (bt->lchild == NULL && bt->rchild == NULL)
		return 1;		//为叶子结点时返回1
	else
	{
     
		num1 = LeafCount(bt->lchild);	//求左子树叶子结点个数
		num2 = LeafCount(bt->rchild); 	//求右子树叶子结点个数
		return (num1 + num2);		//返回和
	}
}
/*以括号法输出*/
void OutputBTree(btnode *bt)
{
     
	if (bt != NULL)
	{
     
		cout << bt->data;
		if (bt->lchild != NULL || bt->rchild != NULL)
		{
     
			cout << "(";
			OutputBTree(bt->lchild);
			if (bt->rchild != NULL)
				cout << ",";
			OutputBTree(bt->rchild);
			cout << ")";
		}
	}
}
void main()
{
     
	btnode *bt;
	CreateBtnode(bt);
	string s;
	char str[maxsize];
	cin >> s;
	strcpy_s(str, s.c_str());
	CreateBtnode2(bt, str);
}

二叉树遍历

二叉树常用的遍历有先序(根)遍历、中序(根)遍历、后序(根)遍历和层次遍历。

基础知识

先序遍历:1.根结点 2.左孩子 3.右孩子
特点:其第一个元素值为二叉树中根结点值。
中序遍历:1.左孩子 2.根结点 3.右孩子
特点:若已知二叉树的根结点值,以该值为界,将中序遍历序列分为两部分,前半部分为左子树的中序遍历序列,后半部分为右子树的中序遍历序列。
后序遍历:1.左孩子 2.右孩子 3.根结点
特点:最后一个元素值为二叉树中根结点值。
实例

A
B
C
D
E
F
 
G
 

先序遍历:ABDECFG
中序遍历:DBEACGF
后序遍历:DEBGFCA
层次遍历:ABCDEFG

构造知识

二叉树的构造就是给定某些遍历序列,反过来唯一地确定该二叉树。
数据结构入门系列——树和二叉树(2)_第2张图片
数据结构入门系列——树和二叉树(2)_第3张图片
数据结构入门系列——树和二叉树(2)_第4张图片
实例
数据结构入门系列——树和二叉树(2)_第5张图片
数据结构入门系列——树和二叉树(2)_第6张图片

代码

/*先序遍历*/
void PreOrder(btnode *bt)
{
     
	if (bt == NULL)
		return;
	else {
     
		cout << bt->data << " ";
		PreOrder(bt->lchild);
		PreOrder(bt->rchild);
	}
}
/*中序遍历*/
void InOrder(btnode *bt)
{
     
	if (bt == NULL)
		return;
	else {
     
		InOrder(bt->lchild);
		cout << bt->data << " ";
		InOrder(bt->rchild);
	}
}
/*后序遍历*/
void PostOrder(btnode *bt)
{
     
	if (bt == NULL)
		return;
	else {
     
		PostOrder(bt->lchild);
		cout << bt->data << " ";
		PostOrder(bt->rchild);
	}
}
/*层次遍历*/
void LevelOrder(btnode *bt)
{
     
	btnode *p;
	btnode *qu[maxsize];	//定义循环队列,存放二叉链结点指针
	int front, rear;		//定义队头和队尾指针
	front = rear = 0;		//置队列为空队列
	rear++; qu[rear] = bt;	//根结点指针进入队列
	while (front != rear)		//队列不为空循环
	{
     
		front = (front + 1) % maxsize;
		p = qu[front];			//出队结点p
		printf("%c ", p->data);	//访问该结点
		if (p->lchild != NULL)		//有左孩子时将其进队
		{
     
			rear = (rear + 1) % maxsize;
			qu[rear] = p->lchild;
		}
		if (p->rchild != NULL)		//有右孩子时将其进队
		{
     
			rear = (rear + 1) % maxsize;
			qu[rear] = p->rchild;
		}
	}
}

哈夫曼树

带权路径长度可能不相同。把其中具有最小带权路径长度的二叉树称为哈夫曼树。
根据哈夫曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶子结点越靠近根结点。
数据结构入门系列——树和二叉树(2)_第7张图片
数据结构入门系列——树和二叉树(2)_第8张图片
哈夫曼编码
数据结构入门系列——树和二叉树(2)_第9张图片

代码

/*实现哈夫曼树的创建算法,并按哈夫曼树实现哈夫曼编码算法。*/
#include
#define Max_value 10000
#define Max_leaf 30
#define Max_node Max_leaf*2-1
using namespace std;
typedef struct HNode
{
     
	int weight;
	int parent;
	int lchild;
	int rchild;
}HuffmanTree[Max_node];
typedef struct codenode
{
     
	char ch;//记录结点值
	char code[Max_leaf];
}CodeNode;
typedef CodeNode HuffmanCode[Max_leaf];
void CrtHuffmanTree(HuffmanTree ht, int w[], int n)//创建哈夫曼树
{
     
	int i, j, m1, m2, x1, x2;
	for (i = 0; i < 2 * n - 1; i++)//所有结点初始化
	{
     
		ht[i].weight = 0;
		ht[i].parent = -1;
		ht[i].lchild = -1;
		ht[i].rchild = -1;
	}
	for (i = 0; i < n; i++)//赋予结点值
		ht[i].weight = w[i];
	for (i = 0; i < n - 1; i++)
	{
     
		m1 = m2 = Max_value;
		x1 = x2 = 0;
		for (j = 0; j < n + i; j++)
		{
     
			if (ht[j].weight < m1&&ht[j].parent == -1)
			{
     
				m2 = m1;
				x2 = x1;
				x1 = j;
				m1 = ht[j].weight;
			}
			else
				if (ht[j].weight < m2&&ht[j].parent == -1)
				{
     
					m2 = ht[j].weight;
					x2 = j;
				}
		}
		ht[x1].parent = n + i;
		ht[x2].parent = n + i;
		ht[n + i].weight = ht[x1].weight + ht[x2].weight;
		ht[n + i].lchild = x1;
		ht[n + i].rchild = x2;
	}
}
void OutputHuffmanTree(HuffmanTree ht, int m)
{
     
	if (m > -1)
	{
     
		cout << ht[m].weight << "  ";
		OutputHuffmanTree(ht, ht[m].lchild);
		OutputHuffmanTree(ht, ht[m].rchild);
	}
	else
		cout << "*  ";
}
void CrtHuffmanCode(HuffmanTree ht, HuffmanCode hc, int n)//创建哈夫曼结点
{
     
	char *cd;
	int i, c = 0, p = 0, start = 0;
	cd = (char *)malloc(n * sizeof(char));
	cd[n - 1] = '\0';
	for (i = 0; i < n; i++)
	{
     
		start = n - 1;
		c = i;
		p = ht[i].parent;
		while (p != -1)
		{
     
			--start;
			if (ht[p].lchild == c)
				cd[start] = '0';
			else
				cd[start] = '1';
			c = p;
			p = ht[p].parent;
		}
		cout << hc[i].ch << "  ";
		for (; start < n; start++)
			cout << cd[start];;
		cout << endl;
		for (int j = 0; j < n - 1; j++)
			cd[j] = '0';
		cd[n - 1] = '\0';
	}
	free(cd);
}
void main()
{
     
	HuffmanTree ht;
	HuffmanCode hc;
	int n, m = 0;
	int w[Max_node];
	int i;
	cout << "请输入哈夫曼树的结点个数:";
	cin >> n;
	for (i = 0; i < 2 * n - 1; i++)
	{
     
		hc[i].ch = '0';
	}
	for (i = 0; i < n; i++)
	{
     
		cout << "第" << i + 1 << "个元素的结点值为:" << endl;
		cin >> hc[i].ch;
		cout << "权重为:";
		cin >> w[i];
	}
	CrtHuffmanTree(ht, w, n);
	cout << "哈夫曼树为:\n";
	OutputHuffmanTree(ht, 2 * n - 2);
	cout << endl;
	cout << "哈夫曼结点的编码为:\n";
	CrtHuffmanCode(ht, hc, n);
	system("pause");
}

你可能感兴趣的:(数据结构)