算法初级面试题07——前缀树应用、介绍和证明贪心策略、拼接字符串得到最低字典序、切金条问题、项目收益最大化问题、随时取中位数、宣讲会安排

第六课主要介绍图,不经常考,故今天先讲第七课的内容,介绍比较常考的树和贪心算法

介绍前缀树

何为前缀树? 如何生成前缀树?

可以查有多少个字符串以“be”为前缀。

如果要判断有没有“be”这个节点,每个节点上加上一个数据项,有多少个字符串以当前节点结尾的(可以查加了多少次特定字符串)。

给一个字符串、返回多少个字符串以这个为前缀。

再加一个数据项,记录该节点被划过多少次。

大概实现:

删除逻辑:

根据path是否变为0,来判断是否继续往下删。

可以解决以下问题:

一个字符串类型的数组arr1,另一个字符串类型的数组arr2。

arr2中有哪些字符,是arr1中出现的?请打印

arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印

arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀。

public class Code_01_TrieTree {

    public static class TrieNode {
        public int path;//多少个到达过节点
        public int end;//多少个以这个节点结尾
        public TrieNode[] nexts;//表示下一个节点的情况

        public TrieNode() {
            path = 0;
            end = 0;
            //26个字母
            nexts = new TrieNode[26];
        }
    }

    public static class Trie {
        private TrieNode root;

        public Trie() {
            root = new TrieNode();
        }

        public void insert(String word) {
            if (word == null) {
                return;
            }
            char[] chs = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - ‘a‘;//a index 0 -- b index 1 -- ...字母对应的
                if (node.nexts[index] == null) {//没有路就新建出来
                    node.nexts[index] = new TrieNode();
                }
                node = node.nexts[index];
                node.path++;
            }
            node.end++;
        }

        public void delete(String word) {
            if (search(word) != 0) {
                char[] chs = word.toCharArray();
                TrieNode node = root;
                int index = 0;
                for (int i = 0; i < chs.length; i++) {
                    index = chs[i] - ‘a‘;
                    if (--node.nexts[index].path == 0) {
                        node.nexts[index] = null;
                        return;
                    }
                    node = node.nexts[index];
                }
                node.end--;
            }
        }

        public int search(String word) {
            if (word == null) {
                return 0;
            }
            char[] chs = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - ‘a‘;
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.end;
        }

        public int prefixNumber(String pre) {
            if (pre == null) {
                return 0;
            }
            char[] chs = pre.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - ‘a‘;
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.path;
        }
    }

    public static void main(String[] args) {
        Trie trie = new Trie();
        System.out.println(trie.search("zuo"));
        trie.insert("zuo");
        System.out.println(trie.search("zuo"));
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.insert("zuo");
        trie.insert("zuo");
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.insert("zuoa");
        trie.insert("zuoac");
        trie.insert("zuoab");
        trie.insert("zuoad");
        trie.delete("zuoa");
        System.out.println(trie.search("zuoa"));
        System.out.println(trie.prefixNumber("zuo"));

    }

}

贪心策略

贪心策略正确性的证明,千万不要纠结,因为纠结你能死去。

想练贪心,记贪心的点就够了,写对数器的方式来验证。

不用去纠结哪个策略,证明上哪个是对的。这是一个大套路。

字典序介绍

例如abc和bce对比,想象成26进制的数进行对比。

长度不等的时候,把短的后面扩长来对比,后补最小的单位。

题目:给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最低的字典序。

什么叫做贪心?

定义一个指标,在这个指标下,把每一个样本分出一个优先来,按照优先大的先执行,优先小的后执行,这就是贪心。

贪心就是一件事情,贪心就是某一种简洁的标准,在这个标准下把所有东西分出个一二三来,然后根据这个优先级,决定一种顺序,就是贪心。

贪心案例:

贪心就是找距离目标最近的拿,不管最终的结果,只注重局部或者眼前的结果的方法

按照这种方法就是有可能丧失全局最优的

就好比两个人分5个大饼,一次可以拿一个或者两个,不吃完不允许再拿

A:用贪心方法:第一次拿两个,B拿一个,那么B最终拿3个

这个就是贪心方法失败的一个例子

有的时候贪心算法也是可以得到最优解的,比如还是大饼的例子,但是现在总数是7个了

A继续贪心,先两个,再两个就是4个了;

而B先1个,后2个,就只能是3个

介绍完贪心就回到题目:

