Java数据结构之线段树的原理与实现

Java数据结构之线段树的原理与实现

什么是线段树

线段树是一种基于分治思想的数据结构,它可以用来解决各种区间查询问题,例如区间求和、最大值、最小值等等。在算法竞赛和数据结构课程中,线段树被广泛应用,是一种非常实用的数据结构。

线段树的基本原理

线段树是一种二叉树,它的每个节点包含一个区间,叶子节点表示区间中的单个元素,非叶子节点表示区间的合并。

线段树的建立过程分为三步:

  1. 从上到下递归地将区间划分为两个子区间。
  2. 当划分到区间长度为1时,将该节点标记为叶子节点,将该位置对应的数值存入该叶子节点。
  3. 从下到上递归地合并每个节点的信息。例如求和的话,每个节点需要记录一个区间的和。

查询的过程也分为三步:

  1. 先找到包含查询区间的最小区间。
  2. 对于覆盖完全的区间,直接返回该区间的信息,否则,递归地查询该节点的子节点。
  3. 将子节点的信息合并,得到查询区间的结果。

修改操作则是找到该位置对应的叶子节点,然后递归地重新合并所有受到该修改操作影响的节点的信息。

编程实现

我们可以使用Java来实现线段树的建立和查询操作。下面是一个示例代码:

class SegmentTree {
    int n;
    int[] nums;
    int[] tree;

    public SegmentTree(int[] nums) {
        this.n = nums.length;
        this.nums = nums;
        this.tree = new int[n * 4];
        build(0, 0, n - 1);
    }

    private void build(int node, int left, int right) {
        if (left == right) {
            tree[node] = nums[left];
        } else {
            int mid = left + (right - left) / 2;
            build(node * 2 + 1, left, mid);
            build(node * 2 + 2, mid + 1, right);
            tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2];
        }
    }

    public int query(int left, int right) {
        return query(0, 0, n - 1, left, right);
    }

    private int query(int node, int nodeLeft, int nodeRight, int queryLeft, int queryRight) {
        if (nodeLeft >= queryLeft && nodeRight <= queryRight) {
            return tree[node];
        } else if (nodeRight < queryLeft || nodeLeft > queryRight) {
            return 0;
        } else {
            int mid = nodeLeft + (nodeRight - nodeLeft) / 2;
            return query(node * 2 + 1, nodeLeft, mid, queryLeft, queryRight)
                    + query(node * 2 + 2, mid + 1, nodeRight, queryLeft, queryRight);
        }
    }

    public void update(int index, int val) {
        update(0, 0, n - 1, index, val);
    }

    private void update(int node, int nodeLeft, int nodeRight, int index, int val) {
        if (nodeLeft == nodeRight) {
            tree[node] = val;
        } else {
            int mid = nodeLeft + (nodeRight - nodeLeft) / 2;
            if (index <= mid) {
                update(node * 2 + 1, nodeLeft, mid, index, val);
            } else {
                update(node * 2 + 2, mid + 1, nodeRight, index, val);
            }
            tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2];
        }
    }
}

上面的代码实现了线段树的基本功能,包括建树、查询和修改。可以根据实际需求对其进行更改。

示例说明1:使用线段树查询数组的区间和

下面是一个使用线段树查询数组的区间和的示例代码:

public class Main {
    public static void main(String[] args) {
        int[] nums = {1, 3, 5, 7, 9, 11};
        SegmentTree tree = new SegmentTree(nums);
        System.out.println(tree.query(0, 2)); // 1 + 3 + 5 = 9
        tree.update(1, 2);
        System.out.println(tree.query(0, 2)); // 1 + 2 + 5 = 8
    }
}

该示例中,我们将数组 {1, 3, 5, 7, 9, 11} 转化为线段树,并查询区间 [0, 2] 的和。然后我们将该数组的第二个元素修改为2,之后再次查询区间 [0, 2] 的和。控制台将输出9和8。

示例说明2:使用线段树查询数组的区间最大值

下面的示例代码演示了如何使用线段树查询数组的区间最大值:

public class Main {
    static class Node {
        int val;
        int start, end;

        public Node(int val, int start, int end) {
            this.val = val;
            this.start = start;
            this.end = end;
        }
    }

    public static void main(String[] args) {
        int[] nums = {1, 3, 5, 2, 4, 6, 8};
        List<Node> list = new ArrayList<>(nums.length);
        for (int i = 0; i < nums.length; i++) {
            Node node = new Node(nums[i], i, i);
            list.add(node);
        }

        SegmentTree tree = new SegmentTree(list.stream().mapToInt(n -> n.val).toArray());

        IntStream.range(0, nums.length - 1).forEach(i -> {
            Node left = list.get(i);
            Node right = list.get(i + 1);
            if (left.val > right.val) {
                left.end = right.start;
            }
        });

        int ans = 0;
        for (Node node : list) {
            ans = Math.max(ans, tree.query(node.start, node.end));
        }

        System.out.println(ans);
    }
}

