hiho一下 第十九周 RMQ问题再临-线段树

相比于之前的题目,数据量扩大为10^6,因此使用线段树来实现

有关线段树的详细分析

用链表的方式实现

#include <stdio.h>
#define MAX_N 1000000
#define MAX_NODE 2000000 //一个二叉树有N个叶子节点,则总的节点数为2N-1 

typedef struct NODE{
	struct NODE *left, *right, *father;
	int value, l ,r;
}Node;

int NodeCount;
Node N[MAX_NODE];
Node* Index[MAX_N + 1];

Node* GetNode(){
	return &N[NodeCount++];
}

int Min(int a, int b){
	return a < b ? a : b;
}

Node* Creat(int i, int j){
	Node *p = GetNode();
	if(i == j){
		p -> left = NULL;
		p -> right = NULL;
		scanf("%d", & p -> value);
		Index[i] = p;
	}else{
		p -> left = Creat(i, (i+j)/2);
		p -> right = Creat((i+j)/2+1, j);
		p -> value = Min(p -> left -> value, p -> right -> value);
		p -> left -> father = p;
		p -> right -> father = p;
	}
	p -> l = i;
	p -> r = j;
	return p;
}

void Adjust(Node *p){
	if(p == NULL){
		return;
	}
	int temp = Min(p -> left -> value, p -> right -> value);
	if(p -> value != temp){
		p -> value = temp; 
		Adjust(p -> father);
	}
}

int Query(Node* p, int i, int j){
	if(i == p -> l && j == p -> r){
		return p -> value;
	}
	int mid = (p -> l + p -> r)/2;
	if(j <= mid){
		return Query(p -> left, i, j);
	}
	if(i >= mid + 1){
		return Query(p -> right, i, j);
	}
	return Min(Query(p -> left, i, mid), Query(p -> right, mid + 1, j));
}

int main(){
	int n, i, Q, a, b, c;
	Node *root;
	
	NodeCount = 0;
	
	scanf("%d", &n);
	root = Creat(1, n);
	root -> father = NULL;
	
	scanf("%d", &Q);
	while(Q--){
		scanf("%d%d%d", &a, &b, &c);
		if(a == 0){
			printf("%d\n", Query(root, b, c));
		}else{
			Index[b] -> value = c;
			Adjust(Index[b] -> father); 
		}
	}
	
	return 0;
} 

减少数据的存储空间,但时间上慢一点

#include <stdio.h>
#include <stdlib.h>
#define MAX_N 1000000
#define MAX_NODE 2000000 //一个二叉树有N个叶子节点,则总的节点数为2N-1 

typedef struct NODE{
	struct NODE *left, *right;
	int value;
}Node;

int Min(int a, int b){
	return a < b ? a : b;
}

Node* Creat(int i, int j){
	Node *p = (Node*)malloc(sizeof(Node));
	if(i == j){
		p -> left = NULL;
		p -> right = NULL;
		scanf("%d", & p -> value);
	}else{
		p -> left = Creat(i, (i+j)/2);
		p -> right = Creat((i+j)/2+1, j);
		p -> value = Min(p -> left -> value, p -> right -> value);
	}
	return p;
}

//深度搜索并更新 
void Adjust(Node *p, int l, int r, int k, int w){//搜索节点,节点区间,将下标k的weight改为w 
	if(l == r){
		p -> value = w;
		return;
	}
	int mid = (l+r)/2, temp;
	if(k <= mid){
		Adjust(p -> left, l, mid, k, w);
	}else{
		Adjust(p -> right, mid+1, r, k, w);
	}
	p -> value = Min(p -> left -> value, p -> right -> value);
}

int Query(Node* p, int l, int r, int i, int j){//查询的节点,节点代表的区间,查询的区间 
	if(i == l && j == r){
		return p -> value;
	}
	int mid = (l + r)/2;
	if(j <= mid){
		return Query(p -> left, l, mid, i, j);
	}
	if(i >= mid + 1){
		return Query(p -> right, mid+1, r, i, j);
	}
	return Min(Query(p -> left, l, mid, i, mid), Query(p -> right, mid+1, r, mid + 1, j));
}

int main(){
	int n, i, Q, a, b, c;
	Node *root;
	
	scanf("%d", &n);
	root = Creat(1, n);
	
	scanf("%d", &Q);
	while(Q--){
		scanf("%d%d%d", &a, &b, &c);
		if(a == 0){
			printf("%d\n", Query(root, 1, n, b, c));
		}else{
			Adjust(root, 1, n, b, c); 
		}
	}
	
	return 0;
} 

更加机智的方法就是用数组来实现,因为用数组,所以要构造的是一棵满二叉树,需要把N拓展为2^x(N<=2^x)的形式,这样访问一个节点的左右孩子就可以通过下标来访问,时间节省很多

#include <stdio.h>
#define INF 1<<30

int ST[2097152];//最多2^21-1个节点,注意不是2*N-1个节点,[0]不用 

int Min(int a, int b){
	return a < b ? a : b;
}

int main(){
	int N, Q, A, B, i, ans, a, b, c;
	
	scanf("%d", &N);
	for(A = 1; A < N; A <<= 1);
	for(i = A; i < A+N; i++){
		scanf("%d", &ST[i]);
	}
	for(i = A+N, B = A << 1; i < B; i++){
		ST[i] = INF;
	}
	//build segment tree
	for(i = A-1; i; i--){
		ST[i] = Min(ST[i << 1], ST[(i << 1) + 1]);
	}
	
	scanf("%d", &Q);
	while(Q--){
		scanf("%d%d%d", &a, &b, &c);
		if(a == 0){
			//query
			ans = INF;
			b = b + A - 1;
			c = c + A - 1;
			while(b <= c){
				if(b == c){
					ans = Min(ans, ST[b]);
					break;
				}
				if(b&1){
					ans = Min(ans, ST[b]);
					b++;
				}
				if((c&1) == 0){
					ans = Min(ans, ST[c]);
					c--;
				}
				b >>= 1;
				c >>= 1;
			}
			printf("%d\n", ans);
		}else{
			//update
			b = b + A - 1;
			ST[b] = c;
			b >>= 1;
			while(b){
				ans = Min(ST[b << 1], ST[(b << 1) +1]);
				if(ST[b] == ans){
					break;
				}
				ST[b] = ans;
				b >>= 1;
			}
		}
	}
	return 0;
} 
另外,在一位大神的代码中发现了一种节省时间的做法,效果非常明显,就是将scanf(“%d”, & ……)替换为F(),

int aa;char ch;int F(){
    while(ch=getchar(),ch<48||ch>57);aa=ch-48;
    while(ch=getchar(),ch>47&&ch<58)aa=(aa<<1)*5+ch-48;return aa;
}

学习了!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值