线段树(区间树)、字典树(Trie)的JAVA代码实现及介绍


一、线段树(区间树)

1.解释

这里引入百度百科的解释:

  • 区间树的核心不是操作,而是区间
  • 时间复杂度 查询操作 数组:O(n) 区间树:O(log n)
  • 区间树不是一个完全二叉树
  • 是一个平衡二叉树

推荐一篇UP主的博客:具有更详细的介绍
线段树

2.代码实现

难度还是有的,需要多揣摩,递归思想

package com.lingo.segment;


import java.util.Arrays;

public class SegmentTreeT<E> {

    private E[] data;
    private E[] segmentArr;
    private Merge<E> merge;

    public SegmentTreeT(E[] arr, Merge<E> merge) {
        this.merge = merge;
        data = (E[]) new Object[arr.length];
        for (int i = 0; i < arr.length; i++) {
            data[i] = arr[i];
        }
        //建树  以空间换时间  假如最底层节点数为x  且如果满时  树的总共用大约2*x
        //而不能确保arr.length正好铺满底层 所以*4
        segmentArr = (E[]) new Object[arr.length * 4];
        buildSegmentTree(0, 0, arr.length - 1);
    }


    private void buildSegmentTree(int index, int l, int r) {
        //递归中止条件
        if (l == r) {
            segmentArr[index] = data[l];
            return;
        }
        int leftChild = leftChild(index);
        int rightChild = rightChild(index);
        int mid = l + (r - l) / 2;

        //向左找
        buildSegmentTree(leftChild, l, mid);
        //向右找
        buildSegmentTree(rightChild, mid + 1, r);

        segmentArr[index] = this.merge.merge(segmentArr[leftChild], segmentArr[rightChild]);

    }

    private int leftChild(int index) {
        return 2 * index + 1;
    }

    private int rightChild(int index) {
        return 2 * index + 2;
    }

    public E get(int index) {
        if (index < 0 || index >= data.length) {
            throw new IllegalArgumentException("参数不合法");
        }
        return data[index];
    }

    public int getSizr() {
        return data.length;
    }

    public void set(int index, E newValue) {
        if (index < 0 || index >= data.length) {
            throw new IllegalArgumentException("参数越界");
        }
        set(0, 0, data.length - 1, index, newValue);
    }

    private void set(int index, int l, int r, int setIndex, E newValue) {
        //这里需要存元素
        if (l == r) {
            segmentArr[index] = newValue;
            return;
        }

        int leftChild = leftChild(index);
        int rightChild = rightChild(index);
        int mid = l + (r - l) / 2;
        if (setIndex <= mid) {
            set(leftChild, l, mid, setIndex, newValue);
        } else {
            set(rightChild, mid + 1, r, setIndex, newValue);
        }
        segmentArr[index] = merge.merge(segmentArr[leftChild], segmentArr[rightChild]);

    }

    public E query(int queryL, int queryR) {
        if (queryL < 0 || queryR >= data.length || queryL > queryR) {
            throw new IllegalArgumentException("参数不合法");
        }
        return query(0, 0, data.length - 1, queryL, queryR);
    }

    private E query(int index, int l, int r, int queryL, int queryR) {
        if (l == queryL && r == queryR) {
            return segmentArr[index];
        }
        int leftChild = leftChild(index);
        int rightChild = rightChild(index);
        int mid = l + (r - l) / 2;
        if (queryR <= mid) {
            return query(leftChild, l, mid, queryL, queryR);
        }
        if (queryL > mid) {
            return query(rightChild, mid + 1, r, queryL, queryR);
        }
        //这里的左边界仍为l 右边界为mid 搜索的范围  左:queryL 右:mid
        //这里容易进坑哦
        E leftResult = query(leftChild, l, mid, queryL, mid);
        //这里的右边界仍为r 左边界为mid+1 搜索的范围  左:mid+1 右:queryR
        //这里容易进坑哦
        E rightResult = query(rightChild, mid + 1, r, mid + 1, queryR);
        return merge.merge(leftResult, rightResult);
    }

    @Override
    public String toString() {
        return "segmentArr=" + Arrays.toString(segmentArr);
    }

