一、题目
1、课程表(LC 207)
2、实现 Trie 前缀树(LC 208)
二、题解
1、课程表(LC 207)

(1)分析
根据给定的课程总数与课程先修关系,判断是否存在无法完成所有课程学习的情况,本质就是给一个有向图,判断有向图中是否有环。将课程与先修关系转化为有向图结构,每一门课程对应图中的一个节点,课程之间的先修约束对应有向边,例如修课程 2 必须先完成课程 1 的学习,就构建一条从节点 1 指向节点 2 的有向边。
采用拓扑排序的方法解决。创建入度数组,每一门课程所需的先修课程数量,数组下标对应课程编号,数组值即为该课程的入度;邻接表记录课程的后续课程;队列存储当前入度为 0 的课程,这类课程没有任何先修约束,是可以直接开始学习的课程。
在循环处理过程中,每次从队列头部取出一个课程节点,代表该课程已成功修读,同时将总课程数减一,随后遍历该课程的所有后续课程,将这些后续课程的入度依次减一,代表完成了一个先修条件,若某门后续课程的入度减至 0,说明其所有先修课程均已完成,将其加入队列继续处理。当队列为空时,循环执行结束,此时判断剩余的总课程数是否为 0 ,若剩余课程数为 0,说明图中无环,可以修完所有课程,若剩余课程数大于 0,说明存在部分课程始终无法进入队列,即图中存在环,无法完成学习。
(2)解答
java
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] inDegree = new int[numCourses];
//记录入度的数组,如inDgree[0] = 2,表示修课程0需要先修2个其他课程
List<List<Integer>> list = new ArrayList<>();
//邻接表,存储每门课程的后续课程,list.get(0),得到的就是需要先修课程0,才能修的课程
Queue<Integer> queue = new LinkedList<>();
//队列存储入度为0的课程,即现在可以直接修的课程
for(int i = 0; i < numCourses; i++){
list.add(new ArrayList<>());
}
for(int[] arr : prerequisites){
inDegree[arr[0]]++;
list.get(arr[1]).add(arr[0]);
}
//初始化邻接表,初始化入度数组
for(int i = 0; i < numCourses; i++){
if(inDegree[i] == 0){
queue.offer(i);
}
}
//初始化入度为0的队列
//BFS
while(!queue.isEmpty()){
Integer i = queue.poll(); //取出一个队首元素,代表修了一门课程
numCourses--;
for(int j : list.get(i)){
inDegree[j]--; //更新入度数组
if(inDegree[j] == 0){ //入度数组中,某个课程对应下标的入度为0,加入队列
queue.offer(j);
}
}
}
return numCourses == 0; //循环结束后课程数为零,代表可以修完
}
}
2、实现 Trie 前缀树(LC 208)

(1)分析
创建二十六叉数,类似于二叉树,有节点数组 son 存放26个子节点,属性 end 代表是否为字符末尾。inesrt函数,从根节点出发,将字符串逐字符转换为数组遍历,对于每一个字符,计算其在子节点数组中的下标,若当前节点的对应下标位置为空,则创建新的节点构建路径,若已存在节点则直接复用,遍历完成所有字符后,将最后一个节点的 end 属性设为 true,标识该节点是一个完整字符串的结尾。
查找完整字符串 search 和查找前缀 startwith 功能共用一个 find 函数,遍历目标字符串对应的所有节点路径,若遍历过程中遇到空节点,说明前缀树中不存在该路径,返回标识 0,若完整遍历完所有字符且路径存在,则根据最后一个节点的 end 属性判断,若 end 为 false 说明仅为前缀,返回标识 1,若 end 为 true 说明是完整字符串,返回标识 2。
(2)解答
java
class Trie {
private static class Node{
Node[] son = new Node[26];
boolean end = false;
}
Node root = new Node();
public Trie() {
}
public void insert(String word) {
Node curr = root;
for(char ch : word.toCharArray()){
int index = ch - 'a';
if(curr.son[index] == null){
curr.son[index] = new Node();
}
curr = curr.son[index];
}
curr.end = true;
}
public boolean search(String word) {
int i = find(word);
if(i == 2){
return true;
}
return false;
}
public boolean startsWith(String prefix) {
int i = find(prefix);
if(i != 0){
return true;
}
return false;
}
public int find(String str){
Node curr = root;
for(char ch : str.toCharArray()){
int index = ch - 'a';
if(curr.son[index] == null){
return 0;
}
curr = curr.son[index];
}
return curr.end == false ? 1 : 2;
}
}
/**
* 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);
*/