Java數(shù)據(jù)結構之線段樹中的懶操作詳解
一、問題提出
對于線段樹,若要求對區(qū)間中的所有點都進行更新,可以引入懶操作。
懶操作包括區(qū)間更新和區(qū)間查詢操作。
二、區(qū)間更新
對 [l,r] 區(qū)間進行更新,例如將 [l,r] 區(qū)間所有元素都更新為 v,步驟如下。
1.若當前節(jié)點區(qū)間被查詢區(qū)間[l,r]覆蓋,則僅對該節(jié)點進行更新并做懶標記,表示該節(jié)點已被更新,對該節(jié)點的子節(jié)點暫不更新。
2.判斷是在左子樹中查詢還是在右子樹中查詢。在查詢過程中,若當前節(jié)點帶有懶標記,則將懶標記傳給子節(jié)點(將當前節(jié)點的懶標記清除,將子節(jié)點更新并做懶標記),繼續(xù)查找。
3.在返回時更新最值。
三、區(qū)間查詢
帶有懶標記的區(qū)間查詢和普通的區(qū)間查詢有所不同,在查詢過程中若遇到節(jié)點有懶標記,則下傳懶標記,繼續(xù)查詢。
四、實戰(zhàn)
1.問題描述

2.輸入
10
5 3 7 2 12 1 6 4 8 15
3 7 25
1 10
3.代碼
package com.platform.modules.alg.alglib.p85;
public class P85 {
public String output = "";
private final int maxn = 100005;
private final int inf = 0x3f3f3f3f;
private int n;
private int a[] = new int[maxn];
private node tree[] = new node[maxn * 4]; // 樹結點存儲數(shù)組
public P85() {
for (int i = 0; i < tree.length; i++) {
tree[i] = new node();
}
}
void lazy(int k, int v) {
tree[k].mx = v; // 更新最值
tree[k].lz = v; // 做懶標記
}
// 向下傳遞懶標記
void pushdown(int k) {
lazy(2 * k, tree[k].lz); // 傳遞給左孩子
lazy(2 * k + 1, tree[k].lz); // 傳遞給右孩子
tree[k].lz = -1; // 清除自己的懶標記
}
// 創(chuàng)建線段樹,k表示存儲下標,區(qū)間[l,r]
void build(int k, int l, int r) {
tree[k].l = l;
tree[k].r = r;
tree[k].lz = -1; // 初始化懶操作
if (l == r) {
tree[k].mx = a[l];
return;
}
int mid, lc, rc;
mid = (l + r) / 2; // 劃分點
lc = k * 2; // 左孩子存儲下標
rc = k * 2 + 1; // 右孩子存儲下標
build(lc, l, mid);
build(rc, mid + 1, r);
tree[k].mx = Math.max(tree[lc].mx, tree[rc].mx); // 結點的最大值等于左右孩子最值的最大值
}
// 將區(qū)間 [l,r] 修改更新為 v
void update(int k, int l, int r, int v) {
// 找到該區(qū)間
if (tree[k].l >= l && tree[k].r <= r) {
lazy(k, v); // 更新并做懶標記
return;
}
if (tree[k].lz != -1)
pushdown(k); // 懶標記下移
int mid, lc, rc;
mid = (tree[k].l + tree[k].r) / 2; // 劃分點
lc = k * 2; // 左孩子存儲下標
rc = k * 2 + 1; // 右孩子存儲下標
if (l <= mid)
update(lc, l, r, v); // 到左子樹更新
if (r > mid)
update(rc, l, r, v);// 到右子樹更新
tree[k].mx = Math.max(tree[lc].mx, tree[rc].mx); // 返回時修改更新最值
}
// 求區(qū)間 [l,r] 的最值
int query(int k, int l, int r) {
int Max = -inf;
if (tree[k].l >= l && tree[k].r <= r) // 找到該區(qū)間
return tree[k].mx;
if (tree[k].lz != -1)
pushdown(k); // 懶標記下移
int mid, lc, rc;
mid = (tree[k].l + tree[k].r) / 2; // 劃分點
lc = k * 2; // 左孩子存儲下標
rc = k * 2 + 1; // 右孩子存儲下標
if (l <= mid)
Max = Math.max(Max, query(lc, l, r)); // 到左子樹查詢
if (r > mid)
Max = Math.max(Max, query(rc, l, r)); // 到右子樹查詢
return Max;
}
public String cal(String input) {
int l, r;
int i, v;
String[] line = input.split("\n");
n = Integer.parseInt(line[0]); // 第 1 行:10
String[] nums = line[1].split(" ");
// 第 2 行:5 3 7 2 12 1 6 4 8 15
for (i = 1; i <= n; i++)
a[i] = Integer.parseInt(nums[i - 1]);
build(1, 1, n); // 創(chuàng)建線段樹
// 輸入查詢最值的區(qū)間 [l,r] 1 10
String[] range = line[2].split(" ");
l = Integer.parseInt(range[0]);
r = Integer.parseInt(range[1]);
// 求區(qū)間[l..r]的最值
output += query(1, l, r) + "\n";
// 輸入更新的區(qū)間值 l r v 第 3 行: 3 7 25
String[] change = line[3].split(" ");
l = Integer.parseInt(change[0]);
r = Integer.parseInt(change[1]);
v = Integer.parseInt(change[2]);
// 將區(qū)間 [l,r] 修改更新為 v
update(1, l, r, v);
// 求區(qū)間[l..r]的最值 第 4 行:1 10
range = line[4].split(" ");
l = Integer.parseInt(range[0]);
r = Integer.parseInt(range[1]);
// 求區(qū)間 [l..r] 的最值
output += query(1, l, r) + "\n";
return output;
}
}
// 結點
class node {
int l; // l 表示區(qū)間左右端點
int r; // r 表示區(qū)間左右端點
int mx; // mx表示區(qū)間[l,r]的最值
int lz; // lz 懶標記
}
4.測試

以上就是Java數(shù)據(jù)結構之線段樹中的懶操作詳解的詳細內容,更多關于Java線段樹 懶操作的資料請關注腳本之家其它相關文章!
相關文章
spring-data-redis自定義實現(xiàn)看門狗機制
redission看門狗機制是解決分布式鎖的續(xù)約問題,本文主要介紹了spring-data-redis自定義實現(xiàn)看門狗機制,具有一定的參考價值,感興趣的可以了解一下2024-03-03
Java中的UrlDecoder 和 UrlEncoder_動力節(jié)點Java學院整理
HTML 格式編碼的實用工具類。該類包含了將 String 轉換為 application/x-www-form-urlencoded MIME 格式的靜態(tài)方法。下文通過實例代碼給大家介紹Java中的UrlDecoder 和 UrlEncoder知識,感興趣的的朋友一起看看吧2017-07-07
Java數(shù)組中的元素刪除并實現(xiàn)向前移的代碼
這篇文章主要介紹了Java數(shù)組中的元素刪除并實現(xiàn)向前移的代碼的相關資料,需要的朋友可以參考下2016-05-05

