目录
207-课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
解析::link
class Solution {
List<List<Integer>> edgs;
int[] visited;
boolean valid = true;
public boolean canFinish(int numCourses, int[][] prerequisites) {
edgs = new ArrayList<>();
for(int i=0;i<numCourses;i++){
edgs.add(new ArrayList<>());
}
visited = new int[numCourses];
//向list中添加每一对关系组,必修在前
for(int[] info:prerequisites){
edgs.get(info[1]).add(info[0]);
}
//遍历节点
for(int i=0;i<numCourses&&valid;i++){
if(visited[i]==0){
dfs(i);
}
}
return valid;
}
void dfs(int u){
//搜索中
visited[u]=1;
for(int v : edgs.get(u)){
//如果v还没有搜索
if(visited[v]=0){
//在搜索v的下一个节点(修完v课程,才可修的课程)
dfs(v);
if(!valid) {
return;
}
}else if(visited[v] == 1){
valid = false;
return ;
}
}
//搜索完成
visited[u] = 2;
}
}
208-实现前缀树
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True
Trie,又称前缀树或字典树,是一棵有根树,其每个节点包含以下字段:
指向子节点的指针数组 children。对于本题而言,数组长度为 26,即小写英文字母的数量。此时 children[0] 对应小写字母 a,children[1] 对应小写字母 bb,…,children[25] 对应小写字母 z。
布尔字段isEnd,表示该节点是否为字符串的结尾。(当前的cur.next[c-'a']是指向的下一个字符的位置,cur.next有26个位置,可以指向不同的字符)
class Trie {
/**
就是实现一颗非典型的多叉树模型
*/
/** Initialize your data structure here. */
private boolean isEnd;
private Trie[] next;
public Trie() {
isEnd = false;
next = new Trie[26];
}
/** Inserts a word into the trie. */
public void insert(String word) {
//从第一个节点开始,可以把本节点当作空节点
Trie cur = this;
char[] ch = word.toCharArray();
for(char c : ch){
//c-'a':找出在数组中的位置
// 如果当前节点的下一个字符处还没有开辟,那就新创建一个
if(cur.next[c-'a']==null){
cur.next[c-'a'] = new Trie();
}
cur = cur.next[c-'a'];
}
cur.isEnd = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
Trie cur = prefixSearch(word);
return cur !=null &&cur.isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
//前缀匹配
return prefixSearch(prefix)!=null;
}
//查找代码的公共部分,提高复用
private Trie prefixSearch(String word){
Trie cur = this;
char[] ch = word.toCharArray();
for(char c: ch){
如果当前节点的对应字符处尚未开辟,说明没有对应的word插入
if(cur.next[c-'a']==null) return null;
cur = cur.next[c-'a'];
}
return cur;
}
}
/**
* 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);
*/
215-数组中第k个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
class Solution {
/**
我们也可以使用堆排序来解决这个问题——建立一个大根堆,做 k - 1次删除操作后堆顶元素就是我们要找的答案。
在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让更面试者自己实现一个堆。
所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。
*/
public int findKthLargest(int[] nums, int k) {
int heapSize = nums.length;
buildMaxHeap(nums,heapSize);
//依次删除
for(int i= nums.length-1;i>=nums.length-k+1;i--){
//删除头部元素时,先把头和最后一个元素交换,然后删除最后一个
swap(nums,0,i);
--heapSize;
//删除之后调整
maxHeapify(nums,0,heapSize);
}
return nums[0];
}
//建堆
public void buildMaxHeap(int[] a, int heapSize){
//建堆时,从最后一个非叶子节点开始调整,即heapSize/2-1(注意数组是从0开始的)
//堆是满二叉树。因此可以用数组来表示,节点i的父亲节点是i/2+1,左节点是2i+1,右节点是2i+2
for(int i=heapSize/2-1;i>=0;i--){
maxHeapify(a,i,heapSize);
}
}
public void maxHeapify(int[] a, int i, int heapSize){
int left = i*2+1,right = i*2+2,largest = i;
if(left<heapSize&&a[left]>a[largest]){
largest = left;
}
if(right<heapSize&&a[right]>a[largest]){
largest = right;
}
if(largest !=i){
swap(a,i,largest);
//递归,再判断上面调整后,是否影响到了左子树或者右子树
maxHeapify(a,largest,heapSize);
}
}
//交换
public void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
221-最大正方形
class Solution {
public int maximalSquare(char[][] matrix) {
int maxSide = 0;
if(matrix==null||matrix.length==0||matrix[0].length==0) return maxSide;
int rows = matrix.length, cols = matrix[0].length;
int[][] dp = new int[rows][cols];
for(int i=0;i<rows;i++){
for(int j=0; j<cols;j++){
//判断边界条件。。i和j有一个为0,如果matrix[i][j]='1',dp[i][j]就等于1
if(matrix[i][j]=='1'){
if(i==0||j==0) {
dp[i][j]=1;
}else{
//不等于1的话,就等于以其左上角、左边,上面的点为右下角的正方形的最小值+1
dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]) + 1;
}
//记录最大值
maxSide = Math.max(maxSide,dp[i][j]);
}
}
}
int maxSquare = maxSide*maxSide;
return maxSquare;
}
}
226-翻转二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return null;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
//交换左右
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
//先进左子树还是右子树都不影响,
//因为交换时候是当前节点的左孩子右孩子交换,不跨树
if(node.right!=null) queue.offer(node.right);
if(node.left!=null) queue.offer(node.left);
}
return root;
}
}
//递归方式
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}