前缀树
基本思路
前缀树用于存储字符串等数据的结构,其插入和删除流程大体如↓:



具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| package p7.preTreeAndGreedy;
public class PreTree { public class preNode{ int pass; int end; preNode[] nexts;
public preNode(){ pass = 0; end = 0; nexts = new preNode[26]; } }
preNode root = new preNode();
public void insert(String word){ if(word == null){ return; } char[] arr = word.toCharArray(); preNode node = root; node.pass++; for (int i = 0; i < arr.length; i++) {
int index = arr[i] - 'a'; if(node.nexts[index] == null){ node.nexts[index] = new preNode(); } node = node.nexts[index]; node.pass++; } node.end++; }
public int search(String word){ if(word == null){ return 0; } preNode node = root; char[] arr = word.toCharArray(); for (int i = 0; i < arr.length; i++) { int index = arr[i] - 'a'; if(node.nexts[index] == null){ return 0; } node = node.nexts[index]; } return node.end; }
public int pathNumber(String word){ if(word == null){ return 0; } preNode node = root; char[] arr = word.toCharArray(); for (int i = 0; i < arr.length; i++) { int index = arr[i] - 'a'; if(node.nexts[index] == null){ return 0; } node = node.nexts[index]; } return node.pass; }
public void delete(String word){ if(word == null || search(word) == 0){ return; } char[] arr = word.toCharArray(); preNode node = root; for (int i = 0; i < arr.length; i++) { int index = arr[i] - 'a'; if(--node.nexts[index].pass == 0){ node.nexts[index] = null; return; } } --node.end; } }
|
贪心算法

经典贪心算法题解
会议室日程安排
题目:一个项目占用一个会议室,会议室同时只能被一个项目占用。给你每个项目的开始和结束时间,请给出最多能够完成多少场项目的宣讲,返回该场次数。
求解思路:
该问题的求解中贪心策略体现在,将尽可能早结束的会议安排进行程,就能得到最优解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package p7.preTreeAndGreedy;
import java.util.Arrays; import java.util.Comparator;
public class Code02_Greedy_Room { class roomTime{ int begin; int end; public roomTime(int begin, int end){ this.begin = begin; this.end = end; } }
public class myComparator implements Comparator<roomTime>{
@Override public int compare(roomTime o1, roomTime o2) { return o1.end - o2.end; } }
public int maxNum(roomTime[] roomTimesSet, int timePot){ if(roomTimesSet == null){ return 0; } Arrays.sort(roomTimesSet, new myComparator()); if(timePot < roomTimesSet[0].begin){ return 0; } int res = 0; for (int i = 0; i < roomTimesSet.length; i++) { if(timePot <= roomTimesSet[i].end){ res++; timePot = roomTimesSet[i].end; } } return res; } }
|
分金条

算法思路:哈夫曼树,依托小根堆数据结构,每次取出小根堆顶端的两个数,求其和,将和累加进最小代价,并将和放入小根堆,重复此操作,直到小根堆不足两个数时,返回最小代价。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package p7.preTreeAndGreedy;
import java.util.PriorityQueue;
public class Code03_Greedy_splitGold { public int splitGold(int[] arr){ if(arr == null){ return 0; } PriorityQueue<Integer> queue = new PriorityQueue<>(); for (int i = 0; i < arr.length; i++) { queue.add(arr[i]); } int res = 0; while(queue.size() > 1){ int temp = queue.poll() + queue.poll(); res += temp; queue.add(temp); } return res; } }
|
求最大资金数

该题求解思路为,每次选择能够执行项目中,利润最大的项目。这样本金能够得到最大程度的上升。
借助两个堆来实现该方案,首先借助一个小根堆,小根堆中依据项目所需本金升序排列项目,每次根据所拥有的本金从小根堆中出队列。出队列的项目入另一个大顶堆,大顶堆依据项目的利润降序排列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package p7.preTreeAndGreedy;
import java.util.Comparator; import java.util.PriorityQueue;
public class Code03_Greedy_MaxMoney { class project{ int cost; int profit;
public project(int cost, int profit){ this.cost = cost; this.profit = profit; } }
public class costComparator implements Comparator<project>{
@Override public int compare(project o1, project o2) { return o1.cost - o2.cost; } }
public class profitComparator implements Comparator<project>{
@Override public int compare(project o1, project o2) { return o2.profit - o1.profit; } }
public int maxMoney(project[] projects, int num, int money){ if(projects == null){ return 0; } PriorityQueue<project> minCostQueue = new PriorityQueue<>(new costComparator()); for (project p : projects) { minCostQueue.add(p); } PriorityQueue<project> maxProfitQueue = new PriorityQueue<>(new profitComparator()); int res = money; for (int i = 0; i < num; i++) { while(!minCostQueue.isEmpty() && minCostQueue.peek().cost <= monry){ maxProfitQueue.add(minCostQueue.poll()); } if(maxProfitQueue.isEmpty()){ return res; } res += maxProfitQueue.poll().profit; } return res; } }
|
拓展:如何通过1个大根堆+1个小根堆实现,实时返回一个数组的中位数?
规则如下:
- 第一个数直接入大根堆
- 后续进入的数,如果大于大根堆堆顶的数,入大根堆,否则入小根堆
- 如果大小堆size大小相差2,将size较大的堆堆顶元素出队,并入队另一个堆
这样,当大小堆size相等时,去两个堆堆顶元素的平均数,不想等时,取size较大那一个堆的堆顶元素,就能取得当前数组中位数。
总结
本节讲解了如何构建一颗前缀树,并实现对树的编辑。学习了贪心算法,需要注意的是贪心算法并没有固定的解题模板,其思路依据场景的变化而变化,需要多尝试,利用对比器进行检验,验证其有效性。
