目录
一.前缀树
前缀树:
1. 初始化树节点:开始时有一个根节点,通常为空节点或者包含一个空字符的节点。
2. 插入单词:对于要插入的每个单词,从根节点开始:
- 检查当前字符是否已经是节点的子节点。
- 如果是,则移动到该子节点继续;如果不是,则创建一个新节点并添加到当前节点的子节点中。
- 继续处理单词的下一个字符,直到单词的所有字符都插入到树中。
3.标记字符出现次数pass:在将单词所有字符插入树中的过程中,将路过的节点的pass++
4. 标记单词结尾end:在单词的最后一个字符的节点,将其end++,表示从根节点到当前节点形成了一个完整的单词的数量加1。
功能:
- 查询一共有多少个字符串。 => 看根节点的pass
- 查询某个字符串是否出现,或者有多少个。=> 直接看该字符串最后一个字符下边的节点的end
- 查询以某个前缀字符串作为前缀的字符串有多少 => 利用前缀字符串的最后一个字符下的节点的pass可以只知晓。如果到不了前缀字符串最后一个字符下的节点,那么说明不存在,返回0.
Nodej节点:
- path:在某条路径上,经过当前字符的次数
- end:在某条路径上,以当前字符结尾的单词数量
- nexts[]:路径a-z => nexts[0-25]
函数:
- insert(String word):向前缀树中插入单词word
- delete(String word):删除单词word
- search(String word):查询单词word
- prefixNumber(String word):查询以word为前缀的单词数量
public static class TrieNode {
public int path;
public int end;
public TrieNode[] nexts;
public TrieNode() {
path = 0;
end = 0;
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';
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-2直至得到最优解
public static class Program{
int start;
int end;
public Program(int a, int b) {
start=a;end=b;
}
}
public static class ProgramComparator implements Comparator<Program>{
public int compare(Program o1, Program o2) {
return o1.end-o2.end;
}
}
public static int bestArrange(Program[] program, int timePoint) {
if(program==null)return 0;
int res=0;
Arrays.sort(program, new ProgramComparator());
for(int i=0;i<program.length;i++) {
if(program[i].start>=timePoint) {
timePoint=program[i].end;
res++;
}
}
return res;
}
三.贪心算法在笔试时的解题套路
1.实现一个不依靠贪心策略的解法X,可以用最暴力的尝试
2.脑部处贪心策略A、贪心策略B、贪心策略C
3.用解法X和对数器,取验证每一个贪心策略,用实验的方式得知那个贪心策略正确
4.不要纠结贪心策略的证明
四.分金条
题目:
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度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 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 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;
}
六.一个数据流中,随时可以取得中位数(用堆实现)
步骤
- 看当前cur是否满足cur<=大根堆顶
- 若是,cur进入大根堆;若不是,cur进小根堆
- 如果|大根堆的size - 小根堆的size| == 2,那么就从size大的那个堆中取出堆顶元素,扔到size小的那个堆中。
public class Code06_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;
}
}
}
}