介绍前缀树

前缀树是N叉树的一种特殊形式。通常来说,一个前缀树是用来存储字符串的。前缀树的每一个节点代表一个字符串(前缀)。每一个节点会有多个子节点,通往不同子节点的路径上有着不同的字符。子节点代表的字符串是由节点本身的原始字符串,以及通往该子节点路径上所有的字符组成的。
image.png
image.pngimage.png
image.png
image.png

public class TrieTree {

    public static class TrieNode {
        public int path; // 加前缀树多少次,或者通过多少次。
        public int end; // 这个节点是不是字符串的结尾节点,如果是是多少个字符串的结尾节点。
        public TrieNode[] nexts;

        public TrieNode() {
            path = 0;
            end = 0;
            // nexts[0] == null 没有走向“a”的路
            // nexts[0] != null 有走向“a”的路
            // ...
            // nexts[25] != null 有走向“z”的路
            nexts = new TrieNode[26];
        }
    }

    public static class Trie {
        // root.pass等于几就代表加入了多少个字符串,也代表加入了多少个空串
        // 因为空串没有其它字符,所以root.pass++ root.end++
        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';
                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"));
    }
}

贪心算法

在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫作贪心算法。也就是说,不从整体最优上加以考虑,所做出的是在某种意义上的局部最优解。

局部最优 —?—> 整体最优

贪心算法在笔试时的解题套路

  1. 实现一个不依靠贪心策略的解法X,可以用最暴力的尝试
  2. 脑补出贪心策略A、贪心策略B、贪心策略C…
  3. 用解法X和对数器,去验证每一个贪心策略,用实验的方式得知哪个贪心策略正确
  4. 不要去纠结贪心策略的证明
  5. 堆和排序是最重要的两个策略

    金条问题

    image.png

    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;
     }
    

    项目利益问题

    image.png ```java public class 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 {

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

    }

    public static class MaxProfitComparator implements Comparator {

     @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;
    

    }

}

<a name="H0ZJe"></a>
## 一个数据流中,随时可以取得中位数

1. 用一个大根堆,一个小根堆,
1. 第一个数放到大根堆
1. 第二个数如果比大根堆的堆顶小则放进大根堆,
1. 因为此时大根堆的size 与 小根堆的size相差2,所以将大根堆的堆顶放到小根堆
1. 第三个数,如果比大根堆的堆顶小则放进大根堆,
1. 第四个数大于小根堆的堆顶,则放进小根堆,此时大根堆size为2,小根堆size为2
1. 第五个数大于小根堆的堆顶,放进小根堆
1. 大根堆放前二分之一个小n,小根堆放后二分之一个大n
```java
public class 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 (maxHeap.isEmpty() || num <= maxHeap.peek()) {
                maxHeap.add(num);
            } else {
                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();
            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^_^");
    }
}

八皇后问题

package class08;

public class Code09_NQueens {

    public static int num1(int n) {
        if (n < 1) {
            return 0;
        }
        int[] record = new int[n];
        return process1(0, record, n);
    }

    public static int process1(int i, int[] record, int n) {
        if (i == n) {
            return 1;
        }
        int res = 0;
        for (int j = 0; j < n; j++) {
            if (isValid(record, i, j)) {
                record[i] = j;
                res += process1(i + 1, record, n);
            }
        }
        return res;
    }

    public static boolean isValid(int[] record, int i, int j) {
        for (int k = 0; k < i; k++) {
            if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {
                return false;
            }
        }
        return true;
    }

    public static int num2(int n) {
        if (n < 1 || n > 32) {
            return 0;
        }
        int upperLim = n == 32 ? -1 : (1 << n) - 1;
        return process2(upperLim, 0, 0, 0);
    }
    // 在八皇后问题上进行位运算加速  优化
    // colLim 列的限制,1的位置不能放皇后,0的位置可以
    // leftDiaLim 左斜线的限制,1的位置不能放皇后,0的位置可以
    // rightDiaLim 右斜线的限制,1的位置不能放皇后,0的位置可以
    public static int process2(int upperLim, int colLim, int leftDiaLim,
            int rightDiaLim) {
        if (colLim == upperLim) {
            return 1;
        }
        int pos = 0;
        int mostRightOne = 0;
        pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
        int res = 0;
        while (pos != 0) {
            mostRightOne = pos & (~pos + 1);
            pos = pos - mostRightOne;
            res += process2(upperLim, colLim | mostRightOne,
                    (leftDiaLim | mostRightOne) << 1,
                    (rightDiaLim | mostRightOne) >>> 1);
        }
        return res;
    }

    public static void main(String[] args) {
        int n = 14;

        long start = System.currentTimeMillis();
        System.out.println(num2(n));
        long end = System.currentTimeMillis();
        System.out.println("cost time: " + (end - start) + "ms");

        start = System.currentTimeMillis();
        System.out.println(num1(n));
        end = System.currentTimeMillis();
        System.out.println("cost time: " + (end - start) + "ms");

    }
}