如果对数组进行排序,再拼接一起,那是不行的。

正确的排序策略:

str1做前缀str2跟后面。和相反着对比。(每个字符串作为前缀贴在一起比较)

排序策略不成立的例子。

比较策略没有传递性,是一个环。

有传递性是:

这个题比较策略就是贪心策略。要确定的一件事情,排序策略是有传递性的。

把a.b看做成K进制的数,“abc” “efg” abcefg,其实就是abc向左移动了b这个数的长度位,再加入efg。

等式两边减b乘c,第二个式子减b乘a

然后再化简。

展开后ac抹掉,同时除与b,a移过去右,c移过去左...

证明排序策略是有传递性,是一个对的排序:

还要证明用这种排序策略得到的序列,就是字典序最小的。

先证明一件事情,序列里面任意调换两个字符串的顺序后,都会产生更大的字典序。

由于传递性:a.m1<=m1.a

一路下去,然后紧贴着b,b再和a交换。一路下来都是大于等于,那么肯定就只有比原始序列大。(所以原始序列是最经济的)

这是一个贪心策略从提出策略,到证明正确,要付出较大的心血。

所以用对数器来验证他对不对即可。

题目实现代码:

public class Code_05_LowestLexicography {

    public static class MyComparator implements Comparator<String> {
        @Override
        public int compare(String a, String b) {
            return (a + b).compareTo(b + a);
        }
    }

    public static String lowestString(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        //按照对比器排序
        Arrays.sort(strs, new MyComparator());
        String res = "";
        for (int i = 0; i < strs.length; i++) {
            res += strs[i];
        }
        return res;
    }

    public static void main(String[] args) {
        String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" };
        System.out.println(lowestString(strs1));

        String[] strs2 = { "ba", "b" };
        System.out.println(lowestString(strs2));

    }

}

题目:

一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度为20的 金条,不管切成长度多大的两半,都要花费20个铜板。一群人想整分整块金 条,怎么分最省铜板?

例如,给定数组{10,20,30},代表一共三个人,整块金条长度为 10+20+30=60. 金条要分成10,20,30三个部分。 如果, 先把长度60的金条分成10和50,花费60 再把长度50的金条分成20和30,花费50 一共花费110铜板。

但是如果, 先把长度60的金条分成30和30,花费60 再把长度30金条分成10和20,花费30 一共花费90铜板。输入一个数组,返回分割的最小代价。

标准的哈夫曼编码问题

子节点合并在一起的代价,是加起来的和。

最终要把所有的叶节点,生成一棵树。

把数组变为小根堆,然后从堆里面取出两个组成树,在把组成的父结点,放入堆中...(如此反复)最后代价就是产生的所有非叶节点。(贪心算法,每次从小根堆里面拿最小的)

顺序是从上面往下面切割。

当这种代价,是有子代价累加/乘,一种累什么的,也可能给一个公式,这都有可能用哈夫曼编码贪出来。

要具备起概念思想,用到其他的题目中。

public class Code_02_Less_Money {

    public static int lessMoney(int[] arr) {
        //优先队列是小根堆
        PriorityQueue<Integer> pQ = new PriorityQueue<>();
        for (int i = 0; i < arr.length; i++) {
            pQ.add(arr[i]);
        }
        int sum = 0;
        int cur = 0;
        while (pQ.size() > 1) {
            cur = pQ.poll() + pQ.poll();
            sum += cur;
            pQ.add(cur);
        }
        return sum;
    }

