一、线段树(区间树)
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.简单介绍
- 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。
- 典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
- 它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
推荐一篇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类问题,欢迎大家积极同我探讨交流学习