详解前缀树和贪心算法
前缀树
前缀树定义
前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。
public class TrieNode {
// 构建树的时候这个节点通过多少次
public int pass;
// 有多少字符串以它为结束点
public int end;
// 可以换成 HashMap
public TrieNode[] nexts;
public TrieNode() {
pass = 0;
end = 0;
nexts = new TrieNode[26];
}
}
生成前缀树
public class Trie {
public TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word) {
if (word == null) {
return;
}
char[] chs = word.toCharArray();
TrieNode node = root;
node.pass++;
int index = 0;
for (char ch : chs) {
index = ch - 'a';
if (node.nexts[index] == null) {
node.nexts[index] = new TrieNode();
}
node = node.nexts[index];
node.pass++;
}
node.end++;
}
public void delete(String word) {
if (word == null || search(word) <= 0) {
return;
}
TrieNode node = root;
int index = 0;
for (char ch : word.toCharArray()) {
node.pass--;
index = ch - 'a';
if (node.nexts[index].pass == 0) {
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
public int search(String word) {
if (word == null) {
return true;
}
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (node.nexts[ch - 'a'] == null) {
return 0;
}
node = node.nexts[ch - 'a'];
}
return node.end;
}
public int prefixNumber(String pre) {
if (pre == null) {
return 0;
}
TrieNode node = root;
for (char ch : pre.toCharArray()) {
if (node.nexts[ch - 'a'] == null) {
return 0;
}
node = node.nexts[ch - 'a'];
}
return node.pass;
}
}
贪心算法
贪心算法定义
在某一个标准下,优先考虑最满足标准的样本,最后考虑不满足标准的样本,最终得到一个答案的算法,叫做贪心算法。
从局部最优 => 全局最优
题目一
给会议室安排日程。
贪心策略:
- 按照开始时间安排,先安排开始时间早的(反例:有一个从开始到最后的会议)
- 按照持续时间安排,先安排持续时间短的(反例:有一个会议横跨两个会议之间并且特别短)
- 按照结束时间安排,先安排结束时间早的
题目二
贪心算法解题思路
- 实现一个不依靠贪心策略的解法X,可以用最暴力的尝试
- 猜测贪心策略,并验证
N皇后问题
public int totalNQueens(int n) {
int[] record = new int[n];
return help(0, record, n);
}
public int help(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 += help(i + 1, record, n);
}
}
return res;
}
public boolean isValid(int[] record, int i, int j) {
for(int t = 0; t < i; t++) {
if(record[t] == j) {
return false;
}
if(t - i == record[t] - j) {
return false;
}
if(t - i == j - record[t]) {
return false;
}
}
return true;
}
N皇后问题优化
N比较大的时候有10倍的差距,较小的时候没有差距
public int totalNQueens2(int n) {
int limit = n == 32 ? -1 : (1 << n) - 1;
return help(limit, 0, 0, 0);
}
public int help(int limit, int colLimit, int leftLimit, int rightLimit) {
if(colLimit == limit) {
return 1;
}
int pos = limit & ~(colLimit | leftLimit | rightLimit);
int res = 0;
int mostRightOne = 0;
while (pos != 0) {
mostRightOne = pos & (~pos + 1);
pos = pos - mostRightOne;
res += help(limit, colLimit | mostRightOne
, (leftLimit | mostRightOne) << 1,
(rightLimit | mostRightOne) >> 1);
}
return res;
}