    public static class MinheapComparator implements Comparator<Integer> {
        //输入参数的顺序都为 5  3
        //如果返回 正数(证明后面的比较小) 第二个参数排在前面   5 - 3 = 2  3排在前面
        //如果返回 负数(证明后面的比较大) 第一个参数排在前面   3 - 5 = -2 5排在前面
        //0代表两个东西一样大
        //总结:负一、正二(解决的是哪个参数排在前面)
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2; // < 0  o1 < o2  负数
        }

    }

    public static class MaxheapComparator implements Comparator<Integer> {

        //等于是-(o1-o2) = o2 - o1
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1; // <   o2 < o1
        }

    }

    public static void main(String[] args) {
        // solution
        int[] arr = { 6, 7, 8, 9 };
        System.out.println(lessMoney(arr));

        int[] arrForHeap = { 3, 5, 2, 7, 0, 1, 6, 4 };

        // min heap
        PriorityQueue<Integer> minQ1 = new PriorityQueue<>();
        for (int i = 0; i < arrForHeap.length; i++) {
            minQ1.add(arrForHeap[i]);
        }
        while (!minQ1.isEmpty()) {
            System.out.print(minQ1.poll() + " ");
        }
        System.out.println();

        // min heap use Comparator
        PriorityQueue<Integer> minQ2 = new PriorityQueue<>(new MinheapComparator());
        for (int i = 0; i < arrForHeap.length; i++) {
            minQ2.add(arrForHeap[i]);
        }
        while (!minQ2.isEmpty()) {
            System.out.print(minQ2.poll() + " ");
        }
        System.out.println();

        // max heap use Comparator
        PriorityQueue<Integer> maxQ = new PriorityQueue<>(new MaxheapComparator());
        for (int i = 0; i < arrForHeap.length; i++) {
            maxQ.add(arrForHeap[i]);
        }
        while (!maxQ.isEmpty()) {
            System.out.print(maxQ.poll() + " ");
        }

    }

}

项目收益最大化问题:

输入: 参数1,正数数组costs 参数2,正数数组profits 参数3,正数k 参数4,正数m

costs[i]表示i号项目的花费 profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润) k表示你不能并行、只能串行的最多做k个项目 m表示你初始的资金

说明:你每做完一个项目,马上获得的收益,可以支持你去做下一个 项目。

输出: 你最后获得的最大钱数。

两个数组cost和profit,通过下标列出每个项目的花费和利润,做完项目后所得是cost+forfit,假设有资金W,一次只能做一个项目,最多做K个项目。输出获得最大的钱数。

贪心策略。

首先项目类型包括花费和利润,再根据花费放入小根堆中,然后看初始资金,在小根堆里面依次弹出头部,只要花费比W低的全部弹出。然后根据收益放入大根堆中,然后做大根堆的第一个项目。

当初始资金增加了,再看看小根堆中哪些项目可以做的,继续扔到大根堆中。一直做K个结束(或者大根堆里面没项目可做)。

public class Code_03_IPO {
    public static class Node {
        public int p;
        public int c;

        public Node(int p, int c) {
            this.p = p;
            this.c = c;
        }
    }

    public static class MinCostComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o1.c - o2.c;
        }

    }

    public static class MaxProfitComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o2.p - o1.p;
        }

    }

    public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
        Node[] nodes = new Node[Profits.length];
        for (int i = 0; i < Profits.length; i++) {
            nodes[i] = new Node(Profits[i], Capital[i]);
        }

        PriorityQueue<Node> minCostQ = new PriorityQueue<>(new MinCostComparator());
        PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
        for (int i = 0; i < nodes.length; i++) {
            minCostQ.add(nodes[i]);
        }
        for (int i = 0; i < k; i++) {
            //先把能做的项目存在大根堆中
            while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
                maxProfitQ.add(minCostQ.poll());
            }
            if (maxProfitQ.isEmpty()) {
                return W;
            }
            W += maxProfitQ.poll().p;
        }
        return W;
    }

}

随时取中位数

一个数据流中,随时可以取得中位数(之前讲过,初级2 020143)

不用堆的话,就用一个容器,吐出一个数就装一个,因为无序的所以装的时候也是无序的。当想要中位数的时候,要排个序,牺牲大。

准备两个堆,一个大根堆一个小根堆。

第一个数进入大根堆,下一个数小于等于就进大根堆。

如果从大根堆弹出一个数,就拿最后的一个数和第一个数交换,然后heapsize界限-1。

看题目怎么做:

先把数加入到大根堆中,如果发现两个堆大小差值超过了1,就把较大的弹出一个放到另外一个去。

如果要查中位数,大根堆堆顶和小根堆堆顶肯定能计算出来,因为

大根堆收集了较小的2分之N个,堆顶是其中的最大值。

小根堆收集的是较大的2分之N个,堆顶是其中的最小值。

正好卡在中间的位置。

只要当前数不是小于等于大根堆的,就扔到小根堆中。

策略是固定的:

1、如果当前数小于等于大根堆的堆顶,到大根堆,否则小根堆

2、不平衡了拿出扔到少的堆里面。

这样随时可以拿到中位数。

堆很好用(调整只和层数相关、o(logn)),几乎搞定所有贪心的题目。(面试也经常考)

优先级队列就是堆。

