数据结构入门系列——树和二叉树(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]);
}

二叉树存储结构

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

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

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

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

二叉链算法

#include <iostream>
#include<string>
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

构造知识

二叉树的构造就是给定某些遍历序列,反过来唯一地确定该二叉树。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实例
在这里插入图片描述
在这里插入图片描述

代码

/*先序遍历*/
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值最小,必须使权值越大的叶子结点越靠近根结点。
在这里插入图片描述
在这里插入图片描述
哈夫曼编码
在这里插入图片描述

代码

/*实现哈夫曼树的创建算法,并按哈夫曼树实现哈夫曼编码算法。*/
#include<iostream>
#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");
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值