需要开通vip的题目暂时跳过
点击链接可跳转到所有刷题笔记的导航链接
包含整数的二维矩阵 M 表示一个图片的灰度。你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个,则尽可能多的利用它们。
解答
public int[][] imageSmoother(int[][] M) {
int R = M.length, C = M[0].length;
int[][] ans = new int[R][C];
for (int r = 0; r < R; ++r)
for (int c = 0; c < C; ++c) {
int count = 0;
for (int nr = r-1; nr <= r+1; ++nr)
for (int nc = c-1; nc <= c+1; ++nc) {
if (0 <= nr && nr < R && 0 <= nc && nc < C) {
ans[r][c] += M[nr][nc];
count++;
}
}
ans[r][c] /= count;
}
return ans;
}
分析
给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。
解答
public int widthOfBinaryTree(TreeNode root) {
if (root == null) return 0;
List<TreeNode> list = new ArrayList<>();
List<Long> index = new ArrayList<>();
index.add((long)1);
list.add(root);
long res = 1;
while (!list.isEmpty()) {
List<TreeNode> temp = new ArrayList<>();
List<Long> indexTemp = new ArrayList<>();
long start = Long.MAX_VALUE;
long end = -Long.MAX_VALUE;
for (int i = 0; i < list.size(); i++) {
TreeNode node = list.get(i);
long ind = index.get(i);
start = Math.min(start, ind);
end = Math.max(end, ind);
if (node.left != null) {
temp.add(node.left);
indexTemp.add(2 * ind);
}
if (node.right != null) {
temp.add(node.right);
indexTemp.add(2 * ind + 1);
}
}
res = Math.max(res, end - start + 1);
list = temp;
index = indexTemp;
}
return (int)res;
}
分析
有台奇怪的打印机有以下两个特殊要求:
给定一个只包含小写英文字母的字符串,你的任务是计算这个打印机打印它需要的最少次数。
解答
public int strangePrinter(String s) {
int n = s.length();
if(n == 0) return 0;
int[][] dp = new int[n + 1][n + 1];
for(int i = 0; i < n; i++){
dp[i][i] = 1;
}
for(int len = 2; len <= n; len++){
for(int i = 0; i + len - 1 < n; i++){
int j = i + len - 1;
dp[i][j] = dp[i+1][j] + 1;
for(int k = i + 1; k <= j; k++){
if(s.charAt(i) == s.charAt(k))
dp[i][j] = Math.min(dp[i][j], dp[i][k - 1] + dp[k + 1][j]);
}
}
}
return dp[0][n - 1];
}
分析
区间DP
区间DP的套路 3层循环
dp[i] [j] 表示 区间i~j的范围内 打印的最少次数
假设字符串i之后 不存在相同的字符 那么
dp[i] [j] = dp[i+1] [j] + 1
遍历分割点
dp[i] [j] = Math.min(dp[i] [j],dp[i] [k]+dp[k+1] [j]);
若s[k] == s[i] 那么dp[i] [k] = d[i] [k-1]
所以dp[i] [j] = Math.min(dp[i] [j],dp[i] [k-1] + dp[k+1] [j])
给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
解答
public boolean checkPossibility(int[] nums) {
// 记录是不是第一次进行改变
boolean is = false;
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] > nums[i + 1]) {
if (is) {
return false;
}
// 判断是不是第一个元素
if (i != 0) {
// 如果 nums[i-1] > nums[i+1],则将 nums[i+1] 改为 nums[i] 即可。
if (nums[i - 1] > nums[i + 1]) {
nums[i + 1] = nums[i];
} else {
// 如果 nums[i-1] <= nums[i+1],则将 nums[i] 改为 nums[i-1]<= nums[i] <=nums[i+1] 即可。
nums[i] = nums[i + 1];
}
} else {
// 当 i = 0 时,则将 nums[i] 改为任意一个小于等于 nums[i+1] 的数即可。
nums[i] = nums[i + 1];
}
is = true;
}
}
return true;
}
分析
若当前 位置大于后一个位置
若是第二次出现,则说明需要修改两次 直接返回false
下面分情况
若当前位置是0,那么直接修改当前位置等于后一个位置的值
若不为0,也分两种情况
当前位置的前一个值 大于 当前位置的后一个值 例如 5 6 3
当前是6 前一个位置是5 后一个位置是3。5大于3 此时将3修改成 6
当前位置的前一个值 小于等于当前位置的后一个值 3 6 4
当前是6 前一个位置是3 后一个位置4,3小于4 此时将6 修改成4
给定两个整数 n 和 k,你需要实现一个数组,这个数组包含从 1 到 n 的 n 个不同整数,同时满足以下条件:
① 如果这个数组是 [a1, a2, a3, … , an] ,那么数组 [|a1 - a2|, |a2 - a3|, |a3 - a4|, … , |an-1 - an|] 中应该有且仅有 k 个不同整数;.
② 如果存在多种答案,你只需实现并返回其中任意一种.
解答
public int[] constructArray(int n, int k) {
int[] res = new int[n];
int index = 0;
int left = 1;
int right = n;
boolean flag = true;
while(k > 1){
if(flag){
res[index++] = left++;
}else res[index++] = right--;
k--;
flag = !flag;
}
for(int i = index;i < n;i++){
if(flag){
res[i] = left++;
}else res[i] = right--;
}
return res;
}
分析
几乎每一个人都用 乘法表。但是你能在乘法表中快速找到第k小的数字吗?
给定高度m 、宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字。
解答
public int findKthNumber(int m, int n, int k) {
int left = 1, right = m * n;
while (left < right) {
int mid = left + (right - left) / 2;
if (kThCount(m, n, mid) >= k) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
public int kThCount(int m, int n, int mid) {
int res = 0,row = 1,col = n;
while(row <= m && col > 0){
if(row * col <= mid){
res += col;
row++;
}else{
col--;
}
}
return res;
}
分析
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
解答
public TreeNode trimBST(TreeNode root, int low, int high) {
TreeNode res = root;
root = trimBSTLow(root,low);
return trimBSTHight(root,high);
}
public TreeNode trimBSTLow(TreeNode root,int low){
if(root == null)return null;
if(root.val < low){
return trimBSTLow(root.right,low);
}else{
root.left = trimBSTLow(root.left,low);
}
return root;
}
public TreeNode trimBSTHight(TreeNode root,int high){
if(root == null) return null;
if(root.val > high){
return trimBSTHight(root.left,high);
}else{
root.right = trimBSTHight(root.right,high);
}
return root;
}
分析
给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能得到的最大值。
解答
public int maximumSwap(int num) {
String number = "" + num;
char[] chars = number.toCharArray();
int len = chars.length;
char[] nums = new char[len];
nums[len-1] = chars[len-1];
int[] indexs = new int[len];
indexs[len-1] = len-1;
for(int i = len - 2;i >= 0;i--){
if(chars[i] - '0' > nums[i+1] - '0'){
nums[i] = chars[i];
indexs[i] = i;
}else {
nums[i] = nums[i+1];
indexs[i] = indexs[i+1];
}
}
for(int i = 0;i < len;i++){
if(chars[i] - '0' < nums[i] -'0'){
char temp = chars[i];
chars[i] = nums[i];
chars[indexs[i]] = temp;
break;
}
}
return Integer.valueOf(new String(chars),10);
}
分析
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。
更正式地说,root.val = min(root.left.val, root.right.val) 总成立。
给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。
解答
//方法1
public int findSecondMinimumValue(TreeNode root) {
Set<Integer> set = new HashSet<>();
PriorityQueue<TreeNode> queue = new PriorityQueue<>(new Comparator<TreeNode>(){
public int compare(TreeNode node1,TreeNode node2){
return node1.val - node2.val;
}
});
dfs(root,queue,set);
if(queue.size() < 2)
return -1;
queue.poll();
return queue.poll().val;
}
public void dfs(TreeNode node,PriorityQueue<TreeNode> queue,Set<Integer> set){
if(!set.contains(node.val)){
set.add(node.val);
queue.add(node);
}
if(node.left != null){
dfs(node.left,queue,set);
}
if(node.right != null){
dfs(node.right,queue,set);
}
}
//方法2
long ans=Long.MAX_VALUE;
public int findSecondMinimumValue(TreeNode root) {
if(root==null) return -1;
int minval=root.val;
dfs(root,minval);
if(ans==Long.MAX_VALUE) return -1;
return (int)ans;
}
private void dfs(TreeNode root, int minval) {
if(root==null) return;
if(root.val>minval&&root.val<ans)
ans=root.val;
dfs(root.left,minval);
dfs(root.right,minval);
}
分析
提交结果
给定一个未排序的整数数组,找到最长递增子序列的个数。
解答
//方法1
public int findNumberOfLIS(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int n = nums.length;
int[] dp = new int[n];
int[] counter = new int[n];
Arrays.fill(dp, 1);
Arrays.fill(counter, 1);
int max = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < i; j++) {
if(nums[i] > nums[j]) {
if(dp[j] + 1 > dp[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
counter[i] = counter[j];
}else if(dp[j] + 1 == dp[i]) {
counter[i] += counter[j];
}
}
}
max = Math.max(max, dp[i]);
}
int res = 0;
for(int i = 0; i < n; i++) {
if(dp[i] == max) res += counter[i];
}
return res;
}
分析
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。
解答
public int findLengthOfLCIS(int[] nums) {
if(nums.length == 0)return 0;
int last = nums[0];
int temp = 1;
int res = 1;
for(int i = 1;i < nums.length;i++){
int cur = nums[i];
if(cur > last){
temp++;
res = Math.max(temp,res);
}
else temp = 1;
last = cur;
}
return res;
}
分析
你被请来给一个要举办高尔夫比赛的树林砍树。树林由一个 m x n 的矩阵表示, 在这个矩阵中:
每一步,你都可以向上、下、左、右四个方向之一移动一个单位,如果你站的地方有一棵树,那么你可以决定是否要砍倒它。
你需要按照树的高度从低向高砍掉所有的树,每砍过一颗树,该单元格的值变为 1(即变为地面)。
你将从 (0, 0) 点开始工作,返回你砍完所有树需要走的最小步数。 如果你无法砍完所有的树,返回 -1 。
可以保证的是,没有两棵树的高度是相同的,并且你至少需要砍倒一棵树。
解答
int[] rArr = new int[]{-1, 1, 0, 0};
int[] cArr = new int[]{0, 0, -1, 1};
public int cutOffTree(List<List<Integer>> forest) {
int rows = forest.size();
int columns = forest.get(0).size();
int[][] arr = new int[rows * columns][];
int[][] grid = new int[rows][columns];
for (int i = 0; i < forest.size(); i++) {
for (int j = 0; j < forest.get(0).size(); j++) {
int height = forest.get(i).get(j);
arr[i * columns + j] = new int[]{i, j, height};
grid[i][j] = height;
}
}
Arrays.sort(arr, Comparator.comparingInt(tree -> tree[2]));
int step = 0;
int[] src = new int[]{0, 0};
for (int[] dest : arr) {
if (dest[2] <= 1) {
continue;
}
int temp = bfs(src, dest, grid);
if (temp < 0) {
return -1;
}
step += temp;
src = dest;
}
return step;
}
private int bfs(int[] src, int[] dest, int[][] grid) {
int rows = grid.length;
int columns = grid[0].length;
boolean[][] seen = new boolean[rows][columns];
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{src[0], src[1], 0});
while (!queue.isEmpty()) {
int[] cur = queue.poll();
if (cur[0] == dest[0] && cur[1] == dest[1]) {
return cur[2];
}
for (int i = 0; i < 4; i++) {
int nextR = cur[0] + rArr[i];
int nextC = cur[1] + cArr[i];
if (inGrid(nextR, nextC, rows, columns) && !seen[nextR][nextC] && grid[nextR][nextC] > 0) {
queue.add(new int[]{nextR, nextC, cur[2] + 1});
seen[nextR][nextC] = true;
}
}
}
return -1;
}
private boolean inGrid(int r, int c, int maxR, int maxC) {
return 0 <= r && r < maxR && 0 <= c && c < maxC;
}
分析
设计一个使用单词列表进行初始化的数据结构,单词列表中的单词 互不相同 。 如果给出一个单词,请判定能否只将这个单词中一个字母换成另一个字母,使得所形成的新单词存在于你构建的字典中。
实现 MagicDictionary 类:
解答
class MagicDictionary {
private Trie trie;
/**
* Initialize your data structure here.
*/
public MagicDictionary() {
trie = new Trie();
}
/**
* Build a dictionary through a list of words
*/
public void buildDict(String[] dict) {
for (String s : dict) {
trie.insert(s);
}
}
/**
* Returns if there is any word in the trie that equals to the given word after modifying exactly one character
*/
public boolean search(String word) {
return trie.search(word);
}
static class Trie {
static class Node {
boolean isWord;
Node[] children;
public Node(boolean isWord) {
this.isWord = isWord;
children = new Node[26];
}
}
private Node root;
public Trie() {
this.root = new Node(false);
}
public void insert(String word) {
Node node = root;
for (char c : word.toCharArray()) {
int idx = c - 'a';
if (node.children[idx] == null) {
node.children[idx] = new Node(false);
}
node = node.children[idx];
}
node.isWord = true;
}
public boolean search(String word) {
return search(word, 0, 1, root);
}
private boolean search(String word, int i, int num, Node root) {
if (num < 0) {
return false;
}
if (i == word.length()) {
return num == 0 && root.isWord;
}
char c = word.charAt(i);
int idx = c - 'a';
for (int j = 0; j < 26; j++) {
if (root.children[j] == null) {
continue;
}
if (idx == j) {
if (search(word, i + 1, num, root.children[idx])) {
return true;
}
} else if (search(word, i + 1, num - 1, root.children[j])) {
return true;
}
}
return false;
}
}
}
分析
实现一个 MapSum 类,支持两个方法,insert 和 sum:
解答
class MapSum {
TrimTree trimTree;
/** Initialize your data structure here. */
public MapSum() {
this.trimTree = new TrimTree();
}
public void insert(String key, int val) {
TrimTree t = this.trimTree;
for(int i = 0;i < key.length();i++){
char cur = key.charAt(i);
if(t.children[cur-'a'] == null){
t.children[cur - 'a'] = new TrimTree();
}
t = t.children[cur - 'a'];
}
t.val = val;
}
public int sum(String prefix) {
TrimTree t = this.trimTree;
for(int i = 0;i < prefix.length();i++){
char cur = prefix.charAt(i);
if(t.children[cur - 'a'] == null)return 0;
t = t.children[cur - 'a'];
}
return sum(t);
}
public int sum(TrimTree t){
int sum = t.val;
for(int i = 0;i < 26;i++){
if(t.children[i]!=null)
sum += sum(t.children[i]);
}
return sum;
}
class TrimTree{
int val;
TrimTree[] children;
public TrimTree(){
children = new TrimTree[26];
}
}
}
分析
给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
解答
//方法1
public boolean checkValidString(String s) {
return checkValidString(s,0,0,0);
}
public boolean checkValidString(String s,int leftNumber,int rightNumber,int index){
if(index == s.length() && leftNumber == rightNumber)return true;
else if(index == s.length())return false;
char cur = s.charAt(index);
if(cur == '('){
return checkValidString(s,leftNumber+1,rightNumber,index+1);
}else if(cur == ')'){
if(leftNumber > rightNumber)
return checkValidString(s,leftNumber,rightNumber + 1,index + 1);
else return false;
}else{
if(leftNumber > rightNumber)
return checkValidString(s,leftNumber + 1,rightNumber, index + 1)
|| checkValidString(s,leftNumber,rightNumber + 1, index + 1)
|| checkValidString(s,leftNumber,rightNumber,index + 1);
else return checkValidString(s,leftNumber + 1,rightNumber, index + 1)
|| checkValidString(s,leftNumber,rightNumber,index + 1);
}
}
//方法2
public boolean checkValidString(String s) {
int n = s.length();
int min = 0,max = 0;
for(int i = 0;i < n;i++){
char cur = s.charAt(i);
if(cur == '('){
min++;
max++;
}else if(cur == ')'){
if(max <= 0)return false;
if(min > 0)min--;
max--;
}else{
if(min > 0)min--;
max++;
}
}
return min == 0;
}
//方法3
public boolean checkValidString(String s) {
int n = s.length();
Stack<Integer> left = new Stack<>(),star = new Stack<>();
for(int i = 0;i < n;i++){
char cur = s.charAt(i);
if(cur == '(')
left.push(i);
else if(cur == '*')
star.push(i);
else {
if(left.size() > 0)left.pop();
else if(star.size() > 0)star.pop();
else return false;
}
}
while(!left.isEmpty() && !star.isEmpty()){
if(left.pop() > star.pop())return false;
}
return left.isEmpty();
}
分析
提交结果
你有 4 张写有 1 到 9 数字的牌。你需要判断是否能通过 *,/,+,-,(,) 的运算得到 24。
解答
static final int TARGET = 24;
static final double EPSILON = 1e-6;
static final int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;
public boolean judgePoint24(int[] nums) {
List<Double> list = new ArrayList<Double>();
for (int num : nums) {
list.add((double) num);
}
return solve(list);
}
public boolean solve(List<Double> list) {
if (list.size() == 0) {
return false;
}
if (list.size() == 1) {
return Math.abs(list.get(0) - TARGET) < EPSILON;
}
int size = list.size();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i != j) {
List<Double> list2 = new ArrayList<Double>();
for (int k = 0; k < size; k++) {
if (k != i && k != j) {
list2.add(list.get(k));//没有被选到的数字放在list2当中
}
}
for (int k = 0; k < 4; k++) {//枚举4种运算
if (k < 2 && i > j) {// + 和 * 的情况下 i < j 的时候已经算过了 i > j的话 就不用重复计算
continue;
}
if (k == ADD) {
list2.add(list.get(i) + list.get(j));
} else if (k == MULTIPLY) {
list2.add(list.get(i) * list.get(j));
} else if (k == SUBTRACT) {
list2.add(list.get(i) - list.get(j));
} else if (k == DIVIDE) {
if (Math.abs(list.get(j)) < EPSILON) {//0无法作为除数
continue;
} else {
list2.add(list.get(i) / list.get(j));
}
}
if (solve(list2)) {//递归
return true;
}
list2.remove(list2.size() - 1);//回溯
}
}
}
}
return false;
}
分析
解答
public boolean validPalindrome(String s) {
char[] chars = s.toCharArray();
int left = 0;
int right = chars.length-1;
while(left < right){
if(chars[left] == chars[right]){
left++;
right--;
}else{
return validPalindrome(chars,left+1,right) || validPalindrome(chars,left,right-1);
}
}
return true;
}
public boolean validPalindrome(char[] chars,int left,int right){
while(left < right){
if(chars[left] == chars[right]){
left++;
right--;
}else return false;
}
return true;
}
分析