问题描述:有一楼梯,你每次可以爬1个或2个台阶,求爬到第n个台阶有多少种不同的方法。
解决思路:使用动态规划,定义一个数组dp[n+1],dp[i]表示爬到第i个台阶的方法数。显然,dp[0]=1,dp[1]=1,dp[2]=2。对于i>2,dp[i] = dp[i-1] + dp[i-2]。
时间复杂度:O(n),空间复杂度:O(n)。
public class ClimbingStairs {
public int climbStairs(int n) {
if (n <= 2) {
return n;
}
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
问题描述:给定一个无序的整数数组,找到其中最长且递增的子序列的长度。
解决思路:使用动态规划,定义一个数组dp[n],dp[i]表示以第i个元素结尾的最长递增子序列的长度。对于每个元素nums[i],遍历其前面所有元素nums[j],如果nums[i] > nums[j],则更新dp[i] = max(dp[i], dp[j] + 1)。
时间复杂度:O(n^2),空间复杂度:O(n)。
public class LongestIncreasingSubsequence {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, 1);
int maxLength = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxLength = Math.max(maxLength, dp[i]);
}
return maxLength;
}
}
问题描述:给定一个数组prices,它的第i个元素prices[i]表示第i天的股票价格。你只能选择某一天买入该股票,并选择在未来的某一天卖出该股票,设计一个算法来计算你所能获取的最大利润。
解决思路:使用动态规划,定义两个变量buy和sell,分别表示截止到当前的最低购买价格和最大利润。遍历数组prices,更新buy和sell的值。
时间复杂度:O(n),空间复杂度:O(1)。
public class BestTimeToBuyAndSellStock {
public int maxProfit(int[] prices) {
int buy = Integer.MAX_VALUE;
int sell = 0;
for (int price : prices) {
buy = Math.min(buy, price);
sell = Math.max(sell, price - buy);
}
return sell;
}
}
问题描述:给定不同面额的硬币coins和一个总金额amount,计算可以凑成总金额所需的最少硬币个数。如果没有任何一种硬币组合能组成总金额,返回-1。
解决思路:使用贪心算法,从面额最大的硬币开始,尽可能多地使用该硬币,然后再使用次大面额的硬币,以此类推。时间复杂度:O(amount * coins.length),空间复杂度:O(amount)。
import java.util.Arrays;
public class CoinChange {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (coin <= i) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
问题描述:给定两个数组g和s,分别表示孩子们的胃口和饼干的大小。求解最多能满足多少个孩子的胃口。
解决思路:使用贪心算法,先将胃口数组g和饼干数组s排序,然后从胃口最小的孩子开始,尽可能使用最小的饼干满足其胃口,直到没有饼干或者没有孩子可以满足。
时间复杂度:O(max(mlogm, nlogn)),空间复杂度:O(1)。
import java.util.Arrays;
public class AssignCookies {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int i = 0;
int j = 0;
int count = 0;
while (i < g.length && j < s.length) {
if (g[i] <= s[j]) {
count++;
i++;
j++;
} else {
j++;
}
}
return count;
}
}
问题描述:给定一个数组prices,它的第i个元素prices[i]表示第i天的股票价格。设计一个算法来计算你所能获取的最大利润,你可以尽可能地完成更多的交易(多次买卖一支股票)。
解决思路:使用贪心算法,只要今天的价格比昨天的价格高,就进行交易。时间复杂度:O(n),空间复杂度:O(1)。
public class BestTimeToBuyAndSellStockII {
public int maxProfit(int[] prices) {
int profit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
profit += prices[i] - prices[i - 1];
}
}
return profit;
}
}
问题描述:给定一个整数数组nums,求解该数组中逆序对的数量。逆序对是指数组中的两个元素nums[i]和nums[j],满足i < j且nums[i] > nums[j]。
解决思路:使用分治算法,将数组一分为二,分别求解左右两个子数组中的逆序对数量,然后再计算跨越两个子数组的逆序对数量,并将其相加。时间复杂度:O(nlogn),空间复杂度:O(n)。
public class ReversePairs {
public int reversePairs(int[] nums) {
return mergeSort(nums, 0, nums.length - 1);
}
private int mergeSort(int[] nums, int left, int right) {
if (left >= right) {
return 0;
}
int mid = (left + right) / 2;
int count = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right);
int[] merge = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
int p = left;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) {
merge[k++] = nums[i++];
count += j - (mid + 1);
} else {
merge[k++] = nums[j++];
}
}
while (i <= mid) {
merge[k++] = nums[i++];
count += j - (mid + 1);
}
while (j <= right) {
merge[k++] = nums[j++];
}
System.arraycopy(merge, 0, nums, p, k);
return count;
}
}
问题描述:给定一个按照升序排列的整数数组nums,和一个目标值target。如果target在数组中存在,则返回它的第一个和最后一个位置,否则返回[-1, -1]。
解决思路:使用分治算法,先使用二分查找找到target的位置,然后分别向左和向右进行扩展,直到找到target的第一个和最后一个位置。时间复杂度:O(logn),空间复杂度:O(1)。
public class FindFirstAndLastPositionOfElementInSortedArray {
public int[] searchRange(int[] nums, int target) {
int left = binarySearch(nums, target, true);
int right = binarySearch(nums, target, false);
return new int[]{left, right};
}
private int binarySearch(int[] nums, int target, boolean isLeft) {
int left = 0;
int right = nums.length - 1;
int result = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] > target || (isLeft && nums[mid] == target)) {
right = mid - 1;
if (nums[mid] == target) {
result = mid;
}
} else {
left = mid + 1;
if (!isLeft && nums[mid] == target) {
result = mid;
}
}
}
return result;
}
}
问题描述:给定一个整数数组nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
解决思路:使用分治算法,将数组一分为二,分别求解左右两个子数组的最大子数组和,然后再计算跨越两个子数组的最大子数组和,并将其相比较。时间复杂度:O(nlogn),空间复杂度:O(logn)。
public class MaximumSubarray {
public int maxSubArray(int[] nums) {
return maxSubArray(nums, 0, nums.length - 1);
}
private int maxSubArray(int[] nums, int left, int right) {
if (left == right) {
return nums[left];
}
int mid = (left + right) / 2;
int leftMax = maxSubArray(nums, left, mid);
int rightMax = maxSubArray(nums, mid + 1, right);
int crossMax = crossMaxSubArray(nums, left, mid, right);
return Math.max(Math.max(leftMax, rightMax), crossMax);
}
private int crossMaxSubArray(int[] nums, int left, int mid, int right) {
int leftMax = Integer.MIN_VALUE;
int leftSum = 0;
for (int i = mid; i >= left; i--) {
leftSum += nums[i];
leftMax = Math.max(leftMax, leftSum);
}
int rightMax = Integer.MIN_VALUE;
int rightSum = 0;
for (int i = mid + 1; i <= right; i++) {
rightSum += nums[i];
rightMax = Math.max(rightMax, rightSum);
}
return leftMax + rightMax;
}
}