笔试问你线段树你却不会?

本文首发自本人微信公众号:今天你A了吗。每日算法讲解,面试题,冲刺BAT大厂。微信扫码关注吧:

在这里插入图片描述

线段树:例题

1 前言

在上一章:《线段树:讲解与模版》中,我们讨论了线段树的原理并且给予了一套模版。我们在文中也提到,每个节点包含数组下标在 [i, j]范围内的聚合信息(如最大值,最小值,总和等)。具体细节大家可以点击上一篇文章进行查看。

这次,我们找到了一道题:杭电 1166:敌兵布阵(链接在本文末尾)。对线段树的应用进行讲解。本文中线段树维护范围中数组的和。

2 题目概述

给定一个一位数组代表各个军营将士的数量数量,给定三种方法:

  • Add ai i 代表对数组中ai位置的元素加上i
  • Sub ai i 代表对数组中ai位置的元素减去i
  • Query i j 代表查询数组中下标i到j的和

本题目的核心操作是求数组的范围和,并且涉及到对数组元素进行改变。本题可以使用树状数组进行解题,效果更快,但是树状数组并不适合范围更新(只适合单点更新)。为了加深大家对上篇文章的理解,本文使用线段树进行解答。线段树有着更加广阔的适用范围。

3 解答

3.1 建树

(1)首先对数组进行输入, 并且开辟tree数组用于存储线段树

nums = new int[n+1]; //数组下标从1开始与tree数组下标相对应
tree = new int[n*4]; //4倍数足够大

(2)处理输入的字符串Add, Sub, Query, sub只需要把add的第二个参数变成负数即可。

public static void processIn(String in, int num1, int num2){
        if(in.equals("Add")){
            update(1, 1, n, num1, num2);
        }else if(in.equals("Sub")){
            update(1, 1, n, num1, -num2);
        }else{
            int res = query(1, 1, n, num1, num2);
            System.out.println(res);
        }
    }

(3)与上篇文章一样,线段树的建树,更新,查询函数。注意:这里的更新不再是值的直接覆盖,而是把数组中的数加上和减去某个数。n是nums数组的长度。

public static  void build(int root, int l, int r){
        if(l == r){
            tree[root] = nums[l];
            return;
        }
        int mid = l + (r - l) / 2;
        build(root * 2, l, mid);
        build(root * 2 + 1, mid + 1, r);
        tree[root] = tree[root * 2] + tree[root * 2 + 1];
    }

    public static void update(int root, int l, int r, int key, int val){
        if(l == r){
            if(l == key)
                tree[root] += val;
            return;
        }
        if(key < l || key > r) return;
        int mid = l + (r - l) / 2;
        update(root * 2, l, mid, key, val);
        update(root * 2 + 1, mid + 1, r, key, val);
        tree[root] = tree[root * 2] + tree[root * 2 + 1];
    }



    public static int query(int root, int l, int r, int ql, int qr){
        if(l >= ql && r <= qr) return tree[root];
        if(ql > r || qr < l) return 0;
        int mid = l + (r - l) / 2;
        int left_sum = query(root * 2, l, mid, ql, qr);
        int right_sum = query(root * 2 + 1, mid + 1, r, ql, qr);
        return left_sum + right_sum;
    }

(4)代码汇总

package multiThreading;

import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main{
    public static int[] nums;
    public static int[] tree;
    public static int n;


    public static  void build(int root, int l, int r){
        if(l == r){
            tree[root] = nums[l];
            return;
        }
        int mid = l + (r - l) / 2;
        build(root * 2, l, mid);
        build(root * 2 + 1, mid + 1, r);
        tree[root] = tree[root * 2] + tree[root * 2 + 1];
    }

    public static void update(int root, int l, int r, int key, int val){
        if(l == r){
            if(l == key)
                tree[root] += val;
            return;
        }
        if(key < l || key > r) return;
        int mid = l + (r - l) / 2;
        update(root * 2, l, mid, key, val);
        update(root * 2 + 1, mid + 1, r, key, val);
        tree[root] = tree[root * 2] + tree[root * 2 + 1];
    }



    public static int query(int root, int l, int r, int ql, int qr){
        if(l >= ql && r <= qr) return tree[root];
        if(ql > r || qr < l) return 0;
        int mid = l + (r - l) / 2;
        int left_sum = query(root * 2, l, mid, ql, qr);
        int right_sum = query(root * 2 + 1, mid + 1, r, ql, qr);
        return left_sum + right_sum;
    }

    public static void processIn(String in, int num1, int num2){
        if(in.equals("Add")){
            update(1, 1, n, num1, num2);
        }else if(in.equals("Sub")){
            update(1, 1, n, num1, -num2);
        }else{
            int res = query(1, 1, n, num1, num2);
            System.out.println(res);
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int T = sc.nextInt();

        for(int round = 1; round <= T; round++){
            n = sc.nextInt();
            nums = new int[n+1]; //数组下标从1开始与tree数组下标相对应
            tree = new int[n*4]; //4倍数足够大
            for(int i = 1; i <= n; i++){
                nums[i] = sc.nextInt();
            }

            build(1, 1, n);

            System.out.println("Case "+ round + ":");
            while (true){
                String s = sc.next();
                if(s.equals("End"))
                    break;
                int num1 = sc.nextInt();
                int num2 = sc.nextInt();
                processIn(s, num1, num2);
            }
            
        }

    }
}

4 题目链接

HDU 1166 敌兵布阵: http://acm.hdu.edu.cn/showproblem.php?pid=1166
线段树(讲解与模版)链接:链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值