• LCA问题: Lowest Common Ancestor


    题目链接【传送门
    之前写过一次,用的是空间换时间,也就是前序、中序构建二叉树,求解每个结点的从根结点到此结点的路径(逆序)存入map中,对查询的时候,遍历二者的路径找最远的公共元素作为输出,AC是AC了,但是这个代码量……比较累赘

    #include
    using namespace std;
    int M,N,u,v;
    vector<int>pre;
    vector<int>in;
    struct Node{
    	int val;
    	Node*left=NULL;
    	Node*right=NULL;
    	Node*parent=NULL;
    };
    Node*build(int inL,int inR,int preL,int preR){
    	if(inL>inR||preL>preR||inL<0||inR>=N||preL<0||preR>=N){
    		return NULL;
    	}
    	Node* root=new Node;
    	
    	root->val=pre[preL];
    	int k;
    	for(k=inL;k<=inR;k++){
    		if(in[k]==root->val){
    			break;
    		}
    	}
    	int num=k-inL;
    	root->left=build(inL,k-1,preL+1,preL+num);
    	root->right=build(k+1,inR,preL+num+1,preR);
    	return root;
    }
    map<int,vector<int>>path;
    map<int,Node*>nodes;
    void levelTravel(Node*root){
    	queue<Node*>q;
    	q.push(root);
    	while(!q.empty()){
    		Node*top=q.front();
    		nodes[top->val]=top;
    		q.pop();
    		if(top->left!=NULL){
    			top->left->parent=top;
    			q.push(top->left);
    		}
    		if(top->right!=NULL){
    			top->right->parent=top;
    			q.push(top->right);
    		}
    	}
    }
    int main(){
    	scanf("%d%d",&M,&N);
    	pre.resize(N);
    	in.resize(N);
    	for(int i=0;i<N;i++){
    		scanf("%d",&pre[i]);
    	}
    	in=pre;
    	sort(in.begin(),in.end()); 
    	Node*root=build(0,N-1,0,N-1);
    	levelTravel(root);
    	for(int i=0;i<M;i++){
    		scanf("%d%d",&u,&v);
    		if(find(pre.begin(),pre.end(),u)==pre.end()){
    			if(find(pre.begin(),pre.end(),v)==pre.end()){
    				printf("ERROR: %d and %d are not found.\n",u,v);
    			}else{
    				printf("ERROR: %d is not found.\n",u);
    			}
    		}else{
    			if(find(pre.begin(),pre.end(),v)==pre.end()){
    				printf("ERROR: %d is not found.\n",v);
    			}else{
    				//寻找两个结点的最近的父辈
    				int p;
    				if(path.find(u)==path.end()){
    					p=u;
    					while(nodes[p]->parent!=NULL){
    						path[u].push_back(p);
    						p=nodes[p]->parent->val;
    					}
    					reverse(path[u].begin(),path[u].end());
    				}
    				if(path.find(v)==path.end()){
    					p=v;
    					while(nodes[p]->parent!=NULL){
    						path[v].push_back(p);
    						p=nodes[p]->parent->val;
    					}
    					reverse(path[v].begin(),path[v].end());
    				}
    				int a=root->val;
    				for(int i=0;i<min(path[u].size(),path[v].size());i++){
    					if(path[u][i]==path[v][i]){
    						a=path[u][i];
    					}else{
    						break;
    					}
    				}
    				if(a==u){
    					printf("%d is an ancestor of %d.\n",a,v);
    				}else if(a==v){
    					printf("%d is an ancestor of %d.\n",a,u);
    				}else{
    					printf("LCA of %d and %d is %d.\n",u,v,a);
    				}
    			}
    		}
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    今天重写的时候发现LCA可以不用这么复杂,于是参考【LCA in Binary Trees柳神】的思路用递归的方式重构了一次……结果喜提超时了一个测试点24/30:

    #include
    using namespace std;
    int M,N,u,v;
    vector<int>pre,in;
    map<int,int>inpos;
    map<int,bool>mp;
    void LCA(int preL,int preR,int inL,int inR){
    	int rootPos=preL;
    	if(u<pre[rootPos]&&v>pre[rootPos]||u>pre[rootPos]&&v<pre[rootPos]){
    		printf("LCA of %d and %d is %d.\n",u,v,pre[rootPos]);
    	}else if(u<pre[rootPos]&&u<pre[rootPos]){
    		//左子树 
    		int k=inpos[pre[rootPos]];
    		int num=k-inL;
    		LCA(preL+1,preL+num,inL,k-1);
    	}else if(u>pre[rootPos]&&u>pre[rootPos]){
    		//右子树
    		int k=inpos[pre[rootPos]];
    		int num=k-inL;
    		LCA(preL+1+num,preR,k+1,inR); 
    	}else if(u==pre[rootPos]){
    		printf("%d is an ancestor of %d.\n",u,v);
    	}else if(v==pre[rootPos]){
    		printf("%d is an ancestor of %d.\n",v,u);
    	}
    }
    int main(){
    	scanf("%d%d",&M,&N);
    	pre.resize(N);
    	for(int i=0;i<N;i++){
    		scanf("%d",&pre[i]);
    		mp[pre[i]]=true;
    	}
    	in=pre;
    	sort(in.begin(),in.end());
    	for(int i=0;i<N;i++){
    		inpos[in[i]]=i;
    	}
    	while(M--){
    		scanf("%d%d",&u,&v);
    		if(mp.find(u)==mp.end()){
    			if(mp.find(v)==mp.end()){
    				printf("ERROR: %d and %d are not found.\n",u,v);
    			}else{
    				printf("ERROR: %d is not found.\n",u);
    			}
    		}else{
    			if(mp.find(v)==mp.end()){
    				printf("ERROR: %d is not found.\n",v);
    			}else{
    				//找到最近公共结点
    				LCA(0,N-1,0,N-1); 
    			}
    		}
    	}
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    看了柳神的此题题解,发现……还是自己对BST树了解有待提高。思路是首先用map存储所有key,对输入先判断是不是树中的结点;接着遍历pre数组,由二叉排序树的性质可知,当顺序遍历pre数组时,第一个出现pre[index]<=u&&pre[index]>=v||pre[index]>=u&&pre[index]<=v即为答案结点,按照要求输出即可

    AC代码如下(代码量和效率的双赢了属实,但是确实对性质理解不到位很难想到这个解法):

    #include
    using namespace std;
    int N,M,a,b;
    vector<int>pre;
    map<int,bool>mp;
    int main(){
    	scanf("%d%d",&M,&N);
    	pre.resize(N);
    	for(int i=0;i<N;i++){
    		scanf("%d",&pre[i]);
    		mp[pre[i]]=true;
    	}
    	while(M--){
    		scanf("%d%d",&a,&b);
    		if(mp.find(a)==mp.end()){
    			if(mp.find(b)==mp.end()){
    				printf("ERROR: %d and %d are not found.\n",a,b);
    			}else{
    				printf("ERROR: %d is not found.\n",a);	
    			}
    		}else{
    			if(mp.find(b)==mp.end()){
    				printf("ERROR: %d is not found.\n",b);
    			}else{
    				int ans;
    				for(int i=0;i<N;i++){
    					if(pre[i]<a&&pre[i]>b||pre[i]>a&&pre[i]<b||pre[i]==a||pre[i]==b){
    						ans=pre[i];
    						break;
    					}
    				}
    				if(ans!=a){
    					if(ans!=b){
    						printf("LCA of %d and %d is %d.\n",a,b,ans);
    					}else{
    						printf("%d is an ancestor of %d.\n",ans,a);
    					}
    				}else{
    					printf("%d is an ancestor of %d.\n",ans,b);
    				}
    			}
    		}
    	}
    	return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    原题如下:
    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U and V as descendants.

    A binary search tree (BST) is recursively defined as a binary tree which has the following properties:

    • The left subtree of a node contains only nodes with keys less than the node’s key.
    • The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.
    • Both the left and right subtrees must also be binary search trees.

    Given any two nodes in a BST, you are supposed to find their LCA.

    Input Specification:

    Each input file contains one test case. For each case, the first line gives two positive integers: M (≤ 1,000), the number of pairs of nodes to be tested; and N (≤ 10,000), the number of keys in the BST, respectively. In the second line, N distinct integers are given as the preorder traversal sequence of the BST. Then M lines follow, each contains a pair of integer keys U and V. All the keys are in the range of int.

    Output Specification:

    For each given pair of U and V, print in a line LCA of U and V is A. if the LCA is found and A is the key. But if A is one of U and V, print X is an ancestor of Y. where X is A and Y is the other node. If U or V is not found in the BST, print in a line ERROR: U is not found. or ERROR: V is not found. or ERROR: U and V are not found..

    Sample Input:

    6 8
    6 3 1 2 5 4 8 7
    2 5
    8 7
    1 9
    12 -3
    0 8
    99 99
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Sample Output:

    LCA of 2 and 5 is 3.
    8 is an ancestor of 7.
    ERROR: 9 is not found.
    ERROR: 12 and -3 are not found.
    ERROR: 0 is not found.
    ERROR: 99 and 99 are not found.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    【Redis】基于Spring Cache + Redis + Jackson的注解式自动缓存方案保姆式教程(2022最新)
    JavaScript设计模式——命令模式
    外贸人常用的几种客户开发渠道
    索引与事务
    Fast unsupervised embedding learning with anchor-based
    Autograd解析|OneFlow学习笔记
    Python爬虫程序网络请求及内容解析
    【jQuery】jQuery操作之如何查找元素_02
    屏幕提词软件Presentation Prompter mac中文版使用方法
    Istio 入门(七):出入口网关 - 负载均衡和熔断等一系列功能
  • 原文地址:https://blog.csdn.net/qq_45751990/article/details/126919953