String
截取字符串
1) subString();
public String substring(int beginIndex)
//该子字符串从指定索引处的字符开始,直到此字符串末尾。
public String substring(int beginIndex, int endIndex)
//从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。
public static void main(String[] args) {
String Str = new String("hello wrold");
System.out.print("返回值,从第4位截取到字符串末尾 :" );
System.out.println(Str.substring(4) );
System.out.print("返回值,从第4位截取到第10位 :" );
System.out.println(Str.substring(4, 10) );
}
返回值,从第4位截取到字符串末尾 : o wrold
返回值,从第4位截取到第10位 : o wrol
3)split()+正则表达式来进行截取
//逗号分割字符串
String str = "hello, name, 12345, 6789";
String[] strs=str.split(",");
for(int i=0,len=strs.length;i<len;i++){
System.out.println(strs[i].toString());
}
hello
name
12345
6789
2)通过StringUtils提供的方法
//与第一种方法效果一样
StringUtils.substring("hello world", 4); // 返回值,从第4位截取到字符串末尾 : o wrold
StringUtils.substring("hello world", 4, 10); // 返回值,从第4位截取到第10位 : o wrol
//截取某个字符串之前的字符
StringUtils.substringBefore("hello world", "l");
//结果是:he 这里是以第一个”l”,为标准。
StringUtils.substringBeforeLast("hello world", "l");
//结果为:hello wor 这里以最后一个“l”为准。
//截取某个字符串之后的字符
StringUtils.substringAfter("hello world", "l");
//结果是:lo world 这里是以第一个”l”,为标准。
StringUtils.substringAfterLast("hello world", "l");
//结果为:d 这里以最后一个“l”为准。
//截取两个字符串之间隔的字符
StringUtils.substringBetween("hello world", "o");
//结果是: w 两个o之间的字符串。
StringUtils.substringBetween("hello world", "l", "r");
//结果是: lo wo 第一个字符“l”与第一个字符“r”之间的字符串
StringUtils.substringsBetween("hello world", "l", "r");
//结果是:数组 [lo wo] 第一个字符“l”与第一个字符“r”之间的字符串,以数组形式返回。
数组
Arrays
Arrays.binarySearch() 查找数组元素,返回下标
import java.util.Arrays;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
int []nums = new int[6];
nums[0]=3; nums[1]=2; nums[2]=4;
// 返回下标
System.out.println(Arrays.binarySearch(nums,2)); //1
//如果找不到元素,返回数组长度+1的负数
// 6==》 -7 3==》-4
System.out.println(Arrays.binarySearch(nums,89)); //-7
}
}
排序
选择排序
int max = b[0] ,index = 0;
for (int i = 1; i < b.length; i++) {
for (int j = i; j <b.length ; j++) {
if(max<b[i]){
max=b[i];
index=i;
}
}
int tmp =b[index];
b[index]=b[i-1];
b[i-1]=tmp;
max=b[i];
index=i;
}
冒泡排序
for (int i = 1; i < a.length; i++) {
for (int j = 0; j < a.length-i; j++) {
if(a[j]<a[j+1]){
int tmp =a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
}
动态规划
解题5步曲
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组
爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的⽅法可以爬到楼顶呢?
注意:给定 n 是⼀个正整数。
示例 1:
输⼊: 2
输出: 2
解释: 有两种⽅法可以爬到楼顶。
1 阶 + 1 阶
2 阶
示例 2:
输⼊: 3
输出: 3
解释: 有三种⽅法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
思路 : 1阶 1种
2阶 2种
3阶 3种(一次只能爬一阶和二阶,所以只能由第一阶楼梯+2 或者
第二阶楼梯+1 得到 ;)
所以我们得出 f(n)=f(n-1)+f(n-2);
if (n <= 1) {
return n; // 因为下⾯直接对dp[2]操作了,防⽌空指针
}
int []dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) { // 注意i是从3开始的
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
//状态压缩
//节省空间
if (n <= 1) {
return n;
}
int dp[3];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
int sum = dp[1] + dp[2];
dp[1] = dp[2];
dp[2] = sum;
}
return dp[2];
扩展 这道题⽬还可以继续深化,就是⼀步⼀个台阶,两个台阶,三个台阶,直到 m个台阶,有多少种⽅法爬
到n阶楼顶
这其实是⼀个完全背包问题
int climbStairs(int n) {
int dp[] = new int[n+1];
dp[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) { // 把m换成2,就可以AC爬楼梯这道题
if (i - j >= 0) dp[i] += dp[i - j];
}
}
return dp[n];
}
最小花费爬楼梯
数组的每个下标作为⼀个阶梯,第 i 个阶梯对应着⼀个⾮负数的体⼒花费值 cost[i](下标从 0 开始)。
每当你爬上⼀个阶梯你都要花费对应的体⼒值,⼀旦⽀付了相应的体⼒值,你就可以选择向上爬⼀个阶
梯或者爬两个阶梯。
请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
输⼊:cost = [10, 15, 20]
输出:15
解释:最低花费是从 cost[1] 开始,然后⾛两步即可到阶梯顶,⼀共花费 15 。
示例 2:输⼊:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出:6
解释:最低花费⽅式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,⼀共花费 6
- 确定dp数组以及下标的含义
dp[i]的定义:到达第i个台阶所花费的最少体⼒为dp[i]。
- 确定递推公式
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; 最小的加上 花费的体力
-
dp数组如何初始化
dp[0]=cost[0] do[1]=cost[1];
初始化跟斐波那契数列初始化差不多啊!
-
确定遍历顺序
从前到后遍历 , 先得算出最优的 dp[i-1] , dp[i-2]
int minCostClimbingStairs(int[]cost) { int []dp = new int[cost.length]; dp[0] = cost[0]; dp[1] = cost[1]; for (int i = 2; i < cost.length; i++) { dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; } // 注意最后⼀步可以理解为不⽤花费,所以取倒数第⼀步,第⼆步的最少值 return Math.min(dp[cost.size() - 1], dp[cost.size() - 2]); //状态压缩 int minCostClimbingStairs(int[] cost) { int dp0 = cost[0]; int dp1 = cost[1]; for (int i = 2; i < cost.size(); i++) { int dpi = Math.min(dp0, dp1) + cost[i]; dp0 = dp1; // 记录⼀下前两位 dp1 = dpi; } return Math.min(dp0, dp1);
不同路径
⼀个机器⼈位于⼀个 m x n ⽹格的左上⻆ (起始点在下图中标记为 “Start” )。
机器⼈每次只能向下或者向右移动⼀步。机器⼈试图达到⽹格的右下⻆(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
-
dp[i,j]表示 (i,j) 位置的路径数
-
递推公式 dp[i,j] = dp[i,j-1] + dp[i-1,j] 只能向下,向右移动
-
初始化
for (int i = 0; i < m; i++) dp[i][0] = 1; for (int j = 0; j < n; j++) dp[0][j] = 1;
-
遍历循序
从前到后 ,dp数组的值是由前面推出来的
int uniquePaths(int m, int n) {
int dp [][] = new int[m][n];
for (int i = 0; i < m; i++)
dp[i][0] = 1;
for (int j = 0; j < n; j++)
dp[0][j] = 1;
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
整数拆分
给定⼀个正整数 n,将其拆分为⾄少两个正整数的和,并使这些整数的乘积最⼤化。 返回你可以获得的
最⼤乘积。
示例 1:
输⼊: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输⼊: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不⼩于 2 且不⼤于 58
- dp[]数组含义 , 分拆数字i,可以得到的最⼤乘积为dp[i]。
- 递推公式 , dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
- 初始化 , dp[2]=1; 0 和 1 是无法拆分成两个正整数的,
- 遍历顺序,从前到后,拆分一个整数,得先获得子拆分数的最优解
int integerBreak(int n) {
int []dp = new int [n+1];
dp[2] = 1;
for (int i = 3; i <= n ; i++) {
for (int j = 1; j < i - 1; j++) {
dp[i] = Math.max(dp[i], Math.max((i - j) * j, dp[i - j] * j));
}
}
return dp[n];
}
附上另一个版本,更容易理解
int integerBreak(int n) {
if (n <= 3)
return 1 * (n - 1);
int []dp = new int [n+1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
for (int i = 4; i <= n ; i++) {
for (int j = 1; j < i - 1; j++) {
dp[i] = max(dp[i], dp[i - j] * dp[j]);
}
}
return dp[n];
}