【树】C++ 树结构算法学习笔记

(一)教材

晴神宝典(算法笔记) 注:本文中未注明出处的代码段均出自晴神宝典

谭浩强C++程序设计

算法笔记pdf版本资源: https://pan.baidu.com/s/1WtrX-uvKQ-UwmK7dViP3vg
提 取 码: u3gk
含上机,需要的朋友请自提

注:
本文持续更新。

(二)论述题

1.什么是树的宽度,什么是树的高度?
宽度:树中结点最大的度成为树的度(也叫树的宽度);
深度:从根节点开始自顶向下逐层累加至该结点时的深度值;
高度:从最底层叶子结点开始自底向上逐层累加至该结点时的高度值;
深度=高度。

2.二叉树与度为二的树的区别
度二位的树不区分左右子树,二叉树有严格区分

3.树的结点定义
树的存储结构一般使用链表定义,所以需要对树的结点进行规定。

4.函数参数中什么时候使用引用,什么时候不使用?
如果函数中需要新建结点,即对二叉树的结构做出修改,就需要加引用;如果只是修改当前已有结点的内容,或仅仅是遍历树,就不需要加引用。
(可以参考C 语言里对链表中Head指针的修改——有可能改变head的情况和只是遍历的情况)

(三)编码

一、基础操作

总述:
树的定义是递归的,这导致树的大多数操作也是递归的

struct node{//树结点定义 
	typename data; //数据域 
	node* lchild;  //左儿子 
	node* rchild;  //右儿子 
};

4.新建结点函数
//用于新建一个结点的函数

