一、JZ66 构建乘积数组(简单)
1、双向遍历
![](https://img-blog.csdnimg.cn/img_convert/7acaa3007e4ecda0e83c04ce3f49f4ae.png)
如上图所示,矩阵中由对角线1将其分成了上三角和下三角。我们先看下三角,如果我们累乘的时候,B[1]是在B[0]的基础上乘了新增的一个A[0],B[2]是在B[1]的基础上乘了新增的一个A[1],那我们可以遍历数组的过程中不断将数组B的前一个数与数组A的前一个数相乘就得到了下三角中数组B的当前数。同理啊,我们在上三角中,用一个变量存储从右到左的累乘,每次只会多乘上一个数字。这样,两次遍历就可以解决。
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
//初始化数组B
int[] B = new int[A.length];
B[0] = 1;
//先乘左边,从左到右
for(int i = 1; i < A.length; i++)
//每多一位由数组B左边的元素多乘一个前面A的元素
B[i] = B[i - 1] * A[i - 1];
int temp = 1;
//再乘右边,从右到左
for(int i = A.length - 1; i >= 0; i--){
//temp为右边的累乘
B[i] *= temp;
temp *= A[i];
}
return B;
}
}
二、JZ50 第一个只出现一次的字符(简单)
1、使用辅助ArrayList
import java.util.ArrayList;
public class Solution {
public int FirstNotRepeatingChar(String str) {
ArrayList<Character> list = new ArrayList<>();
ArrayList<Character> reapyList = new ArrayList<>();
for(int i = 0;i< str.length();i++) {
if(!reapyList.contains(str.charAt(i))) {
if(list.contains(str.charAt(i))) {
reapyList.add(str.charAt(i));
list.remove(list.indexOf(str.charAt(i)));
} else {
list.add(str.charAt(i));
}
}
}
if(list.size() == 0) return -1;
for(int i = 0;i< str.length();i++) {
if(list.get(0) == str.charAt(i)) {
return i;
}
}
return -1;
}
}
2、使用HashMap
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
HashMap<Character, Integer> mp = new HashMap<>();
//统计每个字符出现的次数
for(int i = 0; i < str.length(); i++)
mp.put(str.charAt(i), mp.getOrDefault(str.charAt(i), 0) + 1);
//找到第一个只出现一次的字母
for(int i = 0; i < str.length(); i++)
if(mp.get(str.charAt(i)) == 1)
return i;
//没有找到
return -1;
}
}
三、JZ5 替换空格(简单)
1、正常遍历替换
import java.util.*;
public class Solution {
/**
* @param s string字符串
* @return string字符串
*/
public String replaceSpace (String s) {
// write code here
StringBuffer str = new StringBuffer();
for(int i = 0;i< s.length(); i++) {
if(s.charAt(i) == ' ') {
str.append("%20");
} else {
str.append(s.charAt(i));
}
}
return str.toString();
}
}
四、JZ21 调整数组顺序使奇数位于偶数前面(一)(中等)
1、在原数组上修改
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型一维数组
* @return int整型一维数组
*/
public int[] reOrderArray (int[] array) {
// write code here
int i = 0;
for (int j=0; j < array.length; ++j) {
// 遇到奇数时
if (array[j] % 2 == 1) {
// 先将 array[j] 赋值
int tmp = array[j];
// 将 【i, j-1】数组后移动
for (int k=j-1; k>=i; --k) {
array[k+1] = array[k];
}
// 将array[j]插入到 i++ 的位置
array[i++] = tmp;
}
}
return array;
}
}
2、使用奇数偶数辅助数组
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型一维数组
* @return int整型一维数组
*/
public int[] reOrderArray (int[] array) {
// write code here
ArrayList<Integer> ji = new ArrayList<>();
ArrayList<Integer> ou = new ArrayList<>();
int[] result = new int[array.length];
int index = 0;
for(int i= 0;i< array.length;i++) {
if(array[i] % 2 == 0) {
ou.add(array[i]);
} else {
ji.add(array[i]);
}
}
ji.addAll(ou);
for(int item: ji) {
result[index] = item;
index++;
}
return result;
}
五、JZ39 数组中出现次数超过一半的数字(简单)
1、哈希法
import java.util.HashMap;
public class Solution {
int result = 0;
public int MoreThanHalfNum_Solution(int [] array) {
HashMap<Integer, Integer> hm = new HashMap<>();
for(int item: array) {
hm.put(item, hm.getOrDefault(item, 0) + 1);
}
hm.forEach((k,v) -> {
if(v > (array.length / 2)) {
result = k;
}
});
return result;
}
}
六、JZ43 整数中1出现的次数(从1到n整数中1出现的次数(中等)
1、暴力法
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count=0;
for(int i=1;i<=n;i++){
String q=String.valueOf(i);
char [] chars = q.toCharArray();
for(int j=0;j<chars.length;j++){
if(chars[j]=='1'){
count++;
}
}
}
return count;
}
}
2、数学推导
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count = 0, bitNum = 1, high = n / 10, cur = n % 10, low = 0;
// cur遍历n每一数位
while(cur != 0 || high != 0) {
if(cur < 1) {
// case 1: cur == 0
// cur=0时,高位需要减去一位用于低位进行计算
// 相当于 count = (high - 1) * bitNum + (99 + 1)
count += high * bitNum;
} else if(cur == 1) {
// case 2: cur == 1
// 相当于高位+低位计算结果,即(high * bitNum) + (low + 1)
count += high * bitNum + low + 1;
} else {
// case3: cur > 1
// 相对于cur=0的情况,就不需要高位减去一位来计算低位的结果数了
// 相当于(high * bitNum) + (低位数结果数)
count += (high + 1) * bitNum;
}
// low、cur、high都像左偏移一个位
// bitNum表示cur的数位
low += cur * bitNum;
cur = high % 10;
high = high / 10;
bitNum = bitNum * 10;
}
return count;
}
}
七、JZ45 把数组排成最小的数(中等)
1、重载排序
import java.util.*;
public class Solution {
public String PrintMinNumber(int [] numbers) {
//空数组的情况
if(numbers == null || numbers.length == 0) return "";
String[] str = new String[numbers.length];
//将数字转成字符
for(int i = 0;i< numbers.length;i++) {
str[i] = numbers[i] + "";
}
//按照重载排序
Arrays.sort(str, new Comparator<String>() {
public int compare(String s1, String s2) {
return (s1 + s2).compareTo(s2 + s1);
}
});
StringBuffer result = new StringBuffer();
for(String item: str) {
result.append(item);
}
return result.toString();
}
}
2、冒泡排序
import java.util.*;
public class Solution {
public String PrintMinNumber(int [] numbers) {
//空数组的情况
if(numbers == null || numbers.length == 0)
return "";
String[] nums = new String[numbers.length];
//将数字转成字符
for(int i = 0; i < numbers.length; i++)
nums[i] = numbers[i] + "";
//冒泡排序
for(int i = 0; i < nums.length - 1; i++){
for(int j = 0; j < nums.length - i - 1; j++){
String s1 = nums[j] + nums[j + 1];
String s2 = nums[j + 1] + nums[j];
//比较拼接的大小交换位置
if(s1.compareTo(s2) > 0){
String temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
StringBuilder res = new StringBuilder();
//字符串叠加
for(int i = 0; i < nums.length; i++)
res.append(nums[i]);
return res.toString();
}
}
八、JZ49 丑数(中等)
1、依次比较计算
public int GetUglyNumber_Solution(int index) {
//1 2 3 4 5 6 8
if(index <= 6)
return index; // 加快程序输出
// 三个变量 后面有大作用!
int i2 = 0,i3 = 0,i5 = 0;
int[] res = new int[index];
res[0] = 1; // 第一个丑数为 1
for(int i = 1; i < index; i++){
// 得到下一个丑数,三者中最小的
res[i] = Math.min(res[i2]*2,Math.min(res[i3]*3,res[i5]*5));
/*第一次是 2、3、5比较,得到最小的是2*/
/*第二次是 4、3、5比较,为什么是4了呢?因为上次2已经乘了一次了,所以接下去可以放的丑数在4、3、5之间*/
// 所以开头的三个指针就是来标记2 3 5 乘的次数的
if(res[i] == res[i2]*2)
i2++;
if(res[i] == res[i3]*3)
i3++;
if(res[i] == res[i5]*5)
i5++;
}
return res[index-1];
}
九、JZ74 和为S的连续正数序列(中等)
1、暴力枚举
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer> >();
int sum1 = 0;
//因为序列至少两个数,因此枚举最多到该数字的一半向下取整
int up = (sum - 1) / 2;
//枚举左区间
for(int i = 1; i <= up; i++){
//从左区间往后依次连续累加
for(int j = i; ;j++){
sum1 += j;
//大于目标和则跳出该左区间
if(sum1 > sum){
sum1 = 0;
break;
//等于则找到
}else if(sum1 == sum){
sum1 = 0;
ArrayList<Integer> temp = new ArrayList<Integer>();
//记录线序的数字
for(int k = i; k <= j; k++)
temp.add(k);
res.add(temp);
break;
}
}
}
return res;
}
}
2、滑动窗口
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer> >();
//从1到2的区间开始
for(int l = 1, r = 2; l < r;){
//计算区间内的连续和
int sum1 = (l + r) * (r - l + 1) / 2;
//如果区间内和等于目标数
if(sum1 == sum){
ArrayList<Integer> temp = new ArrayList<Integer>();
//记录区间序列
for(int i = l; i <= r; i++)
temp.add(i);
res.add(temp);
//左区间向右
l++;
//如果区间内的序列和小于目标数,右区间扩展
}else if(sum1 < sum)
r++;
//如果区间内的序列和大于目标数,左区间收缩
else
l++;
}
return res;
}
}
十、JZ57 和为S的两个数字(中等)
1、哈希法
import java.util.*;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> res = new ArrayList<Integer>();
//创建哈希表,两元组分别表示值、下标
HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>();
//在哈希表中查找target-numbers[i]
for(int i = 0; i < array.length; i++){
int temp = sum - array[i];
//若是没找到,将此信息计入哈希表
if(!mp.containsKey(temp)){
mp.put(array[i], i);
}
else{
//取出数字添加
res.add(temp);
res.add(array[i]);
break;
}
}
return res;
}
}
2、双指针
import java.util.*;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> res = new ArrayList<Integer>();
//左右双指针
int left = 0, right = array.length - 1;
//对撞双指针
while(left < right){
//相加等于sum,找到目标
if(array[left] + array[right] == sum){
res.add(array[left]);
res.add(array[right]);
break;
//和太大,缩小右边
}else if(array[left] + array[right] > sum)
right--;
//和太小,扩大左边
else
left++;
}
return res;
}
}
十一、JZ58 左旋转字符串(中等)
1、求余得到切割点进行字符串的切割拼接
public class Solution {
public String LeftRotateString(String str,int n) {
if(str.length() <= 1 || n == 0) return str;
n = n % str.length();
StringBuffer res = new StringBuffer();
res.append(str.substring(n, str.length()));
res.append(str.substring(0, n));
return res.toString();
}
}
2、三次反转
![](https://img-blog.csdnimg.cn/img_convert/42c25cc41970430c766c69cb70f23353.png)
public class Solution {
public String LeftRotateString(String str,int n) {
//取余,因为每次长度为n的旋转数组相当于没有变化
if(str.isEmpty() || str.length() == 0)
return "";
int m = str.length();
n = n % m;
//第一次逆转全部元素
char[] s = str.toCharArray();
reverse(s, 0, m - 1);
//第二次只逆转开头m个
reverse(s, 0, m - n - 1);
//第三次只逆转结尾m个
reverse(s, m - n, m - 1);
return new String(s);
}
//反转函数
private void reverse(char[] s, int start, int end){
while(start < end){
swap(s, start++, end--);
}
}
//交换函数
private void swap(char[] s, int a, int b){
char temp = s[a];
s[a] = s[b];
s[b] = temp;
}
}
十二、JZ62 孩子们的游戏(圆圈中最后剩下的数)(中等)
1、我们用约瑟夫思想来解决该题。具体的方法如下:首先定义n个人,报m次的解是f(n,m),n,m以及编号为0的小朋友确定了,最后的幸运儿也就确定了,第一次报数后,m-1的小朋友出列,接着从m开始报数,但是最后的解是一样的,令g(n-1,m)表示第二次抽人,解是等于f(n,m)的,这两种情况的排列方式不同,一个从0开始报,一个从m开始报,所以我们把第二种情况也排成从0开始报,就是每个人的下标都减少m即可,那么最后的幸运儿加上m,就得出在原来序列的下标了,解就是f(n-1,m)+m,构成了一个递归问题。但由于是一个循环的环,超出环从头排起,就对每一次环的长度进行取余。关系式为f(n,m) = (f(n-1,m) + m)%n。
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(n == 0 || m == 0) return -1;
if(n == 1) return 0;
int x = LastRemaining_Solution(n -1 , m);
return (m + x) % n;
}
}
十三、JZ75 字符流中第一个不重复的字符(中等)
1、使用辅助ArrayList
import java.util.ArrayList;
public class Solution {
//Insert one char from stringstream
ArrayList<Character> res = new ArrayList<>();
ArrayList<Character> repay = new ArrayList<>();
public void Insert(char ch)
{
if(res.contains(ch)) {
res.remove(res.indexOf(ch));
repay.add(ch);
} else {
if (!repay.contains(ch)) {
res.add(ch);
}
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
if(res.size() > 0) {
return res.get(0);
} else {
return '#';
}
}
}
2、哈希法
import java.util.*;
public class Solution {
private StringBuilder s = new StringBuilder();
private HashMap<Character, Integer> mp = new HashMap<>();
//Insert one char from stringstream
public void Insert(char ch)
{
//插入字符
s.append(ch);
//哈希表记录字符出现次数
mp.put(ch, mp.getOrDefault(ch, 0) + 1);
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
//遍历字符串
for(int i = 0; i < s.length(); i++)
//找到第一个出现次数为1的
if(mp.get(s.charAt(i)) == 1)
return s.charAt(i);
//没有找到
return '#';
}
}
十四、JZ14 剪绳子(中等)
1、贪心思想
![](https://img-blog.csdnimg.cn/img_convert/fe82423f02a84348aa13e37d112de2c3.png)
public class Solution {
public int cutRope(int target) {
//不超过3直接计算
if(target <= 3)
return target - 1;
int res = 1;
while(target > 4){
//连续乘3
res *= 3;
target -= 3;
}
return res * target;
}
}
2、动态规划
![](https://img-blog.csdnimg.cn/img_convert/b168584be5400c6fb56094c1a2eab79f.png)
import java.util.*;
public class Solution {
public int cutRope(int target) {
//不超过3直接计算
if(target <= 3)
return target- 1;
//dp[i]表示长度为i的绳子可以被剪出来的最大乘积
int[] dp = new int[target + 1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
dp[4] = 4;
//遍历后续每一个长度
for(int i = 5; i <= target; i++)
//可以被分成两份
for(int j = 1; j < i; j++)
//取最大值
dp[i] = Math.max(dp[i], j * dp[i - j]);
return dp[target];
}
}
十五、JZ81 调整数组顺序使奇数位于偶数前面(二)(简单)
1、双指针交换法
import java.util.*;
public class Solution {
/**
* @param array int整型一维数组
* @return int整型一维数组
*/
public int[] reOrderArrayTwo (int[] array) {
// write code here
int temp = -1;
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) {
for (int j = array.length - 1; j > i; j--) {
if (j > i) {
if (array[j] % 2 == 1) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
break;
}
} else {
return array;
}
}
}
}
return array;
}
}
2、双指针法
import java.util.*;
public class Solution {
public int[] reOrderArrayTwo (int[] array) {
int n = array.length;
int[] res = new int[n];
//统计奇数个数
int odd = 0;
//遍历统计
for(int i = 0; i < n; i++){
if(array[i] % 2 == 1)
odd++;
}
//x与y分别表示答案中奇偶数的坐标
int x = 0, y = odd;
for(int i = 0; i < n; i++){
//奇数在前
if(array[i] % 2 == 1){
res[x] = array[i];
x++;
//偶数在后
}else{
res[y] = array[i];
y++;
}
}
return res;
}
}
十六、JZ83 剪绳子(进阶版)(较难)
1、使用快速幂,不然时间复杂度会超
import java.util.*;
public class Solution {
final int MOD = 998244353;
private long pow(long n) {
long ans = 1;
long a = 3;
while(n > 0) {
if(n%2 == 1) ans = (ans * a )% MOD;
a = (a*a)%MOD;
n /= 2;
}
return ans% MOD;
}
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param number long长整型
* @return long长整型
*/
public long cutRope (long number) {
if(number < 4) return number -1;
if(number % 3 == 0) return pow(number/3) % MOD;
if(number % 3 == 2) return pow(number/3) * 2 % MOD;
return pow(number/3 -1) *4% MOD;
}
}
十七、JZ17 打印从1到最大的n位数(简单)
1、数组输出
import java.util.*;
public class Solution {
/**
* @param n int整型 最大位数
* @return int整型一维数组
*/
public int[] printNumbers (int n) {
// write code here
int num = 1;
for(int i = 0;i < n; i++) {
num *= 10;
}
int[] res = new int[num -1];
for(int i =1; i < num; i++) {
res[i-1] = i;
}
return res;
}
}