该示例中,我们将数组 {1, 3, 5, 2, 4, 6, 8} 转化为线段树,并查询其中的区间最大值。我们还通过修改线段树节点信息来查找每个局部最大值的起始和结束位置。控制台将输出 6,表示数组中的最大值为6。

总结

线段树是一种实用而优秀的数据结构,它可以用来解决区间问题。建树、查询和修改等操作都能快速实现,并且时间复杂度为 O(log n)。在算法竞赛和数据结构课程中,掌握线段树的原理和实现是必不可少的。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构之线段树的原理与实现 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 考研数据结构模板:顺序表、链表、栈、队列

    考研数据结构模板:顺序表、链表、栈、队列 前言 代码风格偏向于考研风格而非算法竞赛风格。 代码实现参考《2024数据结构王道复习指导》。 注释详细、保证看懂。 下面是已实现的数据结构模板: 顺序表SeqList 链表LinkList 双链表DLinkList 顺序栈SeqStack 循环顺序队列CircleQueue 链队列LinkQueue 顺序表SeqL…

    算法与数据结构 2023年4月17日
    00
  • 一文带你走进js数据类型与数据结构的世界

    一文带你走进JS数据类型与数据结构的世界 一、JS数据类型 1. 原始类型 JS中原始类型包括:Undefined、Null、Boolean、Number、String、Symbol(ES6新增)。 其中Undefined和Null分别代表未定义和空值,Boolean表示布尔值,Number表示数字,String表示字符串,Symbol是ES6新增的一种基础…

    数据结构 2023年5月17日
    00
  • 「学习笔记」AC 自动机

    「学习笔记」AC 自动机 点击查看目录 目录 「学习笔记」AC 自动机 算法 问题 思路 代码 例题 Keywords Search 玄武密码 单词 病毒 最短母串 文本生成器 背单词 密码 禁忌 前置:「学习笔记」字符串基础:Hash,KMP与Trie。 好像对例题的讲解越来越抽象了? 算法 问题 求 \(n\) 个单词在一个长度为 \(m\) 的文章里出…

    算法与数据结构 2023年5月5日
    00
  • Java数据结构之双向链表图解

    以下是Java数据结构之双向链表图解的完整攻略: 一、双向链表简介 1.1 定义 双向链表(Doubly Linked List),也叫双向链式线性表,是链式存储结构的基本形式之一。双向链表的每个节点都含有两个指针,分别指向前驱节点和后继节点,因此可以支持双向遍历。 1.2 结构 双向链表的结构可以用下图表示: +——-+ +——-+ +–…

    数据结构 2023年5月17日
    00
  • PHP常用算法和数据结构示例(必看篇)

    PHP常用算法和数据结构示例(必看篇)攻略 在这篇文章中,我们将会学习一些PHP常用的算法和数据结构,并通过一些示例来说明它们的应用场景和使用方法。 1. 哈希表 哈希表是一种常用的数据结构,它根据关键码值(Key Value)而直接进行访问的数据结构。哈希表通常用于实现关联数组。PHP中提供了内置的哈希表数据结构Map和Array。 1.1 使用Map实现…

    数据结构 2023年5月17日
    00
  • Java实题演练二叉搜索树与双向链表分析

    Java实题演练二叉搜索树与双向链表分析 题目描述 给定一个二叉搜索树,将它转换成一个排序的双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 思路分析 对于一颗二叉搜索树,有以下性质: 若左子树不为空,则左子树上所有结点的值均小于它的根结点的值; 若右子树不为空,则右子树上所有结点的值均大于它的根结点的值; 左右子树也为二叉搜索树。 我们考虑…

    数据结构 2023年5月17日
    00
  • 手写 Vue3 响应式系统(核心就一个数据结构)

    下面是手写 Vue3 响应式系统的完整攻略。 1. 概述 Vue3 的响应式系统使用了 Proxy 对象来监测对象的变化,相较于 Vue2 的响应式系统使用 Object.defineProperty 进行数据劫持,Proxy 具有更好的性能和更简洁的 API。 当我们修改 Vue3 中的 reactive 对象内部的数据时,就会触发依赖收集和派发更新的操作…

    数据结构 2023年5月17日
    00
  • C语言数据结构之复杂链表的拷贝

    C语言数据结构之复杂链表的拷贝 什么是复杂链表 在了解如何拷贝复杂链表之前,首先需要知道什么是复杂链表。复杂链表是由多个节点组成的链表,每个节点除了包含普通链表节点的值和指向下一个节点的指针外,还包含一个指向链表中的任意一个节点的指针。因此,每个节点有两个指针:一个指向下一个节点,一个指向任意一个节点。 复杂链表示意图如下: +—+ +—+ +—…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部