笔记:
①C++中申请一个新的空间的方式:node* Node =new node; //申请一个新空间
②C++中结点数据的展现方式:Node->data=v; //结点权值为v
(与C区别开来——
【C】申请一个结点:struct Node * p=(struct Node*)malloc(sizeof(struct Node));
【C】结点数据:(*p).data;

node* newNode(int v)
{
	node* Node =new node;  //申请一个新空间
	Node->data=v;//结点权值为v
	Node->lchild=Node->rchild=NULL;
	return Node; 
}

5.查找一个结点:

void search(node *root,int x,int newdata)
{
	if(root==NULL)
	{
		return;//递归边界,空树 
	}
	if(root->data==x)//找到数据域为x的结点,把它修改成newdata 
	{
		root->data=newdata;
	}
	search(root->lchild,x,newdata);//往左子树搜索x
	search(root->rchild,x,newdata);//往右子树搜索x 
}

6.插入一个结点

//向二叉树插入一个数据域为x的新结点

void insert(node *&root,int x)  //必须使用引用
{
	if(root==NULL)//空树,查找失败, 
	{
		root =newNode(x);
		return;
	}
	if(左子树){
		insert(root->lchild,x);
	}
	else
	{
		insert(root->rchild,x);
	}
}

7.创建一个二叉树

//data数组保存要存入树的信息 
node* Create(int data[],int n)
{
	node* root =NULL;//建立根节点 
	for(int i=0;i<n;i++)
	{
		insert (root,data[i]);
	}
	return root;
}

二、二叉树的遍历

二叉树遍历方式有四种:
①先序遍历;DFS
②中序遍历;DFS
③后序遍历;DFS
④层次遍历。BFS

①先序遍历
根节点-》左子树-》右子树
递归访问

void preorder(node* root)
{
	if(root==NULL)//递归边界 
	{
		return;
	}
	printf("%d\n",root->data);//输出当前结点
	preorder(root->lchild);//递归访问左子树
	preorder(root->rchild);//递归访问右子树	
}

②中序遍历

左子树 -》 根节点 -》右子树

只要知道根节点,就可以通过根节点在中序遍历序列中的位置区分出左右子树
所以先序+中序/后序+中序可以确定整棵树的结构。

void inorder(node *root)
{
	if(root==NULL)
	{
		return ;
	}
	inorder(root->lchild);
	printf("%d\n",root->data);
	inorder(root->rchild);
}

③后序遍历

void postorder(node *root)
{
	if(root==NULL)
	{
		return ;
	}
	postorder(root->lchild);
	postorder(root->rchild);
	printf("%d\n",root->data);
}

④层次遍历
(对二叉树进行广度优先搜索)
思路:
使用一个辅助队列Q,先把根节点放进去,同时将它的左右儿子入队,继续访问;出队一个结点,继续访问它的左右儿子结点,入队,直到整个队列没有结点。

void LayerOrder(node *root)
{
	queue <node*> q;
	q.push(root);  //根节点入队
	while(!q.empty())
	{
		node *now =q.front();//取出队首元素
		q.pop();
		printf("%d",now->data) ;
		if(now->lchild!=NULL)
			q.push(now->lchild);
		if(now0>rchild!=NULL)
			q.push(now->rchild);
	 } 
}

三、 二叉树的结构确立

1.给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树。

算法思想:
依然是个递归函数
参数信息:先序序列为【preL,preR】,中序序列区间为[inL,inR],返回的是根节点的地址

node *create (int preL,int preR,int int inL,int inR)
{
	if(preL>preR)   //先序的长度为0时,整棵树没有根,返回
	{
		return NULL;
	}
	node* root=new node;//建立一个结点,存放当前二叉树的根节点
	root->data=pre[preL];//存放根节点的数据域
	
	for(int k=inL;k<inR;k++)//在中序序列中找根节点的位置 
	{
		if(in[k]==pre[preL])
		{
			break;
		}
	 } 
	
	int numLeft = k-inL;//左子树结点个数 
	root->lchild = create(preL +1,preL +numLeft,inL,k-1);//递归建立左子树
	
	root->rchild = create(preL +numLeft+1,preR,k+1,inR);//递归建立右子树
	return root; 
}

四、二叉树的搜索:BFS与DFS

1.深度优先搜索DFS

【PAT A1053】给定一颗树和每个结点的权值,求从所有根节点到叶子结点的路径,是的每条路径上的权值之和等于给定的常数S。如果有多条这样的路径,则按路径非递增的顺序输出。

思路:
步骤一:这是一棵普通性质的树,因此令结构体node存放结点的数据域和指针域,其中指针域使用vector存放所有孩子结点的编号。又考虑到最后的输出需要按权值从大到小排序,因此不妨在读入的时候就事先对每个结点的子节点vector进行排序,这样在遍历时就会优先遍历到权值大的子结点。
步骤二:令Int型数path[]存放递归过程中产生路径上的结点编号。接下来进行DFS,参数又三个:当前访问的结点标号index,当前路径path上的结点个数numNode(也是递归层数,因为每深入一层,path上就会多一个结点)以及当前路径上的权值和sum。
递归过程的伪代码如下:
①若sum>S,直接return ;
②若sum==S ,说明当前访问结点index为止,输入中需要达到的S已经得到,这时如果结点index为叶子结点,则输出path数组中所有数据,否则return ;
③若sum<S,说明要求还未满足,此时枚举当前访问结点index的所有子节点,对每一个子节点child,先将其存入path,然后在子基础上往下一层递归,下一层的递归参数为child,numNode+1,sum+node[child].weight

void DFS(int index,int numNode ,int sum)
{
	if(sum>S) return ;
	if(sum==S){
	if(Node[index].child.size()!=0)return;
	for(int i=0;<numNode;i++)
	{
		printf("%d",Node[path[i]].weight);
		if(i<numNode-1)printf(" ");
		else printf("\n");
		}return;
	for(int i=0;i<Node[index].child.size();i++)
	{
		int child = Node[index].child[i];
		path[numNode]=child;
		DFS(child,numNode+1,sum+NOde[child].weight);
		}
}

五、二叉查找树 BST

二叉查找树(Binary Search Tree,BST)是一种特殊的二叉树,又称为排序二叉树。

中序遍历二叉查找树即可得到有序序列。

它的递归定义如下:
①要么二叉查找树是一棵空树;
②要么二叉查找树是由根节点、左子树、右子树组成,其中左子树和右子树都是二叉查找树,且左子树上所有节点的数据域均小于等于根节点的数据域,右子树上的所有结点的数据域都大于根节点的数据域。

二叉查找树基本操作——查找:
①若当前根节点为空,说明查找失败,返回;
②如果需要查找的x等于当前根节点的数据域root->data,说明查找成功,访问;
③如果需要查找的x小于当前根节点的数据域,说明应该往左子树拆招,因此向root->lchild递归;
④反之,向root->rchild递归;

void search(node* root,int x)
{
	if(root==NULL){
	printf("searc failed\n");
	return;
	}
	if(x==root->data)
	{
		printf("%d\n",root->data);
	}
	else if(x<root->data)
	{
		search(root->lchild,x);
	}else
	{
		search(root->rchild,x);
	}
}

二叉树基本操作——插入操作

查找某个数据域的结点一定是沿着确定的路径惊醒的,因此,党对某个需要查找的值在二叉查找树中查找成功,说明结点已经存在;反之,如果这个需要查找的值在二叉查找树中查找失败,那么说明查找失败的地方一定是结点需要插入的地方。
因此可以在查找操作的基础上,在root==NULL时新建需要插入的结点。

void search(node* root,int x)
{
	if(root==NULL){
	root=newNode(x);//新建结点,值为x
	return;
	}
	if(x==root->data)
	{
		printf("%d\n",root->data);
	}
	else if(x<root->data)
	{
		search(root->lchild,x);
	}else
	{
		search(root->rchild,x);
	}
}

二叉查找树的基本操作——建立

建立一棵二叉查找树,就是先后插入n个结点的过程,这和一般二叉树的建立是完全一致的,因此代码也基本相同。

node *Create(int data[],int n0
{
	node*root = NULL;
	for(int i=0;i<n;i++)
	{
		insert(root,data[i]);
		}
	return root;
	}

注意:二叉查找树不唯一,即便是同一组数据,当插入顺序不同时得到的二叉查找树也可能不同。

二叉查找树的基本操作——删除

为了在删除结点之后得到的仍然是一棵二叉查找树,有两种方法可以对某个特定结点进行删除:
第一种是选择它左侧比它小的结点对他进行覆盖,然后把那个结点删除掉;
第二种是选择它右侧紧邻着它且比它大的结点对他进行覆盖,然后把那个结点删除掉。

代码:晴神P313

六、平衡二叉树 AVL

AVL也是二叉查找树,但是在BST的基础上增添了平衡的条件,
二叉平衡树具有一个特殊值,叫做平衡因子,每个结点都必须具有一个平衡因子,所谓平衡,就是说每个节点的左右两个子树的结点数差值不超过1;

晴神P320

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值