数据结构常用算法分析(C++版)-树的那些事

简介

树是一个有趣的数据结构,他不像图那么复杂,又不像线性表那么呆板!这种一对多的数据结构,使得树能够实现许多常用的算法! 现实中的许多复杂关系,也往往转化成“一对多”的树来解决!

这一节我们一起来看看“树的那些事”! 本张的主要内容是以二叉树进行展开叙述的,一共由3部分组成:

  • 树的概念及二叉树
  • 树的遍历
  • 最优二叉树

相关代码也托管到Github上了~我是代码,快点我

相关概念:

首先来粗略复习一下树的相关概念: 子树、结点的度、叶子结点、非终端结点、树的度(max(结点的度))、孩子、兄弟、双亲、树的深度、结点的堂兄弟、 二叉树、完全二叉树、满二叉树、有序树、无序树、森林、

树的常用3种表示方法:双亲、孩子、孩子兄弟

二叉树

二叉树的结构:

  • 线性结构:顺序存储
  • 链式结构:链式存储结构

定义如下代码所示:

//顺序
typedef Teletype int[1000];
//链式
typedf struct node
{
	T data;
	node* lchild;
	node* rchild;
}node;

二叉树性质:

  • 1:i层上最多2^(i-1)个点
  • 2:深度为k的二叉树最多2^(k-1)个节点
  • 3:度为2的结点为n1,那么叶子结点的个数n2=n1+1;
  • 4:n个结点的完全二叉树的深度D=[log2(n)]+1 (向下取整+1)
  • 5:完全二叉树编号, * 节点i有双亲则为[i/2],* 2i>n 则i无左孩子,否则左孩子编号为2i * 2i+1>n 则结点i无右孩子,否则右孩子编号为2i+1

树的遍历

树的遍历4种方式:先序遍历、中序遍历、后续便利、层序遍历!

时间复杂度O(n) 空间复杂度O(n)=D(T)

树的遍历分了一章单独来讲:从树的遍历看递归展开 感兴趣的同学可以点进去指点一下! ^-^

Huffman 树

Huffman树又叫最优二叉树,最大的特点是:树的带权深度最小: min(sum(W*L)) 哈夫曼树的性质:

  • 树中没有度为1的点,所以有n个叶子节点的Huffman树一共有2n-1个节点! (可以想象一下,如果存在度为1的点,一定存在比它“带权深度”小的树)
  • Huffman树不唯一,但所有Huffman的带权深度值相等!
  • 用频率表示权重,所有树结点的求和即为通信字符总的编码长度!

Huffman树的构造:

使用线性表的存储形式!

算法思路:

  • 定义临时数组temp,Huffman树数组tree
  • 对temp排序,求权重最小的2个元素a,b,分别放入tree中,并从temp中去除
  • 新建元素,weight=a.weight+b.weight,lchild=a,rchild=b;并放入temp中,
  • 如果temp.size()>1 则继续2,否则5
  • temp中最后一个元素放入tree,tree中即确定树的结构
  • 递归先序遍历,构造parent指针!构造完毕,Huffman树即完成!

哈哈,大功告成!从叶子结点出发,依次访问根节点,即可确定每个叶子结点的Huffman编码~

下面是Huffman的构造代码:


class TreeNode
{
public:
	int index;//值的索引
	int weight;
	int parent;
	int lchild;
	int rchild;
	TreeNode():index(-1),weight(-1),parent(-1),lchild(-1),rchild(-1)
	{}
	TreeNode(int in,int we):parent(-1),lchild(-1),rchild(-1)
	{
		index = in;
		weight = we;
	}
	TreeNode(int in,int we,int pa,int lc,int rc)
	{
		index = in;
		weight = we;
		parent = pa;
		lchild = lc;
		rchild = rc;
	}
	bool operator >(TreeNode t2)
	{
		return weight>t2.weight;
	}
};

bool compare(TreeNode &a,TreeNode &b)
{
	return a.weight<b.weight;
 } 


void ttree(vector<TreeNode> &T,int i)
{

	if(T[i].index==-1)//非叶节点
	{
		T[T[i].lchild].parent=i;
		T[T[i].rchild].parent=i;
		ttree(T,T[i].lchild);
		ttree(T,T[i].rchild);
	}
}

void HuffmanTree()
{
	//以序列 a:1 b:1 c:2 d:2 e:3 为例
	vector<int> num(5,0);//频率
	num[0]=1;num[1]=1;num[2]=2;num[3]=2;num[4]=3;
	vector<string> con(5);//字符
	con[0]="a";con[1]="b";con[2]="c";con[3]="d";con[4]="e";
	vector<TreeNode> tree;//保存构造的Huffman树
	vector<TreeNode> temp;//构造过程 频率 临时数组
	
	for(int j=0;j<con.size();j++)
	{
		temp.push_back(TreeNode(j,num[j]));
	}
	
	for(;temp.size()>1;)
	{
		sort(temp.begin(),temp.end(),compare);
		TreeNode t1(*temp.begin());
		TreeNode t2(*(temp.begin()+1));
		tree.push_back(t1);
		tree.push_back(t2);
		TreeNode t3;
		t3.lchild=tree.size()-1;
		t3.rchild=tree.size()-2;
		t3.weight=t1.weight+t2.weight;
		temp.erase(temp.begin());
		temp.erase(temp.begin());
		temp.push_back(t3);
	}
	tree.push_back(*temp.begin());

	for(int i=0;i<tree.size();i++)
	{
		cout<<"weight: "<<tree[i].weight;
		if(tree[i].index!=-1)
			cout<<" contain:  "<<con[tree[i].index];
		cout<<endl;
	}
	ttree(tree,tree.size());//遍历树,构造parent
}

总结

树型结构,内容基本就这么多了吧,哈哈,到了这里《数据结构》一书也算是复习一半了!

番外话:

找工作倒计时2个月,说实话,心里还是满没底气的!依然要努力加油! 希望能够通过接下来2个月的学习找到本科时的那份自信:为那些不招我的公司感到惋惜!

Search

    Post Directory