public class Code_04_MadianQuick {

    public static class MedianHolder {
        private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator());
        private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(new MinHeapComparator());

        private void modifyTwoHeapsSize() {
            if (this.maxHeap.size() == this.minHeap.size() + 2) {
                this.minHeap.add(this.maxHeap.poll());
            }
            if (this.minHeap.size() == this.maxHeap.size() + 2) {
                this.maxHeap.add(this.minHeap.poll());
            }
        }

        public void addNumber(int num) {
            if (this.maxHeap.isEmpty()) {
                this.maxHeap.add(num);
                return;
            }
            if (this.maxHeap.peek() >= num) {
                this.maxHeap.add(num);
            } else {
                if (this.minHeap.isEmpty()) {
                    this.minHeap.add(num);
                    return;
                }
                if (this.minHeap.peek() > num) {
                    this.maxHeap.add(num);
                } else {
                    this.minHeap.add(num);
                }
            }
            modifyTwoHeapsSize();
        }

        public Integer getMedian() {
            int maxHeapSize = this.maxHeap.size();
            int minHeapSize = this.minHeap.size();
            if (maxHeapSize + minHeapSize == 0) {
                return null;
            }
            Integer maxHeapHead = this.maxHeap.peek();
            Integer minHeapHead = this.minHeap.peek();
            //n & 1 判断奇偶数
            //n&1是n和1做“按位与”运算
            //1的二进制只有末位是1,所以n&1就是只保留n的末位(二进制).n&1就表示了n的奇偶性.
            if (((maxHeapSize + minHeapSize) & 1) == 0) {//两个堆的数量相同
                //如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
                return (maxHeapHead + minHeapHead) / 2;
            }
            //如果大/小根堆数量多中间数就在大/小根堆的堆顶。
            return maxHeapSize > minHeapSize ? maxHeapHead : minHeapHead;
        }

    }

    public static class MaxHeapComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            if (o2 > o1) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    public static class MinHeapComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            if (o2 < o1) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    // for test
    public static int[] getRandomArray(int maxLen, int maxValue) {
        int[] res = new int[(int) (Math.random() * maxLen) + 1];
        for (int i = 0; i != res.length; i++) {
            res[i] = (int) (Math.random() * maxValue);
        }
        return res;
    }

    // for test, this method is ineffective but absolutely right
    public static int getMedianOfArray(int[] arr) {
        int[] newArr = Arrays.copyOf(arr, arr.length);
        Arrays.sort(newArr);
        int mid = (newArr.length - 1) / 2;
        if ((newArr.length & 1) == 0) {
            return (newArr[mid] + newArr[mid + 1]) / 2;
        } else {
            return newArr[mid];
        }
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i != arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        boolean err = false;
        int testTimes = 200000;
        for (int i = 0; i != testTimes; i++) {
            int len = 30;
            int maxValue = 1000;
            int[] arr = getRandomArray(len, maxValue);
            MedianHolder medianHold = new MedianHolder();
            for (int j = 0; j != arr.length; j++) {
                medianHold.addNumber(arr[j]);
            }
            if (medianHold.getMedian() != getMedianOfArray(arr)) {
                err = true;
                printArray(arr);
                break;
            }
        }
        System.out.println(err ? "Oops..what a fuck!" : "today is a beautiful day^_^");

    }

}

宣讲会安排

一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。返回这个最多的宣讲场次。

安排最早开始的先进行,是不行的,如果那项目持续一天,其他项目都讲不来了了。

按照持续时间短来安排,也得不到最优解。如果一个小项目卡在两个大项目中间,阻碍了大项目的安排,也不是最优解。

根据哪个项目早结束安排。

public class Code_06_BestArrange {

    public static class Program {
        public int start;
        public int end;

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

    public static class ProgramComparator implements Comparator<Program> {

        @Override
        public int compare(Program o1, Program o2) {
            return o1.end - o2.end;
        }

    }

    public static int bestArrange(Program[] programs, int start) {
        Arrays.sort(programs, new ProgramComparator());
        int result = 0;
        for (int i = 0; i < programs.length; i++) {
            if (start <= programs[i].start) {
                result++;
                start = programs[i].end;
            }
        }
        return result;
    }

