pat必刷模板第九章 树

本文详细介绍了二叉树的结构、新建、查找、修改、插入操作,以及四种遍历方式(前序、中序、后序和层序)。此外,还涵盖了树的遍历、二叉查找树、AVL树、并查集、堆等数据结构的操作,并探讨了它们在增删改查中的应用。最后,讨论了LCA(最近公共祖先)问题的解决方法。
摘要由CSDN通过智能技术生成

二叉树的增删改查和四种遍历

//二叉树的增删改查,和四种遍历 

                                                                      //二叉树的存储结构
//二叉链表 
struct node{
	typename data;//数据域 
	node* lchild;//指向左子树根结点的指针 
	node* rchild;
};		

//新建结点
node* newNode(int v){
	node* Node = new node;//申请一个node型变量的地址空间 
	Node->data = v;
	Node->lchild = Node->rchild = NULL;//初始状态没有左右孩子
	return Node; 
}				

                                                                                        //二叉树结点的查找,修改
void search(node* root, int x, int newdata){ 
	if(root == NULL) return;//空树或者边界
	if(root->data == x) root->data = newdata;//找到数据域为x的结点,修改为newdata
	search(root->lchild, x, newdata);
	search(root->rchild, x, newdata); 
}									

                                                                                             //二叉树的插入
//根结点root要使用引用,因为要对root本身的地址进行修改,而不是里面的data或者孩子 
void insert(node* &root, int x){
	if(root == NULL){
		root = newNode(x);
		return;
	}
	if(根据二叉树的性质,x应该插在左子树){
		insert(root->lchild, x);//左孩子递归 
	}else{
		insert(root->rchild, x); 
	} 
}															
                      
					                                                                         //二叉树的创建
node* create(int data[], int n){
	node* root = NULL;
	for(int i = 0; i < n; i++){
		insert(root, data[i]);//将数据插入到二叉树中;data[0]~data[n-1] 
	}
	return root;
}																	

root == NULL;//表示结点root不存在
*root == NULL;//表示结点可能存在,但没有内容		


                                                                                            //二叉树的遍历
//先序遍历
void pre(node* root){
	if(root == NULL) return;//边界 
	printf("%d\n", root->data);//根左右的遍历 
	pre(root->lchild);
	pre(root->rchild);
}						
//中序遍历
void in(node* root){
	if(root == NULL) return;
	in(root->lchild);
	printf("%d\n", root->data);
	in(root->rchild);
} 
//后序遍历
void post(node* root){
	if(root == null) return;
	post(root->lchild);
	post(root->rchild);
	printf("%d\n", root->data);
}							

//层序遍历(相当于bfs)
struct node{
	int data;
	int layer;//层次 
	node* lchild;
	node* rchild;
}; 
#include<queue>
#include<iostream>
using namespace std;
void bfs(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){
			now->lchild->layer = now->layer+1;
			q.push(now->lchild);
		} 
		if(now->rchild != NULL){
			now->rchild->layer = now->layer + 1;
			q.push(now->rchild);
		}
	}
}

二叉树的转换

//前序中序 转  后序
void post(int preroot, int l, int r){//root为pre遍历根的下标,start是in的最左下标,end为in最右的下标 
	if(l > r) return;//边界 
	int i = l;
	while(i < r&& pre[preroot] != in[i]) i++;//在in中找到root的下标i 
	post(root + 1 , l, i - 1);//i-1-l是左子树边的个数,右子树的根结点为root+i-l+1+1 
	post(root + 1 + i - l, i + 1, r);                                               //右子树根结点为root加左子树的边+2 
	printf("%d", pre[preroot]);
} 

//后序中序   转   前序
void pre(int postroot, int l, int r){
	if(l > r) return;
	int i = l;
	while(i < r&& post[postroot] != in[i]) i++;
	printf("%d", post[postroot]);
	pre(postroot - 1 - r + i, l, i - 1)                                                 //左子树的根结点为root减右子树的边-2 
	pre(postroot - 1, i + 1, r);//r-i-1只是右子树边的个数 
} 

