一、爬楼梯
1、题目描述
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
2、解答
(1)动态规划解法
这是一道经典的动态规划题目,但不是求最值而是求组合数量。因为动态规划会将每一个子状态进行记录,我们只需要将子状态的数量记录下来即可。动态规划的题目我们要逐步分析,从开始到最后的情况,并总结为递推公式就能解决了。
分析:爬到第n级的方法数等于爬到第n-1级和n-2级台阶的方法数之和,则递推公式为:Fn=Fn-1+Fn-2
class Solution {
public int climbStairs(int n) {
if(n==1){
return 1;
}
if(n==2){
return 2;
}
//已知递推公式:Fn = Fn-1 + Fn-2
int[] dp = new int[n+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];
}
}
(2)递归解法
class Solution {
public int climbStairs(int n) {
if(n == 1) return 1;
if(n == 2) return 2;
return climbStairs(n-1) + climbStairs(n-2);
}
}
二、买卖股票的最佳时期
1、题目描述
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
2、解答
如果题目中出现了一个全局问题可以分解为逐层递进的子问题,则需要使用动态规划来解决。
动态规划问题十分简单,其关键只有两个:状态
和状态转移方程
决定状态的数量(数组的维度)的是影响因素的数量,我们需要根据不同的影响因数来判定状态转移方程的执行。这两者用代码表示出来后,答案自会出现。对于状态,我们需要思考,第二步的状态怎么获得的,或者说倒数第二步状态怎么转移到最后一步状态的。
class Solution {
public int maxProfit(int[] prices) {
//特殊情况处理
if(prices.length<2 || prices ==null){
return 0;
}
//确定状态
int len = prices.length;
int[][] dp = new int[len][2];
//初始状态
dp[0][1]=-prices[0];
dp[0][0]=0;
//状态转移
for(int i=1;i<len;i++){
//前一天不买,或者今天卖
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
//今天不卖,或者今天买
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[len-1][0];
}
}
三、存在重复元素
1、题目描述
给你一个整数数组 nums
。如果任一值在数组中出现 至少两次 ,返回 true
;如果数组中每个元素互不相同,返回 false
。
2、解答
此题主要是学会使用Set来解决重复项问题。
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i=0;i<nums.length;i++){
if(!set.add(nums[i])){
return true;
}
}
return false;
}
}
四、有效的数独
1、题目描述
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
2、解答
class Solution {
public boolean isValidSudoku(char[][] board) {
//分开3次for循环,使用hashset来辨别
for(int i=0;i<9;i++){
Set<Character> set1 = new HashSet<>();
Set<Character> set2 = new HashSet<>();
Set<Character> set3 = new HashSet<>();
for(int j=0;j<9;j++){
if(board[i][j] != '.' && !set1.add(board[i][j])){
return false;
}
if(board[j][i] != '.' && !set2.add(board[j][i])){
return false;
}
int a = (i/3)*3 + j/3;
int b = (i%3)*3 + j%3;
if(board[a][b] != '.' && !set3.add(board[a][b])){
return false;
}
}
}
return true;
}
}
五、整数反转
1、题目描述
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
2、解答
class Solution {
public int reverse(int x) {
String temp = String.valueOf(x);
boolean negative = false;
char[] c;
if(x<0){
negative = true;
c = temp.substring(1).toCharArray();
}else{
c = temp.toCharArray();
}
int i = 0;
int j = c.length - 1;
while(i<j){
char t = c[j];
c[j] = c[i];
c[i] = t;
i++;
j--;
}
int result;
try{
result = Integer.parseInt(new String(c));
}catch(NumberFormatException e){
return 0;
}
if(negative){
return -result;
}else{
return result;
}
}
}
六、验证回文串
1、题目描述
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个回文串。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是回文串,返回 true ;否则,返回 false 。
2、解答
class Solution {
public boolean isPalindrome(String s) {
//通过左右指针
s = s.toLowerCase();
int len = s.length();
int i = 0;
int j = len-1;
while(i<j){
while(i<j && !Character.isLetterOrDigit(s.charAt(i))){
i++;
}
while(i<j && !Character.isLetterOrDigit(s.charAt(j))){
j--;
}
if(s.charAt(i) != s.charAt(j)){
return false;
}
i++;
j--;
}
return true;
}
}
七、删除链表的倒数第N个节点
1、题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
2、解答
该题技巧:双指针(快慢指针) + 假头(太妙了!)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head.next==null){
return null;
}
//加个头
ListNode temp = new ListNode(0);
temp.next = head;
//快慢指针
int j = n;
ListNode t = temp;
while(j>0){
t = t.next;
j--;
}
ListNode r = temp;
while(t.next!=null){
r = r.next;
t = t.next;
}
r.next = r.next.next;
return temp.next;
}
}
七、反转链表
1、题目描述
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
2、解答
(1)使用栈
class Solution {
public ListNode reverseList(ListNode head) {
Stack<ListNode> stack = new Stack<>();
ListNode cur = head;
while(cur!=null){
stack.push(cur);
cur = cur.next;
}
ListNode result = new ListNode(0);
ListNode temp = result;
while(stack.size()>0){
temp.next = stack.pop();
temp = temp.next;
}
temp.next = null;
return result.next;
}
}
八、环形链表
1、题目描述
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
2、解答
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode fast = head;
ListNode slow = head;
while(fast!=null &&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(slow == fast){
return true;
}
}
return false;
}
}
九、买卖股票的最佳时期
1、题目描述
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
2、解答
class Solution {
public int maxProfit(int[] prices) {
if(prices==null || prices.length==0){
return 0;
}
int len = prices.length;
//状态
int[][] dp = new int[len][2];
//初始状态
dp[0][0] = 0;//未持有
dp[0][1] = -prices[0];//持有
for(int i=1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][1] + prices[i],dp[i-1][0]);
dp[i][1] = Math.max(-prices[i],dp[i-1][1]);
}
return dp[len-1][0];
}
}
十、最大子序和
1、题目描述
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
2、解答
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
//状态
int[] dp = new int[len];
//初始状态
dp[0] = nums[0];
int max = dp[0];
//状态转移
for(int i=1;i<len;i++){
dp[i] = Math.max(0,dp[i-1]) + nums[i];
max = Math.max(dp[i],max);
}
return max;
}
}
十一、打家劫舍
1、题目描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
2、解答
class Solution {
public int rob(int[] nums) {
if(nums.length==0 || nums==null){
return 0;
}
int len = nums.length;
//状态
int[][] dp = new int[len][2];
//初始状态
dp[0][0] = 0;
dp[0][1] = nums[0];
for(int i=1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][1],dp[i-1][0]);
dp[i][1] = dp[i-1][0] + nums[i];
}
return Math.max(dp[len-1][0],dp[len-1][1]);
}
}