    public static void main(String[] args) {
        Integer[] arr = {1, 5, 62, 20, 32, 8, 9, 4, 6, 11, 44, 99, 85};
        SegmentTreeT<Integer> integerSegmentTreeT = new SegmentTreeT<>(arr, new Merge<Integer>() {
            @Override
            public Integer merge(Integer e1, Integer e2) {
                return e1 + e2;
            }
        });
        System.out.println("建树成功返回线段树:");
        System.out.println(integerSegmentTreeT);
        System.out.println("将5换成15后线段树:");
        integerSegmentTreeT.set(1, 15);
        System.out.println(integerSegmentTreeT);
        System.out.println("获取下标从0到6的和:");
        Integer query = integerSegmentTreeT.query(0, 6);
        System.out.println(query);
    }
}

输出结果:

建树成功返回线段树:
segmentArr=[386, 137, 249, 88, 49, 21, 228, 6, 82, 40, 9, 10, 11, 143, 85, 1, 5, 62, 20, 32, 8, null, null, 4, 6, null, null, 44, 99, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
将5换成15后线段树:
segmentArr=[396, 147, 249, 98, 49, 21, 228, 16, 82, 40, 9, 10, 11, 143, 85, 1, 15, 62, 20, 32, 8, null, null, 4, 6, null, null, 44, 99, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
获取下标从0到6的和:
147

二、字典树(前缀树)

1.简单介绍

  1. 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。
  2. 典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
  3. 它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

推荐一篇UP主的博客:具有更详细的介绍
字典树(前缀树)

2.代码实现

package com.lingo.tree;

import java.util.Map;
import java.util.TreeMap;

/**
 * 字典树   前缀树   存放用字母来举例
 */
public class Trie {
    private class Node {
        public boolean isWord;
        public Map<Character, Node> next;

        public Node(boolean isWord) {
            this.isWord = isWord;
            next = new TreeMap<>();
        }

        public Node() {
            this(false);
        }
    }

    private int size;
    private Node root;

    public Trie() {
        root = new Node();
        size = 0;
    }

    public int getSize() {
        return size;
    }

    //添加单词
    public void add(String word) {
        Node current = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (current.next.get(c) == null) {
                current.next.put(c, new Node());
            }
            current = current.next.get(c);
        }
        if (current.isWord == false) {
            current.isWord = true;
            size++;
        }
    }

    //判断是否包含

    public boolean contains(String word) {
        Node current = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (current.next.get(c) == null) {
                return false;
            }
            current = current.next.get(c);
        }
//        if (current.isWord==false){
//            return false;
//        }
        return current.isWord;

    }

    /**
     * 前缀搜索
     *
     * @param prefix 字符串前缀
     * @return 返回是否包含前缀
     */
    public boolean isPrefix(String prefix) {

        Node current = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if (current.next.get(c) == null) {
                return false;
            }
            current = current.next.get(c);
        }
        return true;
    }

    /**
     * 模式匹配
     *
     * @param word         待匹配的字符串
     * @param standardChar d.g  .代表任意字符,
     * @return
     */
    public boolean search(String word, char standardChar) {
        return search(root, 0, word, standardChar);
    }

    ;

    private boolean search(Node node, int index, String word, char standardChar) {

        //递归中止条件
        if (index == word.length()) {
            //已经匹配完成了 前面都为true;
            //pattern模式匹配   返回的应该是  是否含有该种模式的单词  如果说含有字符 无单词自然返回fasle
            return node.isWord;
        }

        char c = word.charAt(index);
        if (c != standardChar) {
            if (node.next.get(c) == null) {
                return false;
            } else {
                return search(node.next.get(c), index + 1, word, standardChar);
            }
        } else {
            for (Character character : node.next.keySet()) {
                if (search(node.next.get(character), index + 1, word, standardChar)) {
                    return true;
                }
                return false;
            }
            //for循环完成之后都还没有进行返回true,必然不存在
            return false;
        }


    }
}


测试类:

    public static void main(String[] args){
        Trie trie = new Trie();
        trie.add("apple");
        trie.add("dog");
        trie.add("trie");
        trie.add("tree");
        System.out.println(trie.contains("tree"));
        System.out.println(trie.contains("app"));
        System.out.println(trie.search("a.p",'.'));
        trie.add("app");
        System.out.println(trie.search("a.p",'.'));
    }

输出结果:

true
false
false
true


三、探讨学习

当然,不排除博客中所涉及的代码可能仍存在性能问题或BUG类问题,欢迎大家积极同我探讨交流学习

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值