//后中   转    层序
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
vector<int> post, in;
map<int, int> level;
void pre(int root, int l, int r, int index){
	if(l > r) return;
	int i = l;
	while(i < r&&post[root] != in[i]) i++;
	level[index] = post[root];
	pre(root - 1 - r + i, l, i-1, 2 * index + 1);//index+1为完全二叉树里层序遍历的左子树下标 
	pre(root - 1, i + 1, r, 2 * index + 2);//index+2为完全二叉树里层序遍历右子树下标 
} 
int main(){
	int n;
	scanf("%d", &n);
	post.resize(n);
	in.resize(n);
	for(int i = 0; i < n; i++) scanf("%d", &post[i]);
	for(int i = 0; i < n; i++) scanf("%d", &in[i]);
	pre(n-1, 0, n-1, 0);
	auto it = level.begin();
	printf("%d", it->second); 
	while(++it != level.end()) printf(" %d", it->second);
	return 0;
}

树的遍历

//树的遍历

#include<vector>
#include<iostream>
#include<queue>
using namespace std;
//树的静态写法
struct node{
	int layer;
	int data;
	vector<int> child;//因为不知道孩子有多少个 
}Node[maxn]; 
//新建结点
int index = 0;
int newNode(int v){
	Node[index].data = v;
	Node[index].child.clear();//清空子结点
	return index++;//返回结点下标,并令index自增 
} 

                                                                            //树的先根遍历 
void pre(int root){
	printf("%d",  Node[root].data);
	for(int i = 0; i < Node[root].child.size(); i++){
		pre(Node[root].child[i]);//递归访问结点root的所有子结点 
	} 
}                                                              

                                                                            //树的层序遍历
void bfs(int root){
	queue<int> q;
	q.push(root);
	Node[root].layer = 0;
	while(!q.empty()){
		int front = q.front();
		printf("%d", Node[front].data);
		q.pop();
		for(int i = 0; i < Node[root].child.size(); i++){
			int child = Node[front].child[i];
			Node[child].layer = Node[front].layer + 1
			q.push(child);
		}
	}
}

树的dfs

//dfs
//给出的树的结构和权值,找从根结点到叶子结点的路径上的权值相加之和等于给定目标数的路径,  并且从大到小输出路径

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int target;
struct node{
	int w;
	vector<int> child;
};
vector<node> v;//没有分配大小,此处size()为0 
vector<int> path;//因为vector初始化时,path[0~maxn]都为0 
void dfs(int index, int nodenum, int sum){//结点编号,第几个结点,结点目前总权值 
	if(sum > target) return;//长度超标 
	if(sum == target){
		if(v[index].child.size() != 0) return;//非叶子结点
		for(int i = 0; i < nodenum; i++){
			printf("%d%c", v[path[i]].w, i != nodenum - 1 ? ' ' : '\n');//path[i]是孩子结点的序号,输出路径序号的权值 
		} 	
		return; 
    } 
	for(int i = 0; i < v[index].child.size(); i++){
			int child = v[index].child[i];//结点index的第i个子结点编号 
			path[nodenum] = child;//将结点child加到 path末尾
			//或者path.push_back(child); 也可以把child加到path末尾 
			dfs(child, nodenum + 1, sum + v[child].w);
	}
}
int cmp(int a, int b){
	return v[a].w > v[b].w;
} 
int main(){
	int n, m, id, k;//结点数,叶子节点数,结点编号,每个结点的孩子个数 
	scanf("%d%d%d", &n, &m, &target);
	v.resize(n), path.resize(n);//因为vector初始化时,path[0~maxn]都为0
	for(int i = 0; i < n; i++){
		scanf("%d", &v[i].w);
	}
	for(int i = 0; i < m; i++){//m是无叶子结点个数 
		scanf("%d%d", &id, &k);
		v[id].child.resize(k);
		for(int j = 0; j < k; j++){
			scanf("%d", &v[id].child[j]);
		}
		sort(v[id].child.begin(), v[id].child.end(), cmp);
	}
	dfs(0, 1, v[0].w);
	return 0;
}

bst的增删改查

