1、二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖
先)。”
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) { // 递归结束条件
return root;
}
// 后序遍历
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null && right == null) { // 若未找到节点 p 或 q
return null;
}else if(left == null && right != null) { // 若找到一个节点
return right;
}else if(left != null && right == null) { // 若找到一个节点
return left;
}else { // 若找到两个节点
return root;
}
}
}
2、二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
return root;
}
}
3、剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
时间复杂度:$O(n²)
空间复杂度:$O(n)
class Solution {
public int cuttingRope(int n) {
//定义dp数组,dp[i]表示长度为i的绳子剪成m端后长度的最大乘积(m>1)
int dp[] = new int[n+1];
//初始化
dp[2] = 1;
//目标:求出dp[n]
//dp[2]已知,从dp[3]开始求,直到求出dp[n]
for(int i = 3;i <= n;i++){
//首先对绳子剪长度为j的一段,其中取值范围为 2 <= j < i
for(int j = 2;j <= i;j++){
//转移方程如下
dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
//Math.max(j*(i-j),j*dp[i-j]是由于减去第一段长度为j的绳子后,可以继续剪也可以不剪
//Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]))是当j不同时,求出最大的dp[i]
}
}
//现在已经求出每个长度i对应的最大乘积,返回dp[n]
return dp[n];
}
}
4、整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
class Solution {
public int integerBreak(int n) {
int dp[] = new int[n + 1];
dp[2] = 1;
for(int i = 3; i <= n; i++){
for(int j = 2; j <=i; j++){
dp[i] = Math.max(dp[i],Math.max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];
}
}
5、青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
时间复杂度:$O(n)
空间复杂度:$O(n)
class Solution {
public int numWays(int n) {
if(n == 0){
return 1;
}
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i < dp.length; i++){
dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
}
return dp[n];
}
}
6、三步问题
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
class Solution {
public int waysToStep(int n) {
if (n <= 2) {
return n;
}
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for (int i = 4; i < dp.length; i++) {
//取模,对两个较大的数之和取模再对整体取模,防止越界(这里也是有讲究的)
//假如对三个dp[i-n]都 % 1000000007,那么也是会出现越界情况(导致溢出变为负数的问题)
//因为如果本来三个dp[i-n]都接近 1000000007 那么取模后仍然不变,但三个相加则溢出
//但对两个较大的dp[i-n]:dp[i-2],dp[i-3]之和mod 1000000007,那么这两个较大的数相加大于 1000000007但又不溢出
//取模后变成一个很小的数,与dp[i-1]相加也不溢出
//所以取模操作也需要仔细分析
dp[i] = (dp[i-1] + (dp[i-2] + dp[i-3]) % 1000000007) % 1000000007;
}
return dp[n];
}
}
7、斐波那契数
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n) 。
class Solution {
public int fib(int n) {
if(n <= 1){
return n;
}
int[] dp= new int[n + 1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2; i < dp.length; i++){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
8、跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
时间复杂度:$O(n)
空间复杂度:$O(1)
public class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int right = 0;
for (int i = 0; i < n; i++) {
if (i <= right) {
right = Math.max(right, i + nums[i]);
if (right >= n - 1) {
return true;
}
}
}
return false;
}
}
9、跳跃游戏 II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
class Solution {
public int jump(int[] nums) {
int n = nums.length;
int right = 0;
int end = 0;
int steps = 0;
for (int i = 0; i < n - 1; i++) {
if(i <= right){
// 找到能跳的最远的
right = Math.max(right, i + nums[i]);
if (i == end) { // 遇到边界,就更新边界,并且步数加一
end = right;
steps++;
}
}
}
return steps;
}
}
9、岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
时间复杂度:$O(m*n)
空间复杂度:$O(1)
class Solution {
public int numIslands(char[][] grid) {
int res = 0;
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(grid[i][j] == '1'){
res++;
bfs(grid,i,j);
}
}
}
return res;
}
public void bfs(char[][] grid, int i, int j){
if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] != '1'){
return;
}
grid[i][j] = 2;
bfs(grid,i - 1,j);
bfs(grid,i + 1,j);
bfs(grid,i,j - 1);
bfs(grid,i, j + 1);
}
}