拓扑排序 -- 力扣:207课程表 210课程表ii
运用拓扑排序的标志:具有依赖关系
力扣的 207 和 210 应该是经典例题了吧kk
207 课程表
反正dfs我是看不懂,bfs的方法就是运用了拓扑排序:
1、先通过二维集合表示出 “图”
2、每一个课程都具有:先修课程的数量;后修课程的数量
3、创建队列 -- 该队列存储的就是拓扑排序的结果
4、只有先修课程数量为0的课程可以入队(能够被修读);入队后,其后修课程的先修课程-1;
在此期间用变量allCoures表示可以修读的课程数量
5、重复上述步骤 最终判断是否所有课程都可修读
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 拓扑排序
// 初始化数组 -- 表示先修课程的数量
int[] advance = new int[numCourses];
// 定义集合 -- 表示“图”
List<List<Integer>> edges = new ArrayList<List<Integer>>();
// 根据课程数量初始化集合
for(int i = 0;i<numCourses;i++){
edges.add(new ArrayList<Integer>());
}
// 根据具体的先修课程组初始化集合具体内容
for(int[] arrs : prerequisites){
edges.get(arrs[1]).add(arrs[0]);
// 根据关系更先修课程的数量
++advance[arrs[0]];
}
// 定义队列
Queue<Integer> queue = new LinkedList<Integer>();
// 先将先修课程为0的课程入队
for(int i = 0;i<numCourses;i++){
if(advance[i]==0) queue.offer(i);
}
// 初始化一个课程数量标记变量
int allCoures = 0;
// 深搜
while(!queue.isEmpty()){
++allCoures;
int u = queue.poll();
for(int v : edges.get(u)){
--advance[v];
if(advance[v]==0) queue.offer(v);
}
}
return allCoures==numCourses;
}
}
210 课程表ii
作为207的升级版,其实也就是返回值的区别 -- 返回拓扑排序的结果
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
// 依然是拓扑排序
// 返回学习顺序 -- 在bfs的过程中更新数组
// 初始化先修课程数量数组
int[] indeg = new int[numCourses];
// 初始化集合 -- 表示“图”
List<List<Integer>> edges = new ArrayList<List<Integer>>();
for(int i = 0;i<numCourses;i++){
edges.add(new ArrayList<Integer>());
}
for(int[] arrs : prerequisites){
edges.get(arrs[1]).add(arrs[0]);
++indeg[arrs[0]];
}
// 初始化队列
Queue<Integer> queue = new LinkedList<Integer>();
for(int i = 0;i<numCourses;i++){
if(indeg[i]==0) queue.offer(i);
}
// 初始化传出的数组
int[] study = new int[numCourses];
int j = 0;
// bfs
int visited = 0;
while(!queue.isEmpty()){
++visited;
int u = queue.poll();
study[j++] = u;
for(int v : edges.get(u)){
--indeg[v];
if(indeg[v]==0) queue.offer(v);
}
}
if(visited!=numCourses) return new int[0];
return study;
}
}
前缀树
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
208 实现 Trie (前缀树)
总之就是从基础开始吧 思路还是很清晰的(可能是因为刚刚学hehe)
class Trie {
// 定义树节点类
static class TrieNode{
boolean isWord;
TrieNode[] children;
// 构造函数
public TrieNode(){
// 标记变量 -- 是否为一个单词
isWord = false;
// 子数组 -- 单词组成部分
// 根据题目要求 -- 至多有26个孩子
children = new TrieNode[26];
}
}
// 初始化node父节点
TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word) {
TrieNode curr = root;
// 插入单词
// 遍历单词 -- 每一个字母都是树的节点
for(int i = 0;i<word.length();i++){
char letter = word.charAt(i);
// 找到该字母所对应的索引
int index = letter - 'a';
// 将这个索引延伸
if(curr.children[index]==null){
curr.children[index] = new TrieNode();
}
curr = curr.children[index];
}
// 录入完一个单词后 -- 更新isWord
curr.isWord = true;
}
public boolean search(String word) {
TrieNode curr = root;
// 插入单词
// 遍历单词 -- 每一个字母都是树的节点
for(int i = 0;i<word.length();i++){
char letter = word.charAt(i);
// 找到该字母所对应的索引
int index = letter - 'a';
// 将这个索引延伸
if(curr.children[index]==null){
return false;
}
curr = curr.children[index];
}
// 录入完一个单词后 -- 更新isWord
return curr.isWord;
}
public boolean startsWith(String prefix) {
TrieNode curr = root;
// 插入单词
// 遍历单词 -- 每一个字母都是树的节点
for(int i = 0;i<prefix.length();i++){
char letter = prefix.charAt(i);
// 找到该字母所对应的索引
int index = letter - 'a';
// 将这个索引延伸
if(curr.children[index]==null){
return false;
}
curr = curr.children[index];
}
// 录入完一个单词后 -- 更新isWord
return true;
}
}
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/
211 添加与搜索单词 - 数据结构设计
实践中有变形,此题的重点是万能符号 . 的介入 -- 遇到.要做特殊判断
class WordDictionary {
private Trie root;
public WordDictionary() {
// 初始化前缀树
root = new Trie();
}
public void addWord(String word) {
// 添加单词
root.insert(word);
}
public boolean search(String word) {
return dfs(word,root,0);
}
// dfs
public boolean dfs(String word,Trie node,int index){
// 走到队尾 -- 只要判断是否为整个单词就好
if(index==word.length()){
return node.getIsWord();
}
// 其他情况 -- 字母 和 .
char letter = word.charAt(index);
// 1.字母
if(Character.isLetter(letter)){
// 继续搜索
int childindex = letter - 'a';
Trie child = node.getChildren()[childindex];
if(child!=null&&dfs(word,child,index+1)) return true;
}
else{
for(int i = 0;i<26;i++){
Trie child = node.getChildren()[i];
if(child!=null&&dfs(word,child,index+1)) return true;
}
}
return false;
}
}
// 初始化一个前缀树
class Trie{
private boolean isWord;
private Trie[] children;
// 构造函数
public Trie(){
isWord = false;
children = new Trie[26];
}
// 插入方法
public void insert(String word){
Trie node = this;
for(int i = 0;i<word.length();i++){
char letter = word.charAt(i);
int index = letter-'a';
if(node.children[index]==null){
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isWord = true;
}
// 获取子数组
public Trie[] getChildren(){
return this.children;
}
// 获取单词状态
public boolean getIsWord(){
return this.isWord;
}
}
/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary obj = new WordDictionary();
* obj.addWord(word);
* boolean param_2 = obj.search(word);
*/