    public static void main(String[] args) {

    }

}

贪心策略就是经验式的东西(依靠累计)。不用纠结于证明。

原文地址:https://www.cnblogs.com/xieyupeng/p/10349840.html

时间: 2024-10-09 20:53:03

算法初级面试题07——前缀树应用、介绍和证明贪心策略、拼接字符串得到最低字典序、切金条问题、项目收益最大化问题、随时取中位数、宣讲会安排的相关文章

【算法学习笔记】57. 前缀树 字典序优化技巧 STL学习 SJTU OJ 1366 前缀匹配

Description 给出一个总字符个数大小不超过1,000,000的字典(这个字典的单词顺序不为字典序)和不超过1000个长度不超过1000的前缀,输出字典中匹配该前缀,字典序为K_i的单词在字典中的位置. 所有单词都为小写字母. Input Format 第一行: 两个整数N,M,分别表示字典中的单词个数和需要查询的前缀数. 接下来N行:每行一个字符串,表示字典中的单词. 接下来M行,每行一个K_i, P_i, P_i表示查询的前缀,K_i表示满足这个前缀在字典序中的位置. Output

算法初级面试题03——队列实现栈、栈实现队列、转圈打印矩阵、旋转矩阵、反转链表、之字打印矩阵、排序矩阵中找数

第一部分主要讨论:栈.队列.数组矩阵相关的面试题 题目一 用数组结构实现大小固定的队列和栈 public static class ArrayStack { private Integer[] arr; private Integer size; public ArrayStack(int initSize) { if (initSize < 0) { throw new IllegalArgumentException("The init size is less than 0"

算法初级面试题05——哈希函数/表、生成多个哈希函数、哈希扩容、利用哈希分流找出大文件的重复内容、设计RandomPool结构、布隆过滤器、一致性哈希、并查集、岛问题

今天主要讨论:哈希函数.哈希表.布隆过滤器.一致性哈希.并查集的介绍和应用. 题目一 认识哈希函数和哈希表 1.输入无限大 2.输出有限的S集合 3.输入什么就输出什么 4.会发生哈希碰撞 5.会均匀分布,哈希函数的离散性,打乱输入规律 public class Code_01_HashMap { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); map.p

算法初级面试题08——递归和动态规划的精髓、阶乘、汉诺塔、子序列和全排列、母牛问题、逆序栈、最小的路径和、数组累加成指定整数、背包问题

第八课主要介绍递归和动态规划 介绍递归和动态规划 暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 图灵引入的是:我不知道怎么算,但是我知道怎么试.知道怎么暴力破解出来. 要学会,练习懂得怎么尝试.

数据结构与算法——前缀树和贪心算法(1)

介绍前缀树 何为前缀树?如何生成前缀树? 例子:一个字符串类型的数组arrl,另一个字符串类型的数组arr2.arr2中有哪些字符,是arr 1中 出现的?请打印.arr2中有哪些字符,是作为arr 1中某个字符串前缀出现的?请打印.arr2 中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀. public class TrieTree { public static class TrieNode { public int path; public int

【学习总结】数据结构-Trie/前缀树/字典树-及其最常见的操作

Trie/前缀树/字典树 Trie (发音为 "try") 或前缀树是一种树数据结构,用于检索字符串数据集中的键. 一种树形结构,是一种哈希树的变种. 典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高. 应用: 自动补全 END 原文地址:https://www.cnblogs.com/anliux/p/12590368.html

常用算法之Trie【字典树,前缀树】

Trie中文名又叫做字典树,前缀树等,因为其结构独有的特点,经常被用来统计,排序,和保存大量的字符串,经常见于搜索提示,输入法文字关联等,当输入一个值,可以自动搜索出可能的选择.当没有完全匹配的结果时,可以返回前缀最为相似的可能. 其实腾讯的面试题有一个:如何匹配出拼写单词的正确拼写.其实用匹配树非常合适. 基本性质: 1.根节点不含有字符,其余各节点有且只有一个字符. 2.根节点到某一节点中经过的节点存储的值连接起来就是对应的字符串. 3.每一个节点所有的子节点的值都不应该相同. 借用一下维基

第15个算法-实现 Trie (前缀树)(LeetCode)

解法代码来源 :https://blog.csdn.net/whdAlive/article/details/81084793 算法来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作. 示例: Trie trie = new Trie(); trie.insert("apple"

白话算法与数据结构之【字典树】

1. 什么是trie树 1.Trie树 (特例结构树) Trie树,又称单词查找树.字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高.      Trie的核心思想是空间换时间.利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的. Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子