//BST二叉查找树的增删改查

                                            //search函数查找二叉查找树中数据为x的结点
void search(node* root, int x){
	if(root == NULL){//空树,查找失败 
		printf("search 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用&引用 
void insert(node* &root, int x){
	if(foot == NULL){
		root = newNode(x);
		return;
	}
	if(x == root->data){//查找成功,直接返回 
		return;
	}else if(x < root->data){
		insert(root->lchild, x);
	}else{
		insert(root->rchild, x);
	}
} 								
           
		                                            //二叉查找树的建立
node* creat(int data[], int n){
	node* root = NULL;
	for(int i = 0; i < n; i++){
		insert(root, data[i]);//将data[0~n-1]插入二叉查找树 
	}
	return root;
}														


                                                            //二叉查找树的删除
//删除root为根的data为x的结点 
void deletenode(node* &root, int x){
	if(root == NULL) return;
	if(root->data == x){
		if(root->lchild == NULL&&root->rchild == NULL){
			root = NULL;
		}else if(root->lchild != NULL){
			node* pre = findmax(root->lchild);//找到左子树的最大结点 
			root->data = pre->data;
			deletenode(root->lchild, pre->data);//把pre删了 
		}else(root->rchild != NULL){
			node* post = findmin(root->rchild);
			root->data = post->data;
			deletenode(root->rchild, post->data);
		}
	}else if(root->data > x){
		detetenode(root->lchild, x);
	}else{
		deletenade(root->rchild, x);
	}
}	

镜像bst

//给出n个正整数作为一颗二叉排序树的结点插入排序,
//若是先序序列或者镜像树的先序序列,则输出YES,并且输出对应树的后序序列;                否则输出NO       
#include<iostream>
#include<vector>
using namespace std;
struct node{
	int data;
	node* left;
	node* right;
};
void insert(node* &root, int data){
	if(root == NULL){
		root = new node;
		root->data = data;
		root->left = root->right = NULL;
		return;
	}
	if(data < root->data) insert(root->left, data);
	else insert(root->right, data);
}
void pre(node* root, vector<int> &v){//先序遍历,将结果存在v中 
	if(root == NULL) return;
    v.push_back(root->data);
    pre(root->left, v);
    pre(root->right, v);
}
void premirror(node* root, vector<int> &v){
	if(root == NULL) return;
	v.push_back(root->data);
	premirror(root->right, v);
	premirror(root->left, v);
}
void post(node* root, vector<int> &v){
	if(root == NULL) return;
	post(root->left, v);
	post(root->right, v);
	v.push_back(root->data);
}
void postmirror(node* root, vector<int> &v){
	if(root == NULL) return;
	post(root->right, v);
	post(root->left, v);
	v.push_back(root->data);
}
vector<int> origin, preo, prem, posto, postm;
int main(){
	int n, data;
	node* root  = NULL;//定义头节点 
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		scanf("%d", &data);
		origin.push_back(data);
		insert(root, data);//将data插入二叉树 
	} 
	pre(root, preo);
	premirror(root, prem);
	post(root, posto);
	postmirror(root, postm);
	if(origin == preo){
		printf("YES\n");
		for(int i = 0; i < posto.size(); i++){
			printf("%d", posto[i]);
			if(i < posto.size() - 1) printf(" ");
		}
	}else if(origin == prem){
		printf("YES\n");
		for(int i = 0; i < postm.size(); i++){
			printf("%d", postm[i]);
			if(i < postm.size() - 1) printf(" ");
		}
	}else{
		printf("NO\n");
	}
	return 0;
}

avl树的增删改查

//AVL平衡二叉树
//平衡二叉树仍然是一棵二叉查找树

                                                                            //平衡二叉树的定义
struct node{
	int v, height;
	node *lchild, *rchild;//左右孩子结点地址 
};

       //生成一个新结点,v为结点权值
node* newnode(int v){
	node* Node = new node;
	Node->v = v;
	Node->height = 1;
	Node->lchild = Node->rchild = NULL;
	return Node;//返回新建结点的地址 
}


           //获取以root为根结点的子树的当前height
int getheight(node* root){
	if(root = NULL) return 0;//空结点高度为0 
	return root->height;
}		   	   								

               //获取结点root的平衡因子
int getbalancefactor(node* root){
	return getheight(root->lchild) - getheight(root->rchild);
}			   											  

                  //更新结点root的height
void updateheight(node* root){
	root->height = max(getheight(root->lchild), getheight(root->rchild)) + 1;
}				   

                                                                                //AVL的基本操作
          //查找AVL树中数据域为x的结点
void search(node* root, int x){
	if(root == NULL){
		printf("search failed\n");
		return;
	}
	if(x == root->v){
		printf("%d\n", root->v);
	}else if(x < root->v){
		search(root->lchild, x);
	}else if(x > root->v){
		search(root->rchild, x);
	}
}

                                                                            //插入操作
                                                      //LL型,LR型,RR型,RL型
//左旋
void l(node* &root){
	node* temp = root->rchild;
	root->rchild = temp->lchild;
	temp->lchild = root;
	updateheight(root);//先更新下面的,再更新上面的 
	updateheight(temp);
	root = temp;
} 
//右旋
void r(node* &root){
	node* temp = root->lchild;
	root->lchild = temp->rchild;
	temp->rchild = root;
	updateheight(root);
	updateheight(temp);
	root = temp;
} 

void insert(node* &root, int v){
	if(root == NULL){//到达空结点 
		root = newnode(v);
		return;
	}
	if(v < root->v){
		insert(root->lchild, v);
		updateheight(root);
		if(getbalancefactor(root) == 2){
			if(getbalanncefactor(root->lchild) == 1){//ll型 
				r(root);
			}else if(getbalancefactor(root->lchild)== -1){//lr型 
				l(root->lchild);
				r(root);
			}
		}
	}else{
		insert(root->rchild, v);
		updateheight(root);
		if(getbalancefactor(root) == -2){
			if(getbalancefactor(root->rchild) == -1){//rr型 
				l(root);
			}else if(getbalancefactor(root->rchild) == 1){//rl型 
				r(root->rchild);
				l(root);
			
			}
		}
	}
}

                                                                      //AVL树的建立
node* create(int data[], int n){
	node* root  = NULL;
	for(int i = 0; i < n; i++){
		insert(root, data[i]);
	}
	return root;
}

并查集的查,并,路径压缩

                                                 //并查集
//并查集的基本操作
                          //初始化
int father[MAX_K + 1], height[MAX_K + 1], size[MAX_K + 1];                          
void creat(int N){
    for(int i = 1; i <= N; i++){
	father[i] = i;
	height[i] = 0;
	size[i] = 0;
    }
}
				

                                                     //查找(找到x所在集合的根结点)
int findfather(int x){
	while(x != father[x]){//直到找到father[x] == x(根结点); 
		x = father[x];//一直找父亲 
	}
	return x;
}									  					

                                                         //合并
void Union(int a, int b){
	int faA = findfather(a);
	int faB = findfather(b);
	father[faA] = faB;//直接合并 
	/*if(height[faA] < height[faB]){                                //(总是把小树合并到大树上)
		father[faA] = faB;
		size[faB] += size[faA];
	}else if(height[faB] < height[faA]){
		father[faB] = faA;
		size[faA] += size[faB]; 
	}else{
		father[faB] = faA;
		size[faA] += size[faB];
		height[faA]++;
	}*/
}                               

                                                            //路径压缩
int findfather(int v){
	if(v == father[v]) return v;//找到根结点 
	else{
		int f = findfather(father[v]);//递归寻找father[v]的根结点 
		father[v] = f;
		return f;//返回结点f 
	}
}

并查集小例题

#include<iostream>
using namespace std;
const int N = 110;
int father[N];
bool flag[N];
int findfa(int x){
	if(x == father[x]) return x;
	else{
		int f = findfa(father[x]);
		father[x] = f;
		return f;
	}
} 
void Union(int a, int b){
	int faa = findfa(a);
	int fab = findfa(b);
	if(faa != fab){
		father[faa] = fab;
	}
}
void create(int n){
	for(int i = 1; i  <= n; i++){
		father[i] = i;
		flag[i] = false;
	}
}
int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	create(n);
	for(int i = 0; i < m; i++){
		int a, b;
		scanf("%d%d", &a, &b);
		Union(a, b);
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		if(i == father[i]){
			ans++;
		}
	}
	printf("%d", ans);
	return 0;
}

堆的下坠,上浮,建立,增删,堆排序

          //堆
                            //定义堆 
const int maxn = 100;
//heap为堆,n为元素个数 
int heap[maxn], n = 10;

                            //对heap数组在[low,high]范围进行向下调整
                            //low为预调整结点的数组下标,high一般为堆的最后一个元素的下标  
void downadjust(int low, int high){//O(logn)
	int i = low, j = i*2;
	while(j <= high){//存在孩子结点 
		//如果右孩子的值比左孩子大 
		if(j+1 <= high&& heap[j+1] > heap[i]){
			j = j + 1;//让j存右孩子下标 
		}
		//如果孩子中最大的权值比预调整结点i大
		 if(heap[j] > heap[i]){
		 	swap(heap[j], heap[i]);
		 	i = j;//把预调整结点,换成最大值孩子下标 
		 	j = i*2;
		 }else{
		 	break;//孩子结点比预调整结点i小 
		 }
	}
}					

                                    //建堆
void createheap(){//O(n)
	for(int i = n/2; i >= 1; i--){//从非叶子结点,依次往顶循环 
		downadjust(i, n);
	}
}														 

                                    //删除堆顶元素
void deletetop(){//O(logn)
	heap[1] = heap[n--];//用最后一个元素覆盖堆顶元素,且让n-1 
	downadjust(1, n);//向下调整堆顶 
}									  

                                    //往堆里添加元素,向上调整
                                    //往上调整,low为1,high为预调整结点的数组下标  
void upadjust(int low, int high){//O(logn) 
	int i = high, j = i/2;//i为预调整结点,j为父亲 
	while(j >=  low){
		//父亲权值小于预调整结点i的权值 
		if(heap[j] < heap[i]){
			swap(heap[j] > heap[i]);//交换父亲和预调整结点
			i = j;//把预调整结点往上调,换成父亲下标
			j = i/2; 
		}else{
			break;//父亲权值比预调整i的权值大,调整结束 
		}
	}
}									 

                                    //添加元素x 
void insert(int x){
	heap[++n] = x;//n+1,数组末位赋值x 
	upadjust(1, n);//向上调整新加入的结点n 
}									 

                                    //堆排序,把堆从小到大排
void heapsort(){//O(nlogn) 
	createheap();
	for(int i = n; i > 1; i--){
		swap(heap[i], heap[1]);
		downadjust(1, i-1);//每次都向下调整到i的前一个位置 
	}
}

lca(最近公共祖宗)

                                                                //LCA最小公共祖宗 
#include<iostream>
#include<vector>
#include<map>
using namespace std;
map<int, bool> mp;//用来表示输入的数字是否true 
int main(){
	int m, n, u, v, a;
	scanf("%d%d", &m, &n);
	vector<int> pre(n);//用来存遍历 
	for(int i = 0; i < n; i++){
		scanf("%d", &pre[i]);
		mp[pre[i]] = true;
	}
	for(int i = 0; i < m; i++){
		scanf("%d%d", &u, &v);
		for(int j = 0; j < n; j++){ 
			a = pre[j];
			if((a < v&&a > u)||(a > v&&a < u)||(a == u||a == v)) break;//pre遍历和BST的性质,决定了a在u和v之间,则a为u,v的最近祖宗 
		}
		if(mp[u] == false&&mp[v] == false)
		    printf("ERROR: %d and %d are not found.\n", u, v);
		else if(mp[u] == false||mp[v] == false)
		    printf("ERROR: %d is not found.\n", mp[u] == false ? u : v);
		else if(a == u||a == v)
		    printf("%d is an ancestor of %d.\n", a, a == u ? v : u);
		else
		    printf("LCA of %d and %d is %d.\n", u, v, a);	    
	}
	return 0;
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值