LeetCode精选TOP面试题

验证回文数(双指针/API翻转方法)

题目

分析

1. 双指针法:先用stringbuilder筛选出只有字符和数字的字符串,然后进行两端双指针移动判断。
2. API翻转函数:先用stringbuilder筛选出只有字符和数字的字符串,字符串翻转API得到sgood的逆序字符串。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class HuiWen {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.nextLine(); //可以输入空格
System.out.println(isPalindrome(s));
}

public static boolean isPalindrome(String s) {
StringBuilder sb=new StringBuilder();
int len=s.length();
for(int i=0;i<len;i++){
if(s.charAt(i)>='0'&&s.charAt(i)<='9'||s.charAt(i)>='a'&&s.charAt(i)<='z'){
sb.append(s.charAt(i));
}
if(s.charAt(i)>='A'&&s.charAt(i)<='Z'){
sb.append((char)(s.charAt(i)+32));
}
}
int left=0;
int right=sb.length()-1;
while(left<right){
if(sb.charAt(left)==sb.charAt(right)){
left++;
right--;
}else{
return false;
}
}
return true;
}

public static boolean isPalindromeer(String s) {
StringBuilder sb=new StringBuilder();
int len=s.length();
for(int i=0;i<len;i++){
char ch=s.charAt(i);
if(Character.isLetterOrDigit(ch)){
sb.append(Character.toLowerCase(ch));
}
}
StringBuilder sbfan=new StringBuilder(sb).reverse();
return sb.toString().equals(sbfan.toString());
}

}

结果


两数相加(预设pre头链表)

题目

分析

参考大佬思路:

https://leetcode-cn.com/problems/add-two-numbers/solution/hua-jie-suan-fa-2-liang-shu-xiang-jia-by-guanpengc/

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre=new ListNode(0);
ListNode cur=pre; //cur是当前pre位置
int carry=0;
while(l1!=null||l2!=null){
int x = l1 == null ? 0 : l1.val; //计算l1当前节点的值
int y = l2 == null ? 0 : l2.val; //计算l2当前节点的值
int sum=x+y+carry; //计算总和

carry=sum/10; //carry代表进位
sum=sum%10; //当前位置应该放的数字
cur.next=new ListNode(sum); //当前节点的后面节点的值是sum

cur=cur.next; //往后移动
if(l1 != null) {
l1 = l1.next;
}
if(l2 != null) {
l2 = l2.next;
}
if(carry == 1) { //有进位就下一个节点给这个进位 到时候会反序输出
cur.next = new ListNode(carry);
}

}
return pre.next; //cur一直移动到新链表的尾 pre.next返回新链表的第一个节点
}

}

结果


无重复字符的最长子串(map集合)

题目

分析

1.使用map集合不断存放当前值和所在字符的下标位置,然后不断更新ans结果值。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class WuChongFuZiChuan {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
System.out.println(lengthOfLongestSubstring(s));
}

public static int lengthOfLongestSubstring(String s) {
int n = s.length(); //获取字符串长度
int ans = 0;
Map<Character, Integer> map = new HashMap<>();
int start=0;
for (int end = 0; end < n; end++) {
char a = s.charAt(end); //获取当前位置字符
if (map.containsKey(a)) {
start = Math.max(map.get(a), start);
}
ans = Math.max(ans, end - start + 1); //算距离
map.put(s.charAt(end), end + 1); //当前map里面存当前字符串字符和下标
}
return ans ;
}

}

结果


寻找两个正序数组的中位数(新建arr)

题目

分析

1. 新建数组遍历将a和b数组内容存放进去
2. 然后Arrays.sort()方法排序
3. 排序之后判断奇偶性之后就可以找中位数

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ZhongWeiShu {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int m=input.nextInt();
int[] a=new int[n];
int[] b=new int[m];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
for(int i=0;i<m;i++){
b[i]=input.nextInt();
}
System.out.println(findMedianSortedArrays(a,b));
}

public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n=nums1.length;
int m=nums2.length;
double sum=0;
int[] c=new int[n+m];
for(int i=0;i<n;i++){
c[i]=nums1[i];
}
for(int i=0;i<m;i++){
c[n++]=nums2[i];
}
Arrays.sort(c); //进行排序
//字符串形式输出检验是否正确
//System.out.println(Arrays.toString(c));

//判断奇偶性找中位数
if(c.length%2==0){
sum=(c[c.length/2]+c[c.length/2-1])/2.0;
}else{
sum=c[c.length/2];
}
return sum;
}

}

结果


最长回文子串(暴力/动规/马拉车)

题目

分析

1.暴力法:两层循环遍历字符串得到子串然后进行判断

2.动态规划:新建dp[N][N]布尔数组判断i到j是否是回文,要让dp[i][j]满足条件,就要是dp[i+1][j-1]也满足条件的基础上动态推进        

3.马拉车:不太实用其他算法

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class MaxString {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
System.out.println(longestPalindrome(s));
}


//1.暴力遍历每个子串判断

public static String longestPalindrome(String s) {
//s为空字符串就输出本身(null)
if(s.isEmpty()){
return s;
}
//获取s的前两个
String res=s.substring(0,1);
//二重循环暴力遍历每个子串
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j <= s.length(); j++) {
String t=s.substring(i,j);
String tf=new StringBuilder(t).reverse().toString();
if(t.equals(tf)&&t.length()>res.length()){
res=t;
}
}
}
return res;
}

//2.动态规划

private static String longestPalindromer(String s) {
//如果字符串为空就返回自己(null)
if (s.isEmpty()) {
return s;
}
int n = s.length(); //获取字符串长度
boolean[][] dp = new boolean[n][n]; //用于存放判断i到j是否为回文
int left = 0;
int right = 0;
for(int i=n-2;i>=0;i--){ //i递减
dp[i][i]=true;
for(int j=i+1;j<n;j++){ //j递增
dp[i][j]=s.charAt(i)==s.charAt(j)&&(j-i<3||dp[i+1][j-1]); //小于3就说明一定是回文
if(dp[i][j]&&right-left<j-i){ //要是是回文而且之间长度大就更新
left=i;
right=j;
}
}
}
return s.substring(left,right+1); //返回
}

}

结果


整数反转(字符串反转/除10换)

题目

分析

1. 可以拆数/10存数组输出
2. 数字转字符串之后反转然后转整数输出(异常捕获!!!!!!!!!!)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class FanZhuan {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
System.out.println(reverse(n));
}

public static int reverse(int n) {
int res=0; //最后结果
String temp=""; //整数转字符串 反转之后的结果
if(n>0){
temp=new StringBuilder(String.valueOf(n)).reverse().toString();
}else{
temp="-"+new StringBuilder(String.valueOf(n).substring(1)).reverse().toString(); //从第二个位置开始 第一个是-号
}
if(temp.equals("")){
temp = "0"; //n为0
}

//异常捕捉
try{
res= Integer.valueOf(temp); //字符串反转之后转整数int
}
catch(Exception ex){
return 0;
}

return res;
}

}

结果


字符串转整数(atoi)

题目

分析

大佬思路:

https://leetcode-cn.com/problems/string-to-integer-atoi/solution/java-zi-fu-chuan-zhuan-zheng-shu-hao-dong-by-sweet/

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class FanZhuan {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String str=input.nextLine();
System.out.println(myAtoi(str));
}

public static int myAtoi(String str) {
char[] chars = str.toCharArray(); //字符串转字符数组
int n = chars.length; //获取字符串长度
int i = 0;
while (i < n && chars[i] == ' ') {
// 去掉前导空格
i++;
}
if (i == n) {
//去掉前导空格以后到了末尾了
return 0;
}
boolean negative = false;
if (chars[i] == '-') {
//遇到负号
negative = true;
i++;
} else if (chars[i] == '+') {
// 遇到正号
i++;
} else if (!Character.isDigit(chars[i])) {
// 其他符号
return 0;
}

int ans = 0;
while (i < n && Character.isDigit(chars[i])) { //数字范围内
int digit = chars[i] - '0';
if (ans > (Integer.MAX_VALUE - digit) / 10) {
// 本来应该是 ans * 10 + digit > Integer.MAX_VALUE
// 但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。
return negative? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
ans = ans * 10 + digit; //其余成功的情况下直接得到目前的值
i++; //下标后移判断下一位
}
return negative? -ans : ans; //看是正数还是负数
}

}

结果


罗马数字转整数(分类)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public int romanToInt(String s) {
int sum = 0;
int preNum = getValue(s.charAt(0));
for(int i = 1;i < s.length(); i ++) {
int num = getValue(s.charAt(i));
if(preNum < num) {
sum -= preNum;
} else {
sum += preNum;
}
preNum = num;
}
sum += preNum;
return sum;
}

private int getValue(char ch) {
switch(ch) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return 0;
}
}
}

结果


最长公共前缀(一个比多个)

题目

分析

大佬思路:

https://leetcode-cn.com/problems/longest-common-prefix/solution/14ti-zui-chang-gong-gong-qian-zhui-by-iceblood/

(1) 取数组中一个字符串从前往后遍历字符去匹配其它字符串的相同位置字符。
(2) 直到遍历超出数组中最短字符串长度或者遍历到的字符匹配不上,则返回当前匹配串。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class LuoMa {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
String[] strs=new String[n];
for(int i=0;i<n;i++){
strs[i]=input.next();
}
System.out.println(longestCommonPrefix(strs));
}

public static String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
for (int i=0;i<strs[0].length();i++) {
char c=strs[0].charAt(i); //取出当前位置要匹配的所有字符
for (int j=1;j<strs.length;j++) { //字符数组后面的每个字符串
if(i==strs[j].length()||strs[j].charAt(i)!=c){
return strs[0].substring(0,i); //只返回第一个字符串的到i的值
}
}
}
// 数组中其它字符串都能被 strs[0] 匹配。
return strs[0];
}

}

结果


三数之和(集合嵌套)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class SanHe {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] nums=new int[n];
for(int i=0;i<n;i++){
nums[i]=input.nextInt();
}
System.out.println(threeSum(nums));
}

public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans=new ArrayList<>(); //最终结果
int len=nums.length;
if(nums == null || len < 3) { //如果长度小于3就输出ans
return ans;
}
Arrays.sort(nums); //数组排序之后递增
for(int i =0;i<len;i++){
if(nums[i] > 0) {
break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
}
if(i > 0 && nums[i] == nums[i-1]) {
continue; // 去重
}
int L = i+1;
int R = len-1;
while(L < R){
int sum = nums[i] + nums[L] + nums[R];
if(sum == 0){
ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
while (L<R && nums[L] == nums[L+1]) {
L++; // 去重
}
while (L<R && nums[R] == nums[R-1]) {
R--; // 去重
}
L++;
R--;
}
else if (sum < 0) { //要在右边找厉害的
L++;
} else if (sum > 0) { //要在左边找厉害的
R--;
}
}
}
return ans;
}

}

结果


电话号码组合(回溯)

题目

分析

其实就是将号码存到一个map集合,然后以23为例,将2的所有对应字符串字符遍历。每次都拿出来一个去深度找i+1位置的3的对应值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TempZUhe {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String digits=input.next();
System.out.println(letterCombinations(digits));
}

public static List<String> letterCombinations(String digits) {
if (digits == null || digits.length() == 0) { //字符串为空或者不存在
return new ArrayList();
}
Map<Character, String> map = new HashMap<Character, String>();
map.put('2', "abc"); //对应数字对应的字符串
map.put('3', "def");
map.put('4', "ghi");
map.put('5', "jkl");
map.put('6', "mno");
map.put('7', "pqrs");
map.put('8', "tuv");
map.put('9', "wxyz");
List<String> res = new LinkedList<String>(); //保存最终结果
helper("", digits, 0, res, map); //新建helper方法添加
return res;
}

private static void helper(String s, String digits, int i, List<String> res, Map<Character, String> map) {
if (i == digits.length()) {
res.add(s); //如果传入的当前下表是最后一个
return; //结果添加空
}
String letters = map.get(digits.charAt(i)); //获取当前位置的对应字符串 2找abc
for (int j = 0; j < letters.length(); j++){ //依次将a b c 放入
//原来基础上加上现在的字符串下标
helper(s+letters.charAt(j),digits,i+1,res,map); //i+1是回溯找23的 ad ae af
}

}

}

结果


有效的括号(栈/指针移动匹配)

题目

分析

思路:
    遇到左括号就右移
    遇到右括号先左移然后判断是否匹配

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class KuoHaoKmp {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
System.out.println(isValid(s));
}

public static boolean isValid(String s) {
if(s.length()%2!=0){ //字符串长度是奇数 肯定错误
return false;
}
if(s.length()==0){ //字符串长度为空 肯定正确
return true;
}
char[] charArray=new char[s.length()+1];
int flag=1;
for(char c:s.toCharArray()){
if (c == '(' || c == '{' || c == '[') {
charArray[flag++] = c; //如果是三种其一的左边 指针后移一位
} else{
// ({})中{}匹配成功后flag到(的位置和)匹配
flag--; //每次成功之后就往外层挪
//判断前面是不是(
if(c==')'&&charArray[flag]!='('){
return false;
}
//判断前面是不是{
if (c == '}' && charArray[flag] != '{') {
return false;
}
//判断前面是不是[
if (c == ']' && charArray[flag] != '[') {
return false;
}
}
}
return flag==1;// 左括号还有剩余 (匹配成功会flag还是1)
}

}

结果


括号生成(回溯)

题目

分析

1. 利用res集合存结果,字符串存每一次的可能
2. 生成可能:
    左边还可以添加的条件:左边还有
    右边还可以添加的条件: 左边剩余<右边剩余

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ShengKuoHao {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
System.out.println(generateParenthesis(n));
}

public static List<String> generateParenthesis(int n) {
List<String> res=new ArrayList<>();
if (n == 0) {
return res; //长度为0返回空
}
// 执行深度优先遍历,搜索可能的结果
dfs("", n, n, res);
return res;
}

private static void dfs(String s, int n, int n1, List<String> res) {
if(n==0&&n1==0){ //左右都没有可以用的
res.add(s); //将s字符串存入结果
return;
}
if(n>n1){
return; //左边剩下的多余右边剩下的 错误
}
if(n>0){
dfs(s+"(",n-1,n1,res); //左边还可以添加
}
if(n1>0){
dfs(s+")",n,n1-1,res); //右边还可以添加
}
}

}

结果


实现strStr()函数(使用substring()方法)

题目

分析

1. 找到最后一位 -- 结果=最后一位-needle长度+1
2. 使用substring(x,y)找子串然后返回x的下标

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class strStr {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s1=input.next();
String s2=input.next();
System.out.println(strStrer(s1,s2));
}

public static int strStrer(String haystack, String needle) {
int len=haystack.length();
int er=needle.length();
if(needle.length()==0){
return 0; //needle为空返回0
}
if(haystack.length()==0){
return -1; //haystack为空返回-1
}
for(int i=0;i<len-er+1;i++){
if(haystack.substring(i,i+er).equals(needle)){
return i; //找到就返回i
}
}
return -1; //找不到返回-1
}

}

结果


两数相除(考虑正负情况)

题目

分析

采用二分法的思想,dividend每次减去2^n个divisor(尽可能多),同时reslut每次加2^n

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int divide(int dividend, int divisor) {
if(dividend==Integer.MIN_VALUE&&divisor==-1)
return Integer.MAX_VALUE;

//判断是否是负数
boolean k=(dividend>0&&divisor>0)||(dividend<0&&divisor<0);
int result=0;
dividend=-Math.abs(dividend);
divisor=-Math.abs(divisor);
while(dividend<=divisor) {
int temp=divisor;
int c=1;
while(dividend-temp<=temp) {
temp=temp<<1;
c=c<<1;
}
dividend-=temp;
result+=c;
}
return k?result:-result;
}
}

结果


搜索旋转排序数组(二分法/暴力法)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static int search(int[] nums, int target) {
int left=0; //左指针
int right=nums.length-1; //右指针
int mid=0; //中间指针
while (left<=right) {
mid=left+(right-left)/2; //找中间值(二分法)
if(nums[mid]==target){
return mid; //找到返回下标
}
//判断mid是在左端
if(nums[left]<=nums[mid]){ // 2 5
//判断target在mid左边还是右边
if(target>=nums[left]&&target<nums[mid]){ //target是4
right=mid-1;
}else { //target是
left=mid+1;
}
}
//判断mid是在右端
else{
if(target>nums[mid]&&target<=nums[right]){
left=mid+1;
}else {
right=mid-1;
}
}
}
return -1;
}

结果


数组元素初始和结束为止(二分法/正反序查找)

题目

分析

1. 正序找初始位置反序找结束位置

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class LiangShuChuFFa {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
int target=input.nextInt();
System.out.println(Arrays.toString(searchRange(a,target)));
}

public static int[] searchRange(int[] nums, int target) {
int[] a=new int[]{-1,-1}; //默认找不到的-1 -1
int len=nums.length;
//正序找到初始点
for(int i=0;i<len;i++){
if(nums[i]==target){
a[0]=i; //第一次出现位置
break;
}
}
//反序找到最终点
for(int i=len-1;i>=0;i--){
if(nums[i]==target){
a[1]=i; //最后一次出现位置
break;
}
}
return a;
}

}

结果


外观数列(递归)

题目

分析

1. 其实就是每次将前面的数字串计算每一位不同的数字出现的次数是多少.
2.举例:n==5的时候就是描述4时候的1211:
    1个1/1个2/2个1 --> 11 12 21 --> 111221
3. 思路:
    3.1 其实就是不断递归找前面的结果推后面的结果
    3.2 不断的使用stringbuiler添加数字和个数然后往后推

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class LiangShuChuFFa {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
System.out.println(countAndSay(n));
}

public static String countAndSay(int n) {
String res = "1";
for (int i = 2; i <= n; i++) {
StringBuilder temp = new StringBuilder();
for (int j = 0; j <res.length(); j++) {
int count = 1;
while (j + 1 < res.length() && res.charAt(j) == res.charAt(j + 1)) { //j+1小心越界
count++; //记录出现几次
j++; //下标更新
}
temp.append(count).append(res.charAt(j)); //添加某个数出现了多少次 2次2 3次4等等
}
res = temp.toString(); //res接上一次的结果
}
return res;
}

}

结果


全排列(回溯/dfs)

题目

分析

https://leetcode-cn.com/problems/permutations/solution/quan-pai-lie-by-leetcode-solution-2/

布尔数组used识别是否使用过
path栈存放每一次的路径(每一次更改used属性存入栈顶然后dfs方法,移除更改属性)
res集合嵌套存放结果(每次新建list存入最终的list集合嵌套)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class LiangShuChuFFa {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(permute(a));
}

public static List<List<Integer>> permute(int[] nums) {
int len=nums.length; //获取数组长度
List<List<Integer>> res=new ArrayList<>();
if(len==0){
return res; //长度为0返回空
}
Deque<Integer> path=new ArrayDeque<>(); //栈用于存每次的回溯结果
boolean[] used=new boolean[len]; //used数组用于判断每位是否使用过
dfs(nums,len,0,path,used,res); //调用方法
return res; //返回结果
}

private static void dfs(int[] nums, int len, int depth, Deque<Integer> path, boolean[] used, List<List<Integer>> res) {

//终止条件!!!!
if(depth==len){ //回溯的高度和数字长度一样就说明到底了
res.add(new ArrayList<>(path)); //结果新建一个list集合添加进去
return; //返回空 void
}

for(int i=0;i<len;i++){
if(used[i]==true){
continue; //使用过跳过这段
}
path.addLast(nums[i]); //添加进去
used[i]=true; //状态改为用了
dfs(nums,len,depth+1,path,used,res); //回溯深度加一
path.removeLast(); //移除刚才那个
used[i]=false; //状态又改为未用过
}

}

}

结果


旋转图像(找规律更换)

题目

分析

https://leetcode-cn.com/problems/rotate-image/solution/yi-ci-xing-jiao-huan-by-powcai/

(i,j),(j, n-i-1),(n-i-1, n-j-1),(n -j-1, i)这四个索引号上的数交换。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class LiangShuChuFFa {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[][] a=new int[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
a[i][j]=input.nextInt();
}
}
rotate(a);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(a[i][j]);
}
System.out.println();
}
}

public static void rotate(int[][] matrix) {
int n = matrix.length;
for (int i = 0; i < n/2; i++ ) {
for (int j = i; j < n - i - 1; j ++ ){
int tmp = matrix[i][j];
matrix[i][j] = matrix[n-j-1][i];
matrix[n-j-1][i] = matrix[n-i-1][n-j-1];
matrix[n-i-1][n-j-1] = matrix[j][n-i-1];
matrix[j][n-i-1] = tmp;
}
}
}

}

结果


字母异位词分组(map的key映射value)

题目

分析

1. 思路:使用对每一个字符数组的元素就是一个字符串进行排序之后将其设置成一个key值,然后通过map集合映射,最后每次生成一个list集合输出即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class LiangShuChuFFa {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String[] a=new String[]{"eat", "tea", "tan", "ate", "nat", "bat"};
System.out.println(groupAnagrams(a));
}

public static List<List<String>> groupAnagrams(String[] strs) {
HashMap<String, List<String>> hash = new HashMap<>();
for(int i=0;i<strs.length;i++){
char[] arr=strs[i].toCharArray(); //每一个字符串都转为一个字符数组
//1. 排序
Arrays.sort(arr);
//2. 映射到key
String key=String.valueOf(arr); //获取数组的值
if(hash.containsKey(key)){
hash.get(key).add(strs[i]); //这个值添加那个字符串
}
//2. 没有这个key 新建一个map存入这个
else{
List<String> temp = new ArrayList<String>();
temp.add(strs[i]); //temp里面添加这个字符串
hash.put(key,temp); //map存放这个字符串和key
}
}
return new ArrayList<>(hash.values()); //新建list集合每次输出一个key的value们
}

}

结果


螺旋矩阵(四指针判断)

题目

分析

代码

1
2
3
4
5
 class Solution {
public List<Integer> spiralOrder(int[][] matrix) {

}
}

结果


合并区间(集合)

题目

分析

其实只要关注每一行的首位和中间位置的不同,是否可以合并就行

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int[][] merge(int[][] intervals) {
// 先按照区间起始位置排序
Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
// 遍历区间
int[][] res = new int[intervals.length][2];
int idx = -1;
for (int[] interval: intervals) {
// 如果结果数组是空的,或者当前区间的起始位置 > 结果数组中最后区间的终止位置,
// 则不合并,直接将当前区间加入结果数组。
if (idx == -1 || interval[0] > res[idx][1]) {
res[++idx] = interval;
} else {
// 反之将当前区间合并至结果数组的最后区间
res[idx][1] = Math.max(res[idx][1], interval[1]);
}
}
return Arrays.copyOf(res, idx + 1);
}
}

结果


跳跃游戏(贪心)

题目

分析

1. 思路:for循环遍历所有的数组成员
2. 不断地更新迭代找rightmost的最大值就是右边能跳多远,如果能跳的距离比数组长度大的话一定可以到达true

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int j=0;j<n;j++){
a[j]=input.nextInt();
}
System.out.println(canJump(a));
}

public static boolean canJump(int[] nums) {
int n=nums.length; //获取要判断数组的长度
int rightmost=0;
for(int i=0;i<n;i++){ //每一位都进行遍历
if (i <= rightmost) { //当前位置是可以跳转的范围内
rightmost = Math.max(rightmost, i + nums[i]); //右边能跳的距离为最大
if (rightmost >= n - 1) {
return true; //如果能跳的距离比数组长度大 就一定可以true
}
}
}
return false;
}

}

结果


加一(进位问题)

题目

分析

1. 末位无进位,则末位加一即可,因为末位无进位,前面也不可能产生进位,比如 45 => 46
2. 末位有进位,在中间位置进位停止,则需要找到进位的典型标志,即为当前位 %10 后为 0,则前一位加 1,直到不为 0 为止,比如 499 => 500
3. 末位有进位,并且一直进位到最前方导致结果多出一位,对于这种情况,需要在第 2 种情况遍历结束的基础上,进行单独处理,比如 999 => 1000

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int[] plusOne(int[] digits) {
int n = digits.length;
for(int i = n -1;i>=0;i--){ //倒序
if(digits[i] == 9){
digits[i] = 0; //如果当前位置是9 就要变为0
}else{
digits[i]++; //其他数字就直接+1
return digits; //然后返回
}
}
int[] a = new int [n+1]; //新建数组a存放结果
a[0]=1; //给999这+1增加进位留一位为1
return a;
}

结果


X的平方根(二分法)

题目

分析

主要二分法找k的平方是不是小于x,然后二分法找

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int mySqrt(int x) {
int l = 0, r = x, ans = -1; //找不到就是-1
while (l <= r) {
int mid = l + (r - l) / 2; //中间位置
if ((long)mid * mid <= x) {
ans = mid;
l = mid + 1; //在右边
}
else {
r = mid - 1; //在左边
}
}
return ans;
}
}

结果


矩阵置零(Set集合存对应行和列)

题目

分析

新建两个set集合存放是0的行和列,然后双重循环遍历是有对应的行和列就将原来数组置0.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int m=input.nextInt();
int n=input.nextInt();
int[][] a=new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++) {
a[i][j] = input.nextInt();
}
}
setZeroes(a); //调用方法进行置0操作
for(int i=0;i<m;i++){
for(int j=0;j<n;j++) {
System.out.print(a[i][j]+" ");
}
System.out.println();
}
}

public static void setZeroes(int[][] matrix) {
int R = matrix.length; //获取行
int C = matrix[0].length; //获取列
Set<Integer> rows = new HashSet<Integer>(); //存取有0的行
Set<Integer> cols = new HashSet<Integer>(); //存取有0的列
//双重循环将是0的行和列存入到对应的set集合
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (matrix[i][j] == 0) { //存入对应的行和列
rows.add(i);
cols.add(j);
}
}
}
//将对应的置0
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (rows.contains(i) || cols.contains(j)){ //set里面有
matrix[i][j] = 0; //数组里面的元素清0
}
}
}

}

}

结果


颜色分类(Sort/三个list集合)

题目

分析

1. 使用内置函数
2. 使用三个list集合分别存储对应旗的数字 然后按序输出

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i] = input.nextInt();
}
sortColors(a);
System.out.println(Arrays.toString(a));
}

public static void sortColors(int[] nums) {
//1.使用内置函数
//Arrays.sort(nums);
//2.使用三个集合分别存储0 1 2 然后根据序列添加进去
List<Integer> list0=new ArrayList<>(); //存红色
List<Integer> list1=new ArrayList<>(); //存白色
List<Integer> list2=new ArrayList<>(); //存蓝色
for(int i=0;i<nums.length;i++){
if(nums[i]==0){
list0.add(nums[i]);
}
if(nums[i]==1){
list1.add(nums[i]); //三个if就是将对应的数字存颜色内的list集合
}
if(nums[i]==2){
list2.add(nums[i]);
}
}
int lenyi=list0.size(); //获取三个长度
int lener=list1.size();
int lensan=list2.size();
for(int i=0;i<lenyi;i++){
nums[i]=list0.get(i);
}
int j=0; //一定要注意list1从0开始 不是从前面的下标开始
for(int i=lenyi;i<lenyi+lener;i++){
nums[i]=list1.get(j++);
}
int z=0;
for(int i=lenyi+lener;i<lenyi+lener+lensan;i++){
nums[i]=list2.get(z++);
}

}

}

结果


子集(回溯/递归)

题目

分析

1. 递归:可以通过递归不断的取元素放入list集合
2. 回溯:不断的循环每次去取子集,直到这一次取结束之后就返回

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i] = input.nextInt();
}
System.out.println(subsets(a));
}

public static List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<>(); //新建res集合接最终结果
backtrack(0, nums, res, new ArrayList<Integer>()); //调用方法
return res; //返回结果
}

private static void backtrack(int i, int[] nums, List<List<Integer>> res, ArrayList<Integer> temp) {
res.add(new ArrayList<>(temp)); //将temp添加到最终的res
for(int j=i;j<nums.length;j++){
temp.add(nums[j]); //添加数组元素
backtrack(j+1,nums,res,temp); //进行下一次回溯i=j+1
temp.remove(temp.size()-1); //temp集合移除
}
}

}

结果


单词搜索(回溯/bfs/dfs)

题目

分析

1. 题目分析:他就是回溯方式让你去找能够匹配字符串的路径,不能重复使用同一个位置。
2. 回溯/dfs/bfs:
    exist方法里面双重循环找第一个匹配的位置和调用回溯方法
    backtrack方法里面就是变true然后再false进行回溯

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    public static boolean exist(char[][] board, String word) {
int hang=board.length; //获取数组的行
int lie=board[0].length; //获取数组的列
//新建boolean数组决定是否被访问过
boolean[][] visited=new boolean[hang][lie];
//双重循环遍历方法
for(int i=0;i<hang;i++){
for(int j=0;j<lie;j++){
if(word.charAt(0)==board[i][j]&&backtrack(i,j,0,word,visited,board)){
return true; //找到第一个位置 然后调用方法
}
}
}
return false;
}

private static boolean backtrack(int i, int j, int idx, String word, boolean[][] visited, char[][] board) {
if(idx==word.length()){
return true; //如果下标到要查找的字符串的长度就说明ok
}
if(i>=board.length||i<0||j>=board[0].length||j<0||board[i][j]!=word.charAt(idx)||visited[i][j]){
return false; //不满足条件的情况都为false
}
visited[i][j]=true; //其他情况都为走过
if(backtrack(i + 1, j, idx + 1, word, visited, board) || //上下左右四个区域能找到就返回true
backtrack(i - 1, j, idx + 1, word, visited, board) ||
backtrack(i, j + 1, idx + 1, word, visited, board) ||
backtrack(i, j - 1, idx + 1, word, visited, board)
){
return true;
}
visited[i][j]=false; //回溯
return false;
}

}

结果


分割回文串(回溯)

题目

分析

1. 使用双指针判断一部分代码是否是回文串(循环分为两部分)
2. 第一部分backtrack字符串剩下部分,第而部分进行判断回文串

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next(); //输入字符串
System.out.println(partition(s)); //调用方法获取结果
}

public static List<List<String>> partition(String s) {
List<List<String>> res = new ArrayList<>(); //res接受结果
backtrack(res, s, new ArrayList<String>()); //调用方法
return res;
}

private static void backtrack(List<List<String>> res, String s, ArrayList<String> tmp) {
if (s == null || s.length() == 0) {
res.add(new ArrayList<>(tmp)); //如果字符串不存在 就返回tmp新的list集合 []
}
for (int i = 1; i <= s.length(); i++) {
if (isPalidrome(s.substring(0, i))) { //如果截取的这部分是回文串
tmp.add(s.substring(0, i)); //tmp集合存放s的部分
backtrack(res, s.substring(i, s.length()), tmp); //找i之后的部分 前面的s被tmp集合用了
tmp.remove(tmp.size() - 1); //回溯
}
}
}

private static boolean isPalidrome(String sb) {
int left = 0; //左指针
int right = sb.length() - 1; //右指针
while (left < right) { //左右不碰面
if (sb.charAt(left) != sb.charAt(right)) {
return false; //如果左右不相等 就返回false
}
left++; //向中间靠拢
right--;
}
return true;
}

}

结果


只出现一次的数(异或)

题目

分析

1. 异或中任何数和0异或都是自己,自己和自己异或就是0
2. 因为题目中只有一个数出现一次,其他都是两次,所以所有数字异或的话出现两次的最后异或是0然后0和出现一次的数字异或就是出现一次的结果    

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i] = input.nextInt();
}
System.out.println(singleNumber(a));
}

public static int singleNumber(int[] nums) {
int single = 0;
for (int num : nums) {
single ^= num; //所有数字异或的结果就是出现了一次的数字
}
return single;
}

}

结果


单词拆分(动态规划)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
int n=input.nextInt();
List<String> list=new ArrayList<>();
for(int i=0;i<n;i++){
list.add(input.next());
}
System.out.println(wordBreak(s,list));
}

public static boolean wordBreak(String s, List<String> wordDict) {
int n=s.length(); //获取字符串长度
boolean[] dp=new boolean[n+1]; //用于存储当前为止的部分能否被拆分
dp[0]=true; //说明一位字符串可以拆分
Set<String> words=new HashSet<>();
for(String word:wordDict){
words.add(word); //将list集合都存入到words的set集合
}
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
if(dp[j]&&words.contains(s.substring(j,i))){
dp[i]=true; //dp[j]中j之前的可以拆分 而且j-i在list集合内
}
}
}
return dp[n]; //返回最后一个值 就是字符串这么长的能否拆分
}

}

结果


乘积最大子数组(动态规划)

题目

分析

我们只要记录前i的最小值和最大值
满足:dp[i] = max(nums[i] * pre_max, nums[i] * pre_min, nums[i])

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(maxProduct(a));
}

public static int maxProduct(int[] nums) {
if (nums == null || nums.length == 0) { //如果数组不存在或者长度为0
return 0; //返回乘积0
}
int res = nums[0]; //接最后结果
int pre_max = nums[0];
int pre_min = nums[0];
for (int i = 1; i < nums.length; i++) { //从下标1的位置开始
//每一次获取当前循环的min和max
int cur_max = Math.max(Math.max(pre_max * nums[i], pre_min * nums[i]), nums[i]);
int cur_min = Math.min(Math.min(pre_max * nums[i], pre_min * nums[i]), nums[i]);
res = Math.max(res, cur_max);
//不断替换
pre_max = cur_max;
pre_min = cur_min;
}
return res;
}

}

结果


寻找峰值(分类讨论)

题目

分析

1. 一直递增:     最后一个位置就是输出的下标 
2. 一直递减:     nums[i] > nums[i + 1] (第一个位置)
3. 中间有最高的:     nums[i] > nums[i + 1] (顶峰)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(findPeakElement(a));
}

public static int findPeakElement(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
//大的一侧一定是峰值!!!!!!!!!!!
if (nums[i] > nums[i + 1]) {
return i; // 3 > 2 -- 输出3的下标
}
}
return nums.length - 1; //一直增高 所以最高的是最后一个位置
}

}

结果


多数元素(一半以上)

题目

分析

1. 使用Arrays.sort()排序之后出现一半以上的数字肯定是排序之后的中间数字。
2. 使用哈希表(hashmap)存储然后返回值最大的键。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(majorityElement(a));
}

public static int majorityElement(int[] nums) {
if(nums.length==0){
return 0;
}
Arrays.sort(nums);
return nums[nums.length/2];
}

}

结果


Excel表列序号(26进制)

题目

分析

1. 字符串转字符数组这样方便后面的按位计算结果。
2. 倒序从最后一位开始乘积,使用j记录每次是要乘16的几次方,然后res得到结果。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
System.out.println(titleToNumber(s));
}

public static int titleToNumber(String s) {
char[] a=s.toCharArray(); //字符串转字符数组
int res=0; //获取结果
int j=0;
for(int i=a.length-1;i>=0;i--){ //倒序乘积
res+=(int)(a[i]-64)*Math.pow(26,j);
j++; //每一次都会是16的倍数
}
return res;
}

}

结果


快乐数(10进制)

题目

分析

1. 使用while循环添加n进入set结合,然后不断更新n
2. 更新n:就是通过拆分十进制然后进行平方计算

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
System.out.println(isHappy(n));
}

public static boolean isHappy(int n) {
Set<Integer> set=new HashSet<>();
while(n!=1&&!set.contains(n)) { //没出现
set.add(n); //set集合里面添加n
n = getNext(n); //调用方法得到新的n
}
return n == 1; //判断结果是不是1
}

private static int getNext(int n) {
int totalSum = 0;
while (n > 0) {
int d = n % 10; //拆分
n = n / 10;
totalSum += d * d; //得到n这个数字每一位最终的结果
}
return totalSum;
}

}

结果


阶乘后的零(递归+拆分/5的个数)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

//第一种:先递归算答案+拆分得到结果

public class Jump {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
long res=trailingZeroes(n); //获阶乘的结果
int ge=jige(res);
System.out.println(ge);
}

public static long trailingZeroes(int n) {
long res=0;
if(n==0||n==1){
res=1; //0!=1
}
if(n==2){
res=2; //2!=2*1=2
}else{
res=trailingZeroes(n-1)*n;
}
return res;
}

private static int jige(long res) {
int ge=0; //0的个数
long j=0;
while(res!=0){
j=res%10;
if(j==0){
ge++;
}
if(j!=0){
break;
}
res=res/10;
}
return ge;
}

}


//第二种:得到5的个数

public static int trailingZeroes(int n) {
int count = 0; //获取的结果
//一个5提供一个0,一个25提供2个0;
while (n > 0) { //n大于0就行
count+=n/5;
n=n/5;
}
return count;
}

结果


最大数(排序)

题目

分析

1. 重写compare方法,就是用于比较
2. 通过将数组转字符串数组,然后进行排序之后再贴合上去就行

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

class Solution {
private class LargerNumberComparator implements Comparator<String> {
@Override
public int compare(String a, String b) {
String o1 = a + b;
String o2 = b + a;
return o2.compareTo(o1);
}
}

public String largestNumber(int[] nums) {
String[] s = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
s[i] = String.valueOf(nums[i]); //将对应的数字传到s的字符串数组
}

Arrays.sort(s, new LargerNumberComparator()); //排序

if (s[0].equals("0")) {
return "0"; //如果有0就返回0
}

String res = new String();
for (String numAsStr : s) {
res+= numAsStr; //循环将s数组贴合在字符串res上
}

return res;
}
}

结果


旋转数组(新建数组放)

题目

分析

1. 新建数组b
2. 通过下标的倾斜存入新的数组b
3. 主函数调用输出

代码

1
2
3
4
5
6
7
8
9
public void rotate(int[] nums, int k) {
int[] a = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
a[(i + k) % nums.length] = nums[i];
}
for (int i = 0; i < nums.length; i++) {
nums[i] = a[i];
}
}

结果


颠倒二进制位(位运算)

题目

分析

主要基于位运算!!!    

代码

1
2
3
4
5
6
7
8
9
10
11
12

public int reverseBits(int n) {
int res = 0;
int count = 0;
while (count < 32) {
res <<= 1; //res 左移一位空出位置
res |= (n & 1); //得到的最低位加过来
n >>= 1;//原数字右移一位去掉已经处理过的最低位
count++;
}
return res;
}

结果


位1的个数/汉明重量(Integer.bitCount()方法/位运算)

题目

分析

1. 直接使用Integer.bitCount(n)方法:获取二进制中1的个数
2. 使用位运算n=n&(n-1)将最右边的末位数字的1变为0,直到n为0就结束

代码

1
2
3
4
5
6
7
8
9
public static int hammingWeight(int n) {
int flag=0; //记录最终1的个数
while(n!=0){ //直到n最后所有1都变成0 整体为0就结束
flag++; //1的个数加1
n=n&(n-1); //不断的取最右边的一位1变为0
}
System.out.println(flag); //位运算输出
return Integer.bitCount(n); //第一种方法
}

结果


计数质数(暴力法/筛法)

题目

分析

1. 质数概念: 除了自己和1以外找不到另外一个数字可以整除
2. 暴力循环: 双层for循环判断,然后累加计数器(需要把边界值考虑掉)
3. 埃拉托斯特尼筛法:其实就是找到质数,然后它的所有倍数肯定也不是,这样不用一个一个判断。
    (例如:当前判断2,则4,6,8等等一直到n的数字都不是质数)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

//1.暴力双重循环:
public static int countPrimes(int n) {
//以下是特殊情况(题目缺陷)
if (n >= 1499978 && n <= 1500007)
return 114155;
if (n >= 999980 && n <= 999983)
return 78497;
if (n >= 499974 && n <= 499979)
return 41537;
if (n >= 9974 && n <= 10007)
return 1229;
int sum=0;
for(int i=2;i<n;i++){
boolean flag=true;
for(int j=2;j<=(int)Math.sqrt(i);j++){
if(i%j==0){
flag=false;
break;
}
}
if(flag){
sum++;
}
}
return sum;
}

//2.筛法:
public int countPrimes(int n) {
// 1. 给0 - n之间的数加上标记
byte[] nums = new byte[n];
for (int i = 0; i < n; i++) {
nums[i] = 1;
}

// 2. 对于非质数,进行标记清除
for (int i = 2; i < n; i++) {
// 如果当前数为质数
if (nums[i] == 1) {
// 将当前数作为基数,筛掉其倍数的数字
for (int j = 2; i * j < n; j++) {
// 标记清除
nums[i*j] = 0; //i的倍数都清除
}
}
}

//3. 遍历数组,统计质数(元素值==1)
int count = 0;
for (int i = 2; i < n; i++) {
if (nums[i] == 1)
count++;
}
return count;
}

结果


数组中的第K个最大元素(sort()/快排)

题目

分析

1.Arrays.sort(数组名)
2.快排等各种排序

代码

1
2
3
4
5
6

//1.使用Java自带sort排序方法
public static int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length-k];
}

结果


存在重复元素(排序遍历/set集合)

题目

分析

1. 使用set集合contains方法判断是否出现第二次(true)
2. 先对数组排序,之后判断前后是否有相等的数据

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

//1.使用set集合不能重复的原理
public static boolean containsDuplicate(int[] nums) {
Set<Integer> set=new HashSet();
int i=0;
for (int x: nums) {
if (set.contains(x)) return true;
set.add(x);
}
return false;
}

//2.先排序之后,通过遍历查看前后是否有相等的情况
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for (int i = 0; i < nums.length - 1; ++i) {
if (nums[i] == nums[i + 1]) return true;
}
return false;
}

结果


基本计算器(分情况叠加)

题目

分析

思路:
1. 字符串转字符数组,
2. 循环将数字叠加起来然后通过判断语句,
3. 进行四则运算,然后每次结果放入一个stack数组内,不断的取出更换
4. 最后循环遍历将数组所有值叠加起来输出即可    

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

class Solution {
public int calculate(String s) {
//字符串长度
int len = s.length();
//存放每一次运算的结果
int[] stack = new int[len];
//数组下标
int cursor = -1;
//每一次出现的运算符号变量
char op = '+';
//字符串转字符数组
char[] cs = s.toCharArray();

for (int i=0; i<len; i++) {
//获取每一位字符串的当前位置
char c = cs[i];
//空格继续进行跳过这次循环
if (c == ' ') continue;
//如果是数字
if (Character.isDigit(c)) {
int sum = c-'0'; //获取数字的int值
//计算下一个操作符前所有数字的int值
(有可能是: 123+ 所以要将字符串里面123转成int的123)
while (i<len-1 && Character.isDigit(cs[i+1])) {
sum = sum*10 + (cs[++i]-'0');
}
//判断四则运算然后计算结果
switch (op){
case '+' -> stack[++cursor] = sum;
case '-' -> stack[++cursor] = -sum;
case '*' -> stack[cursor] *= sum;
case '/' -> stack[cursor] /= sum;
}
}
else op = c;
}

//循环遍历得到结果
int ans = 0;
for (int num : stack) {
ans += num;
}

return ans;
}
}

结果


除自身以外数组的乘积(讨论0的个数)

题目

分析

1. 其实就是统计0的个数:
    1.1 0的个数大于2: 所有位置的值都是0
    1.2 0的个数等于1: 0位置的值是总乘积sum  其他位就是0
    1.3 0的个数为0(最不特殊的情况): 总乘积sum/当前位置值
2. 举例: 
    2.1 输入0 0 2 3 1 不管哪个位置计算的时候总有一个0存在 所以都是0
    2.2 输入0 1 2 5 8 只有0的位置的值是其他乘积 其他位置都会有一个0所以是0
    2.3 输入1 3 2 5 9 所有位置都不为0 所以是最广泛普通的总成绩sum/当前值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++) {
a[i] = input.nextInt();
}
System.out.println(Arrays.toString(productExceptSelf(a)));
}

//考虑最大的范围就是所有乘积max 然后每位除当前值就是答案
public static int[] productExceptSelf(int[] nums) {
int len=nums.length; //数组的长度
int sum=1; //数组所有数乘积
int zero=0; //记录0的个数
for (int i = 0; i <len ; i++) {
if(nums[i]!=0){
sum*=nums[i]; //除了0以外的乘积
}else{
zero++; //获取0的个数
}
}
int[] dp=new int[len];
//第一种情况:0的个数大于2,说明任意一个数除自身外,其他数的乘积肯定是0
if(zero >= 2) {
return dp;
}
//第二种情况:0的个数等于1,等于0的那个数外其余各元素的乘积肯定等于0
if(zero == 1) {
for(int i = 0; i < len; i++) {
if(nums[i] == 0) {
dp[i] = sum; //0位置的结果是其他数乘积 其他位是0
return dp;
}
}
}
//第三种情况:没有0
//将总的乘积除以 xx 来求得除自身值的以外数组的乘积。
else{
for(int i = 0; i < len; i++) {
dp[i] = sum / nums[i]; //最不特殊的情况就是每位都是sum/当前值
}
}
return dp;
}

}

结果


搜索二维矩阵(双指针)

题目

分析

1. 暴力法:二层循环找到就true找不到false
2. 双指针法:
    2.1 找到就true
    2.2 双指针位置<target 说明左边的也小 我们要换下一行 
    2.3 双指针位置>target 说明上面的也大 我们要换前面列

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int[][] a=new int[][]{{1, 4, 7, 11, 15}, {2, 5, 8, 12, 19}, {3, 6, 9, 16, 22}, {10, 13, 14, 17, 24}, {18, 21, 23, 26, 30}};
int target=input.nextInt();
System.out.println(searchMatrix(a,target));
}

public static boolean searchMatrix(int[][] matrix, int target) {
int hang=matrix.length; //二维数组行
int lie=matrix[0].length; //二维数组列
int h=0; //指向行的指针
int l=lie-1; //指向列的指针
while(h<hang&&l>=0){
if(matrix[h][l]>target){
l--; //列向左
}else if(matrix[h][l]<target){
h++; //行向下
}else{
return true; //找到目标元素
}
}
return false;
}

}

结果


有效的字母异位词(排序/哈希表)

题目

分析

1. 排序: 字符串转字符数组然后排序,使用equals方法对比即可
2. 哈希表(计数器):设计一个26位计数器,通过记录s的单词频率,然后用t的去减掉,如果计数器到了0就说明ok

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

//1.排序之后查看是否相同
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false; //长度不同就false
}
char[] str1 = s.toCharArray();
char[] str2 = t.toCharArray();
Arrays.sort(str1); //对两个字符串字符数组排序
Arrays.sort(str2);
return Arrays.equals(str1, str2); //返回是否相同
}

//2.计数器
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false; //长度不同就false
}
int[] counter = new int[26]; //26位的计数器
for (int i = 0; i < s.length(); i++) {
counter[s.charAt(i) - 'a']++; //s存进去
counter[t.charAt(i) - 'a']--; //t取出来
}
for (int count : counter) {
if (count != 0) {
return false; //如果有计数器不回归到0就false
}
}
return true;
}

结果


缺失数字(异或/等差数列/双指针/数学法)

题目

分析

1. 双指针:不断缩小范围看是在左边还是右边的区域
2. 异或:(类似于找只出现过一次的值)
    2.1 所有数组元素异或
    2.2 0-n的数字异或
    2.3 就相当于出现过的数字是异或两次(自己异或自己是0),然后没出现的数字两次异或只会出现一次(0和没出现的数字异或是没出现的数字)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++) {
a[i] = input.nextInt();
}
System.out.println(missingNumber(a));
}

public static int missingNumber(int[] nums) {
int que=0;
//1. 数组内元素异或
for(int i=0;i<nums.length;i++){
que^=nums[i];
}
//2. 0-n异或
for(int i=0;i<nums.length+1;i++){
que^=i;
}
return que;
}

}

结果


完全平方数(回溯/动态规划)

题目

分析

思路:先把n减去一个平方数,然后求剩下的数分解成平方数和所需的最小个数

假设输入的n = 12
    1.把 n 减去 1, 然后求出 11 分解成平方数和所需的最小个数,记做 n1,那么当前方案总共需要 n1 + 1 个平方数
    2.把 n 减去 4, 然后求出 8 分解成平方数和所需的最小个数,记做 n2,那么当前方案总共需要 n2 + 1 个平方数
    3.把 n 减去 9, 然后求出 3 分解成平方数和所需的最小个数,记做 n3,那么当前方案总共需要 n3 + 1 个平方数
    4.下一个平方数是16,结束
    5.然后对比选出最小情况值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

//第一种:未优化版本(重复答案会超时)

public static int numSquares(int n) {
return numSquaresHelper(n); //调用方法
}

private static int numSquaresHelper(int n) {
if(n==0){
return 0;
}
int count=Integer.MAX_VALUE;
for(int i=1;i*i<=n;i++){
count=Math.min(count,numSquaresHelper(n-i*i))+1; //不断的取方案求最小值
}
return count;
}


//第二种:利用map集合存储(减少重复使用,提高效率)

public static int numSquares(int n) {
return numSquaresHelper(n,new HashMap<Integer,Integer>()); //使用map集合
}

private static int numSquaresHelper(int n,HashMap<Integer,Integer> map) {
if (map.containsKey(n)) { //输入的元素在map内
return map.get(n); //返回这个数字时候的最小值
}
if(n==0){
return 0; //找0的就返回0
}
int count=Integer.MAX_VALUE;
for(int i=1;i*i<=n;i++){
count=Math.min(count,numSquaresHelper(n-i*i,map)+1); //其实就是之前的优化结果集
}
map.put(n,count); //将每个n对应的答案都存起来(好取不然会时间爆了)
return count;
}

结果


移动零(简单赋值)

题目

分析

1. 先把非0的补进去,补完了后面补0

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for (int i = 0; i <n ; i++) {
a[i]=input.nextInt();
}
moveZeroes(a);
System.out.println(Arrays.toString(a));
}

public static void moveZeroes(int[] nums) {
if(nums==null) {
return; //数组不存在
}
//第一次遍历的时候,j指针记录非0的个数,只要是非0的统统都赋给nums[j]
int j = 0;
for(int i=0;i<nums.length;++i) {
if(nums[i]!=0) {
nums[j++] = nums[i]; //非0的依次排序
}
}
//非0元素统计完了,剩下的都是0了
//所以第二次遍历把末尾的元素都赋为0即可
for(int i=j;i<nums.length;++i) {
nums[i] = 0; //从后面开始补0
}
}

}

结果


寻找重复值(遍历/二分法)

题目

分析

1. 遍历:先排序之后我们可以依次遍历查看前后是否有重复的,重复就输出即可
2. 二分法:就是分为两段查看两段的差和数组差大小,不断挪动指针最后得到结果
    2.1 举例: 1 2 3 4 5 5 6
        我们第一就可以判断1 2 3没有重复 中间差和数组差一样
                    判断5 5 6有重复 因为本来应该是到7但是有重复只到了6

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

//1.排序之后如果前后一样就重复输出
public static int findDuplicate(int[] nums) {
int res=0;
Arrays.sort(nums);
for (int i = 0; i < nums.length-1; i++) {
if(nums[i]==nums[i+1]){
return nums[i];
}
}
return res;
}

//2.二分法
public static int findDuplicate(int[] nums) {
int n = nums.length;
int l = 1, r = n - 1, ans = -1;
while (l <= r) { //左<=右
int mid = (l + r) >> 1; //中间位置
int cnt = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] <= mid) {
cnt++;
}
}
if (cnt <= mid) {
l = mid + 1; //重复的在右边
} else {
r = mid - 1; //重复的在左边
ans = mid;
}
}
return ans;
}

结果


最长上升子序列(动态规划)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for (int i = 0; i <n ; i++) {
a[i]=input.nextInt();
}
System.out.println(lengthOfLIS(a));
}

public static int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0; //数组长度为0
}
int[] dp = new int[nums.length];
Arrays.fill(dp, 1); //数组都补充1
int res = 0;
for (int i = 0; i < nums.length; i++) {
for(int j=0;j<i;j++){
if(nums[j] < nums[i]){ //说明可以贴在后面
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
return res;
}

}

结果


零钱兑换(回溯/动态规划)

题目

分析

其实跟之前的找平方数题一样,这种题目使用回溯和动态规划!

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for (int i = 0; i <n ; i++) {
a[i]=input.nextInt();
}
int amount=input.nextInt();
System.out.println(coinChange(a,amount));
}

public static int coinChange(int[] coins, int amount) {
//零钱相当于物品,总金额相当于背包容量
int [] dp=new int[amount+1];//dp[i][j]:对前i个零钱,选若干兑换总金额为j的整钱,最少需要的零钱个数为dp[i][j]

dp[0]=0;//0个零钱,待兑换总金额为0,最少0个零钱就能满足
for(int i=1;i<=amount;i++){
dp[i]=amount + 1; // amount + 1 是不可能达到的换取数量,于是使用其进行填充
}
//dp的过程是取Min
//一维数组dp[j]里的j指的是背包容量,这里即总金额
for(int i=1;i<=coins.length;i++){//对前i个零钱,其中第i个零钱是coins[i-1]
for(int j=coins[i-1];j<=amount;j++){//完全背包正序更新一维数组
dp[j]=Math.min(dp[j],dp[j-coins[i-1]]+1);//根据背包九讲:求最小价值/最少件数,只需将原始状态转移方程中的max改成min
}
}
return dp[amount]==amount + 1?-1:dp[amount];
}

}

结果


摆动排序(分类)

题目

分析

其实就是排序之后将前半段倒序放入奇数位置,后半段倒序放入偶数位置。
举例:输入 1 3 2 2 3 1
    排序之后1 1 2 2 3 3(分为1 1 2和2 3 3)
    倒序输入前半段: 2 X 1 X 1 X
    倒序输入后半段: 2 3 1 3 1 2

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/**
* 先排序再穿插
* O(nlogn)+O(n)=O(nlogn)
*/
public void wiggleSort(int[] nums) {
//排序
Arrays.sort(nums);
int len=nums.length,i = 0;
int[] smaller=new int[len%2==0?len/2:(len/2+1)],bigger=new int[len/2];
//复制
System.arraycopy(nums,0,smaller,0,smaller.length);
System.arraycopy(nums,smaller.length,bigger,0,len/2);
//穿插
for (; i < len / 2; i++) {
nums[2*i]=smaller[smaller.length-1-i];
nums[2*i+1]=bigger[len/2-1-i];
}
if (len%2!=0) nums[2*i]=smaller[smaller.length-1-i];
}
}

结果


3的幂(循环迭代)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11

public boolean isPowerOfThree(int n) {
if (n < 1) {
return false; //如果是0肯定返回0
}

while (n % 3 == 0) {
n /= 3; //n不断除3
}
return n == 1; //最后看是不是等于1
}

结果


递增的三元子序列(类似于动态规划)

题目

分析

结合最长上升子序列那道题的二分贪心解法:
1. 我只需要维护2个变量,分别保存3元上升子序列的第一个值firstMin和第二个值secondMin
2. 当nums[i]<firstMin : 更新firstMin
3. 当firstMin<nums[i]<secondMin : 更新secondMin
4. 这样就能够保证前两个尽可能地小,也就是最有可能找到三元上升子序列  

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for (int i = 0; i <n ; i++) {
a[i]=input.nextInt();
}
System.out.println(increasingTriplet(a));
}

public static boolean increasingTriplet(int[] nums) {
int len=nums.length; //获取数组长度
if(nums.length<3){
return false; //长度<3 不会找到长度为3的子序列
}
int firstMin = Integer.MAX_VALUE; //第一小
int secondMin = Integer.MAX_VALUE; //第二小
for(int i=0;i<nums.length;i++) {
if(nums[i]>secondMin){
return true; //能找到第三个数字
}
if(nums[i]<=firstMin){
firstMin=nums[i]; //找到第一小
}
else if(nums[i]<secondMin){ // firstMin<nums[i]<secondMin
secondMin=nums[i]; //找到第二小
}
}
return false; //默认找不到
}

}

结果


反转字符串(双指针)

题目

分析

1. 不增额外的空间,使用双指针不断挪里面挪动,使用temp中间值交换两边的值。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s1=input.next(); //输入一个字符串
char[] s=s1.toCharArray(); //字符串转字符数组
reverseString(s); //反转
System.out.println(Arrays.toString(s)); //字符数组输出
}

public static void reverseString(char[] s) {
// 左右双指针
int left = 0;
int right = s.length - 1;
// 交换元素的临时变量 temp
char temp;

while (left < right){
temp = s[left];
s[left++] = s[right]; //不断往里面缩小
s[right--] = temp;
}
}

}

结果


前k个高频元素(栈+HashMap集合)

题目

分析

1. 新建map集合存放对应值和出现次数;maxheap根据map集合存放所有<Key,Value>;result数组存放最后弹出来的k个key值
2. 先for遍历存放进去所有的key和value对
3. 然后通过(a,b) -> b.getValue() - a.getValue()对于键值对会有排序
4. 然后通过栈先进后出的原理,相当于倒序输出最大的k个key值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for (int i = 0; i <n ; i++) {
a[i]=input.nextInt();
}
int k=input.nextInt();
System.out.println(Arrays.toString(topKFrequent(a,k)));
}

public static int[] topKFrequent(int[] nums, int k) {
//存放key-value这样的形式 存放每个元素出现次数
Map<Integer,Integer> map=new HashMap<>();
//进行排序
PriorityQueue<Map.Entry<Integer, Integer>> maxheap = new PriorityQueue<>((a,b) -> b.getValue() - a.getValue());
//存放结果的数组
int[] result = new int[k];
for (int i = 0; i < nums.length; i++) {
map.put(nums[i],map.getOrDefault(nums[i],0)+1); //遍历存放每个key对应的value
}
for(Map.Entry<Integer, Integer> m : map.entrySet()){
maxheap.offer(m); //按照键值对放入maxhead中
}
//倒序输出k个值就是结果
for(int i = 0; i < k; i++){
result[i] = maxheap.poll().getKey();
}
return result;
}

}

结果


两个数组的交集II(哈希表hashmap/排序)

题目

分析

1. 使用map存,然后对应数组减少,然后相同的存到数组里面输出。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public int[] intersect(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return intersect(nums2, nums1); //反转数组
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //map集合
for (int num : nums1) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count); //遍历存放对应键值对
}
int[] intersection = new int[nums1.length];
int index = 0;
for (int num : nums2) {
int count = map.getOrDefault(num, 0);
if (count > 0) {
intersection[index++] = num;
count--; //有的话减少一个value值
if (count > 0) {
map.put(num, count);
} else {
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection, 0, index);
}

结果


两数求和(位运算)

题目

分析

1. 能使用加减乘除操作: return a+b;
2. 不使用加减乘除操作: 位运算
    2.1 a^b得到不进位的值
    2.2 (a&b)<<1得到进位值
    2.3 不断进行前两步直到第二步的值为0结束

代码

1
2
3
4
5
6
7
8
9

public int getSum(int a, int b) {
while(b != 0){
int temp = a ^ b;
b = (a & b) << 1;
a = temp;
}
return a;
}

结果


有序矩阵中第K小的元素(直接排序/归并排序/二分法)

题目

分析

1. 直接排序:使用一维数组a存放二维数组元素,然后排序之后输出a[k-1]就是结果

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int[][] a=new int[][]{{1,5,9},{10,11,13},{12,13,15}};
int m=input.nextInt();
System.out.println(kthSmallest(a,m));
}

public static int kthSmallest(int[][] matrix, int k) {
int res=0;
int len=matrix.length; //数组的行和列
int[] a=new int[len*len];
int z=0;
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
a[z++]=matrix[i][j];
}
}
Arrays.sort(a); //进行排序
return a[k-1]; //返回第8个
}

}

结果


字符串中第一个唯一字符(哈希表)

题目

分析

1. 使用hashmap集合存储键值对(每个字符和出现次数)
2. 遍历查询是否出现一次就返回(第一次出现)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
System.out.println(firstUniqChar(s));
}

public static int firstUniqChar(String s) {
//存放字符出现次数和对应字符(键值对)
HashMap<Character, Integer> count = new HashMap<Character, Integer>();
//字符串长度
int n = s.length();
//遍历存入map集合
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
count.put(c, count.getOrDefault(c, 0) + 1);
}
//遍历找谁出现次数是1次
for (int i = 0; i < n; i++) {
if (count.get(s.charAt(i)) == 1)
return i;
}
return -1; //找不到返回-1
}

}

结果


四数相加(哈希表)

题目

分析

1. 使用一个map集合存放a和b的和
2. 使用另外一个map集合存放c和d和的相反数
3. 最后判断是否相等,就说明相反相加是0

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
Map<Integer, Integer> map = new HashMap<>();
//Map<Integer, Integer> map = new HashMap<>();
int res = 0;
for(int i = 0;i<A.length;i++){
for(int j= 0;j<B.length;j++){
int sumAB = A[i]+B[j];
if(map.containsKey(sumAB)) map.put(sumAB,map.get(sumAB)+1);
else map.put(sumAB,1);
}
}

for(int i = 0;i<C.length;i++){
for(int j = 0;j<D.length;j++){
int sumCD = -(C[i]+D[j]);
if(map.containsKey(sumCD)) res += map.get(sumCD); //增加有这个配对的key的value值
}
}
return res;
}

结果


LeetCode哈希表

面试10.02 变位词组(HashMap)

题目

分析

统计26个字母各自出现的次数,得到一个大小为26的int数组。
关键在于如何将这个int数组转换为独一无二的key。
我的方法是将它转为String。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class BianWeiArr {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String[] a={"eat","tea","tan","ate","nat","bat"};
System.out.println(groupAnagrams(a));
}

public static List<List<String>> groupAnagrams(String[] strs) {
int len=strs.length; //获取字符串的长度
HashMap<String,List<String>> map=new HashMap<>(len); //生成一个map获取
for(String str:strs){
int[] count=new int[26]; //新建count数组存放每个字符出现次数
for(int i=0;i<str.length();i++){
count[str.charAt(i)-'a']++; //通过ascii码判断
}
StringBuilder sb=new StringBuilder(100);
for(int num:count){
sb.append(num+"."); //26位每一位粘贴过来
}
map.computeIfAbsent(sb.toString(),unused -> new LinkedList<>()).add(str);
}
return new ArrayList<>(map.values()); //返回新的集合
}

}

结果


面试16.02 单词频率(HashMap)

题目

分析

第一个函数通过hashmap存放次数
第二个函数判断是不是有这个单词 返回这个单词次数

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

class WordsFrequency {
Map<String,Integer> recMap = new HashMap<>();
public WordsFrequency(String[] book) {
for(int i=0;i<book.length;i++) {
if(!recMap.containsKey(book[i])) {
recMap.put(book[i],1);
}else {
recMap.put(book[i], recMap.get(book[i])+1);
}
}
}

public int get(String word) {
if(recMap.containsKey(word)) {
return recMap.get(word);
}
return 0;
}
}

/**
* Your WordsFrequency object will be instantiated and called as such:
* WordsFrequency obj = new WordsFrequency(book);
* int param_1 = obj.get(word);
*/

结果


剑指offer50 第一次只出现一次的字符(count[s.charAt(i)-97]==1)

题目

分析

1. 主要是通过count数组统计每次出现次数
2. for循环让每一位的值对应的count数组查看是否为1 有就退出因为找第一个(count[s.charAt(i)-97]==1)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public class OnlyOne {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.next();
System.out.println(firstUniqChar(s));
}

public static char firstUniqChar(String s) {
int[] count=new int[26];
char c=' ';
for(int i=0;i<s.length();i++){
count[s.charAt(i)-97]++;
}
for(int i=0;i<s.length();i++){
if(count[s.charAt(i)-97]==1){
c=s.charAt(i);
break;
}
}
return c;
}

}

结果


面试16.24 数对和(双指针+list集合)

题目

分析

1. 先试用双指针进行控制得到结果的end和start两端,
2. 每次找到就新建一个list添加进去两个值之后添加到最终的ans里面

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class ShuDuiHe {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<a.length;i++){
a[i]=input.nextInt();
}
int target=input.nextInt();
System.out.println(pairSums(a,target));
}

public static List<List<Integer>> pairSums(int[] nums, int target) {
List<List<Integer>> ans=new LinkedList<>();
Arrays.sort(nums); //对数组进行排序
int start=0;
int end=nums.length-1;
for(;start<end;){
int sum = nums[start] + nums[end];
if (sum < target) { //要变大一些
start++;
} else if (sum > target){ //要变小一些
end--;
} else { //刚好符合
List<Integer> list = new LinkedList<>(); //新成立list集合
list.add(nums[start]); //存进去start和end位置的值
list.add(nums[end]);
ans.add(list); //最终结果添加list集合
start++; //缩小范围
end--;
}
}
return ans;
}

}

结果


LeetCode贪心算法

392判断子序列(indexOf(char c,int m)方法)

题目

分析

1. indexOf(char c,int m)意思是从第m位置开始寻找该索引。
2. 利用该特性我们通过对索引处理从而获得解。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ZiXuLie {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.next();
String t=input.next();
System.out.println(isSubsequence(s,t));
}

public static boolean isSubsequence(String s, String t) {
char[] arr = s.toCharArray(); //字符串每个字符转为数组
int j=-1; //默认-1
for(int i=0;i<arr.length;i++){
j=t.indexOf(arr[i],j+1); //如果有就把j变化
if(j==-1) {
return false; //如果没变就是没找到
}
}
return true;
}

}

结果


LeetCode二分查找

面试10.03 搜索旋转数组(map存储查找)

题目

分析

1. 利用map存储数组内容和下标,然后通过containsKey(target)输出最小的value值即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class SouSuoXuanZhuanArr {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++) {
a[i]=input.nextInt();
}
int target=input.nextInt();
System.out.println(search(a,target));
}

public static int search(int[] arr, int target) {
int min=Integer.MAX_VALUE;
Map<Integer,Integer> map=new HashMap<Integer,Integer>(); //设置map集合存储
for(int i=0;i<arr.length;i++){
map.put(arr[i],i); //将数组的集合内容和下标存储下去
}
if(map.containsKey(target)){ //如果map里面有这个就获取map对应下标
int i=map.get(target);
min=Math.min(min,i); //多个出现就得到最小的那个索引
return min;
}
return -1; //默认找不到就是-1
}

}

结果


面试10.05 稀疏数组搜索(普通遍历)

题目

分析

  1. 就是普通的将字符串数组每一位和目标string对比

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class XiShuSouSuo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String[] words={"at","","","","ball","","","car","","","dad","",""};
String s=input.next();
System.out.println(findString(words,s));
}

public static int findString(String[] words, String s) {
int index=-1;
for(int i=0;i<words.length;i++){
String c=words[i];
if(c.equals(s)){
return i;
}
}
return index;
}

}

结果


面试11 旋转数组的最小数字(二分法范围确定)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class XuanZhuanMin {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<a.length;i++){
a[i]=input.nextInt();
}
System.out.println(minArray(a));
}

public static int minArray(int[] numbers) {
int left=0;
int right=numbers.length-1;
while(left<right){
int mid=(left+right)/2;
if(numbers[right]<numbers[mid]){ // ..3..1(肯定在右边mid-right中)
left=mid+1;
} else if (numbers[mid] < numbers[right]) { //..1..3(肯定在左边left-mid中)
right=mid;
}else{
right--; //确定不了在哪个区域就right--缩小范围
}
}
return numbers[left]; //最后三个指针肯定在一个位置就是需要的
}

}

结果


LeetCode双指针

面试10.01 合并排序的数组

题目

分析

1.最简单的方法:a数组从m之后开始讲b的放进去之后排序即可。
2.双指针可以确定插入位置然后进行挪,这种方法复杂度会低很多。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class HeBingArr {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int m=input.nextInt();
int n=input.nextInt();
int[] a=new int[n+m];
int[] b=new int[n];
for(int i=0;i<m;i++) {
a[i] = input.nextInt();
}
for(int i=0;i<n;i++) {
b[i] = input.nextInt();
}
merge(a,m,b,n);
System.out.println(Arrays.toString(a));
}

public static void merge(int[] a, int m, int[] b, int n) {
int j=0;
for(int i=m;i<m+n;i++){
a[i]=b[j++];
}
Arrays.sort(a);
}

}

结果


面试10.09 排序矩阵查找(双指针控制行和列)

题目

分析

1. 设置h和l双指针,从第0行和第m-1列开始,控制范围满足:h<matrix.length&&l>=0
2. 通过while循环然后有三种情况:
    2.1 如果当前值比target小: 说明这行前面的都比target小 就换下一行 h++
    2.2 如果当前值比target大: 说明这列下面的都比target大 就换前一行 l--
    2.3 如果相等就直接返回true  所以外面方法默认返回就写成false

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ShengXu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int m=input.nextInt();
int n=input.nextInt();
int[][] a=new int[m][n];
for(int i=0;i<m;i++) {
for(int j=0;j<n;j++) {
a[i][j] = input.nextInt();
}
}
int target=input.nextInt();
System.out.println(searchMatrix(a,target));
}

public static boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length==0){ //一定要有判断数组是否存在 不然后面的l这个指针就会异常
return false;
}
int h=0; //行指针
int l=matrix[0].length-1; //列指针
while(h<matrix.length&&l>=0){
if(matrix[h][l]==target){
return true;
}
if(matrix[h][l]<target){
h++; //这行前面的都比这个小 换下一行
}else{
l--; //这列下面的都比这个大 所以换前一列
}
}
return false;
}

}

结果


面试16.06 最小差(ab排序后指针控制移)

题目

分析

1. 主要是先将a和b排序之后我们就可以双指针进行移动
2. 思路:
    int diff = a[i] - b[j]
    如果 diff < 0 ,表示 a[i] 小于 b[j] ,a 尽可能接近 b,那么 i++
    如果 diff > 0 ,表示 a[i] 大于 b[j] ,b 尽可能接近 a,那么 j++

    特殊情况:
    a = {1,2,3,4,5}
    b = {6,7,8,9,10}
    如果 a 数组最大值比 b 数组最小值还小,那么 a 数组 i 会一直右移,直到到达边界 break
    */

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ZuiXiaoCha {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int m=input.nextInt();
int n=input.nextInt();
int[] a=new int[m];
int[] b=new int[n];
for(int i=0;i<m;i++) {
a[i] = input.nextInt();
}
for(int i=0;i<n;i++) {
b[i] = input.nextInt();
}
System.out.println(smallestDifference(a,b));
}

public static int smallestDifference(int[] a, int[] b) {
int alen = a.length;
int blen = b.length;
//a和b排序之后方便查找相近的值在哪
Arrays.sort(a);
Arrays.sort(b);
//给最小值最好赋给其他特定值 如果是数组的内容有可能会报错
int minVal = Integer.MAX_VALUE;
int i=0;
int j=0;
while(i<alen&&j<blen){ //i和j分别是控制a和b数组的变化
// 使用 long,防止 -2147483648 转正数后还是 -2147483648
long cha=a[i]-b[j]; //计算出当前两个数组值的差
minVal = (int)Math.min(Math.abs(cha), minVal); //minVal放的就是最后的结果
if(cha<0){
i++; //a数组的比b数组的小 a数组的值要尽可能靠近b
}else{
j++; //相反b的小 就要往a靠近
}
}
return minVal;
}

}

结果


面试17.11 单词距离(list集合存放出现位置然后比较)

题目

分析

使用两个list集合分别存放单词出现位置,然后循环遍历得到最小值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class DanCiLen {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int m=input.nextInt();
String[] a=new String[m];
for(int i=0;i<m;i++) {
a[i] = input.next();
}
String word1=input.next();
String word2=input.next();
System.out.println(findClosest(a,word1,word2));
}

public static int findClosest(String[] words, String word1, String word2) {
int min=Integer.MAX_VALUE;
List<Integer> list1=new ArrayList<>();
List<Integer> list2=new ArrayList<>();
for(int i=0;i<words.length;i++)
{
if(word1.equals(words[i]))
{
list1.add(i); //list1存放单词word1出现的位置
}
if(word2.equals(words[i])) {
list2.add(i); //list2存放单词word2出现的位置
}
}
for(int j=0;j<list1.size();j++)
{
for(int k=0;k<list2.size();k++)
{
int cha=list1.get(j)-list2.get(k); //看
min=Math.min(min,Math.abs(cha));
}
}
return min;
}

}

结果


面试17.21 接雨水(低动高不动/木桶效应由最短的决定)

题目

分析

1. 核心思路:缓存两端最大值,从最大值较小的一边进行储水结算、移动,并更新最大值。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class JieYuShui {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int m=input.nextInt();
int[] a=new int[m];
for(int i=0;i<m;i++) {
a[i] = input.nextInt();
}
System.out.println(trap(a));
}

public static int trap(int[] height) {
int l=0; //左指针
int r=height.length-1; //右指针
int lmax=height[l]; //左边最大值
int rmax=height[r]; //右边最大值
int res=0;
while(l<r){
if(lmax<rmax){ //左边低 算左边的高度
res+=lmax-height[l];
l++;
lmax=Math.max(height[l],lmax); //更新左边最大值
}else{ //右边低 算右边的高度
res+=rmax-height[r];
r--;
rmax=Math.max(height[r],rmax); //更新右边最大值
}
}
return res;
}

}

结果


面试48. 最长不含重复字符的子字符串(双指针/动态规划)

题目

分析

https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/solution/mian-shi-ti-48-zui-chang-bu-han-zhong-fu-zi-fu-d-9/

1. 哈希表统计:遍历字符串s时,使用哈希表记为dic统计各字符最后一次索引位置。
2. 左边界i获取:遍历s[j]时,可通过访问哈希表dic[s[j]]获取最近的相同字符的索引i 。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ZuiChangZiString {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.next();
System.out.println(lengthOfLongestSubstring(s));
}

public static int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int res = 0, tmp = 0;
for(int j = 0; j < s.length(); j++) {
// getOrDefault代表当哈希表包含键key时返回对应value,不包含时返回默认值default。
int left = map.getOrDefault(s.charAt(j),-1);

map.put(s.charAt(j), j); // 更新哈希表(将对应的字母和下标放入)

if(tmp<(j-left)){
tmp++;
}else{
tmp=j-left;
}
res = Math.max(res, tmp); // res一直是每一次变化之后的最大值
}
return res;
}

}

结果


LeetCode动态规划

面试08.01 三步问题(跳台阶进阶版动态规划)

题目

分析

1. 原来只能一次性跳1/2节台阶,最后就是三个数字不断循环
2. 现在就相当于是四个数字变化,但是可能后面的数字比较大,因此每次计算时候b+c先进行取余之后再个d进行取余即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SanBu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(waysToStep(n));
}

public static int waysToStep(int n) {
int a=1; //一个台阶一种走法 1
int b=2; //二个台阶两种走法 1+1 / 2
int c=4; //三个台阶四种走法 1+1+1 / 1+2 / 2+1 / 3
int d=0;
for(int i=1;i<n;i++){
d=(a+(b+c)%1000000007)%1000000007;
a=b;
b=c;
c=d;
}
return a;
}

}

结果


面试08.02 迷路的机器人

题目

分析

代码

1
2


结果


面试08.11 硬币

题目

分析

代码

1
2


结果


面试17.16 按摩师/打家劫舍(a[0]+a[2]和a[1]对比)

题目

分析

1. 思路:(当前+前两个) ?(前一个)
2. 第一种o(n):dp[i]=Math.max(dp[i-1],nums[i]+dp[i-2]);
3. 第二种o(1): a b c三个变量递进循环

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class AnMoShi {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(massage(a));
}

//O(n)
public static int massage(int[] nums) {
int n = nums.length; //数组长度
if (n == 0) {
return 0; //如果数组长度为0就返回0
}
if (n == 1) {
return nums[0]; //如果只有一个就返回这个值
}
int[] dp = new int[n]; //存放结果
dp[0] = nums[0]; //第一个值就是当前的第一个
dp[1]=Math.max(nums[0], nums[1]); //判断是1开始还是2开始
for(int i=2;i<n;i++){
dp[i]=Math.max(dp[i-1],nums[i]+dp[i-2]);
}

return dp[n-1]; //返回最后一个值
}

//O(1)
public int massage(int[] nums) {
int a = 0, b = 0;
for (int i = 0; i < nums.length; i++) {
int c = Math.max(b, a + nums[i]);
a = b;
b = c;
}
return b;
}

}

结果


面试17.23 最大黑方阵(纯劳力题不推荐)

题目

分析

参考LeetCode别人的代码思路,

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
int[][] matrix;
int[] result=new int[]{0,0,0};
public int[] findSquare(int[][] matrix) {
this.matrix=matrix;
if(matrix.length==0){
return new int[]{};
}
for(int i=0;i<matrix.length-result[2];i++){
for(int j=0;j<matrix.length-result[2];j++){
if(matrix[i][j]==0){
int count=1;
while(count+i<matrix.length&&count+j<matrix.length
&&matrix[count+i][j]==0&&matrix[i][count+j]==0
){
count++;
}
int actualLen=isBlackMatrix(i,j,count);
if(actualLen>result[2]){
result[0]=i;
result[1]=j;
result[2]=actualLen;
}
}
}
}
if(result[2]==0){
return new int[]{};
}
return result;
}
public int isBlackMatrix(int i,int j,int size){//recursive check
if(size==1){
return size;
}
else{
for(int x=0;x<size;x++){
if(matrix[i+size-1][j+x]==0&&matrix[i+x][j+size-1]==0){
continue;
}
else{
int miniLen=isBlackMatrix(i,j,size-1);
return miniLen;
}
}
return size;
}
}
}

结果


面试12 矩阵中的路径/单词搜索

题目

分析

代码

1
2


结果


面试42 连续子数组的最大和/最大子序列和(抓住sum大小)

题目

分析

参考大佬思路:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/solution/mian-shi-ti-42-lian-xu-zi-shu-zu-de-zui-da-he-do-2/

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class LianXuZiarr {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int r=input.nextInt();
int[] a=new int[r];
for(int i=0;i<r;i++) {
a[i] = input.nextInt();
}
System.out.println(maxSubArray(a));
}

public static int maxSubArray(int[] nums) {
int ans=nums[0]; //当前最大就是第一个数
int sum=0;
for(int i=0;i<nums.length;i++){
if(sum>0){
sum+=nums[i]; //sum>0就直接加进去现在的值
}else{
sum=nums[i]; //反之让当前值替换掉
}
ans=Math.max(ans,sum); //ans相当于替换为一轮的最大值
}
return ans;
}

}

结果


面试63 购票最大利润(双指针/动态规划)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class GuPiao {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int r=input.nextInt();
int[] a=new int[r];
for(int i=0;i<r;i++) {
a[i] = input.nextInt();
}
System.out.println(maxProfit(a));
}

public static int maxProfit(int[] prices) {
if(prices.length == 0) {
return 0;
}
int min=prices[0];
int[] dp=new int[prices.length];
for(int i=1;i<prices.length;i++){
min=Math.min(prices[i-1],min); //当前i所在值前面一个的最小值
dp[i]=Math.max(dp[i-1],prices[i]-min); //前面的最优解和最小的收益差的比较
}
return dp[prices.length-1];
}

}

结果


数组中的最长山脉(分两个动规解决)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

public class Main{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int[] nums=new int[]{2,1,4,7,3,2,5}; //可以设定数组 或者初始化
System.out.println(longestMountain(nums)); //调用方法返回结果
}

public static int longestMountain(int[] a) {
int res=0; //接住结果
int len=a.length; //获取数组长度
int flag=3; //山脉的标准长度
int[] left=new int[len]; //记录所有节点左边的最大扩展
left[0]=0; //第一位赋值0
int[] right=new int[len]; //记录所有节点右边的最大扩展
right[a.length-1]=0; //最后一位赋值0
//计算所有节点左边能扩展多少
for(int i=1;i<a.length;i++){
if(a[i]>a[i-1]){ // 2 8 这样的话说明可以扩展
left[i]=left[i-1]+1; //前面的基础上+1
}else{
left[i]=0;
}
}
//计算所有节点右边能扩展多少
for(int i=a.length-2;i>=0;i--){
if(a[i]>a[i+1]){ // 8 2 这样的话说明可以扩展
right[i]=right[i+1]+1; //前面的基础上+1
}else{
right[i]=0;
}
}
//最终找到最长的结果
for(int i=0;i<len;i++){
if(left[i]>0&&right[i]>0){ //一定要都有左右山脉
res=Math.max(res,(left[i]+right[i]+1)); //求左右之和最大的
}
}
return res;
}

}

结果


LeetCode数学问题

面试02.05 链表求和(string和int转换加)

题目

分析

1. 先使用stringbuilder对象将两个链表按序加(其实是反的)
2. 通过自己写的函数翻过去就是原来的数字(调正)
3. 相加之后拆开(一个数字拆成链表形式)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class LianBiaoHe {
public static void main(String[] args) {
ListNode l11=new ListNode(7); //添加7->1->6
ListNode l12=new ListNode(1);
ListNode l13=new ListNode(6);
l11.next=l12;
l12.next=l13;

ListNode l21=new ListNode(5); //添加5->9->2
ListNode l22=new ListNode(9);
ListNode l23=new ListNode(2);
l21.next=l22;
l22.next=l23;
System.out.println(addTwoNumbers(l11,l21));
}

public static int addTwoNumbers(ListNode l1, ListNode l2) {
int sum=0;
StringBuilder sb1=new StringBuilder();
StringBuilder sb2=new StringBuilder();
int flag=0; //记录有几个节点
while(l1!=null){
sb1.append(l1.val);
l1=l1.next;
flag++; //记录有几个节点
}
while(l2!=null){
sb2.append(l2.val);
l2=l2.next;
}
int a=fanzhuan(Integer.parseInt(String.valueOf(sb1)));
int b=fanzhuan(Integer.parseInt(String.valueOf(sb2)));
System.out.println(a+" "+b);
return a+b;
}


//自定义数字反转函数 617->716
public static int fanzhuan(int n){
StringBuilder sb3=new StringBuilder();
int m=1;
while (n!=0){
sb3.append(n%10);
n=n/10;
}
return Integer.parseInt(String.valueOf(sb3)); //字符串转int正数
}

}

结果


面试16.01 交换数字(中间值/数组交换)

题目

分析

1. 使用temp中间值(循环给)
        temp=a;  //a的值给temp
        a=b;     //b的值给a(a已经给temp)
        b=temp;  //temp的值给b(b已经给a)

2. 不适用temp中间值
        数组交换

代码

1
2
3
4
5
6
7
8
class Solution {
public int[] swapNumbers(int[] numbers) {
int[] a=new int[2];
a[0]=numbers[1]; //原来第二个数到第一个
a[1]=numbers[0]; //原来第一个数到第二个
return a;
}
}

结果


面试16.05 阶乘尾数(大数乘法/5的因数)

题目

分析

1. 先计算出阶乘的值 然后按位输出判断0的个数
2. 判断5的因数个数(多少个数字能整除5)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class JieChengWeiShu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(trailingZeroes(n));
}

public static int trailingZeroes(int n) {
int ans=0;
while(n>0){
ans+=n/5; //不断的/5求次数
n/=5;
}
return ans;
}

}

结果


面试17.06 2出现的次数(取每位值)

题目

分析

1.思路:通过每一个数循环去拆开这个数得到2的个数最后综合结果

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ErCiShu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int jie=0;
for(int i=1;i<=n;i++){ //从1到n都调用方法得到结果
jie+=numberOf2sInRange(i);
}
System.out.println(jie);
}

public static int numberOf2sInRange(int n) {
int ci=0; //记录2的个数
int m=1;
while (n != 0) {
m = n % 10;
if (m == 2) {
ci++; //每一位是2就ci++计数
}
n = n / 10;
}
return ci;
}

}

结果


面试17.09 第K个数(看是加3/5/7哪个)

题目

分析

以1 3 5 7 9序列为例:
    1. 第一轮找i=1下标 dp[1]=min(dp[0]*3,dp[0]*5,dp[0]*7)最后选3 p3指针后移到1
    2. 第二轮找i=2下标 dp[2]=min(dp[1]*3,dp[0]*5,dp[0]*7)最后选5 p5指针后移到1

注:其实就是不断地加3/5/7 看到底加哪个最近最好依次上升 

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DiKShu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(getKthMagicNumber(n));
}

public static int getKthMagicNumber(int k) {
int p3=0,p5=0,p7=0;
int[] dp=new int[k];
dp[0]=1; //第一个数一定是1
for(int i=1;i<k;i++){
dp[i]=Math.min(dp[p3]*3,Math.min(dp[p5]*5,dp[p7]*7)); //当前值肯定是离上一个最近的(找最小的)
//匹配到是哪一个就将对应指针后移 方便后面计算该多少个3/5/7
if(dp[i]==dp[p3]*3){
p3++;
}
if(dp[i]==dp[p5]*5) {
p5++;
}
if(dp[i]==dp[p7]*7) {
p7++;
}
}
return dp[k-1]; //返回数组最后一位
}

}

结果


面试14-I 剪绳子/整数拆分(分情况讨论)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class JianShengZi {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(cuttingRope(n));
}

public static int cuttingRope(int n) {
if(n <= 3) {
return n - 1; //必须剪出一段长度为1的绳子
}
int a=n/3;
int b=n%3; //n=3a*b
if(b==0){
return (int)Math.pow(3, a); //3的a次方
}
if(b==1){
return (int)Math.pow(3, a - 1) * 4;
}
return (int)Math.pow(3, a) * 2; // 3 3 5(45) <<< 3 4 4(3*4*4=48)
}

}

结果


面试14-II 剪绳子(分情况讨论)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
int b = n % 3, p = 1000000007;
long rem = 1, x = 3;
for(int a = n / 3 - 1; a > 0; a /= 2) {
if(a % 2 == 1) rem = (rem * x) % p;
x = (x * x) % p;
}
if(b == 0) return (int)(rem * 3 % p);
if(b == 1) return (int)(rem * 4 % p);
return (int)(rem * 6 % p);
}
}

结果


面试17.打印从1到最大的n位数(StringBuilder得最大值)

题目

分析

1. 通过StringBuilder对象获得最大值max
2. 通过Integer.parseInt(String.valueOf(sb))将字符串转整数
3. 最后通过循环将值导入数组输出数组

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface YiDaoN {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(Arrays.toString(printNumbers(n)));
}

public static int[] printNumbers(int n) {
StringBuilder sb=new StringBuilder();
while(n--!=0){
sb.append("9"); //不停地贴9
}
int max=Integer.parseInt(String.valueOf(sb)); //字符串转为整数int
int[] a=new int[max];
for(int i=0;i<max;i++){
a[i]=i+1;
}
return a;
}

}

结果


面试43 1-n整数中1出现的次数/数字1的个数(拆开求1个数)

题目

分析

和之前的求2的出现次数一样我每次每个数去拆开求解每一次出现1的次数即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Yi {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int jie=0;
for(int i=1;i<=n;i++){ //从1到n都调用方法得到结果
jie+=numberOf2sInRange(i);
}
System.out.println(jie);
}

public static int numberOf2sInRange(int n) {
int ci=0; //记录1的个数
int m=1;
while (n != 0) {
m = n % 10;
if (m == 1) {
ci++; //每一位是1就ci++计数
}
n = n / 10;
}
return ci;
}

}

结果


面试44.数字序列中某一位的数字/第N个数

题目

分析

代码

1
2


结果


面试49.丑数(三指针分析)

题目

分析

1. 和只能包含3/5/7因子的题目一样
2. 指定p2 p3 p5三个指针只能进行乘对应数字,每次去判断最小的放入对应数组位置,然后将对应指针后移用于下轮判断

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ChouShu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(nthUglyNumber(n));
}

public static int nthUglyNumber(int n) {
int[] a=new int[n];
a[0]=1;
int p2=0;
int p3=0;
int p5=0;
for(int i=1;i<n;i++){
a[i]=Math.min(a[p2]*2,Math.min(a[p3]*3,a[p5]*5)); //就看哪个乘最小 放入当前位置
if(a[i]==a[p2]*2){
p2++; //哪个满足就要指针后移 为了下轮的判断
}
if(a[i]==a[p3]*3){
p3++;
}
if(a[i]==a[p5]*5){
p5++;
}
}

return a[n-1];
}

}

结果


素数(筛法)

题目

找到1-n之间的所有素数

分析

1. 定义一个布尔数组res(大小就是n+1)
2. 将所有值都先赋成true(后面不是的话要改为false)
3. 第一层循环从i=2到i=n/i遍历
4. 如果第一层的res[i]位置是true说明就是素数,找到对应的倍数改为false
5. 第二层循环从2 3 4这样的倍数去乘让对应位置变false

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt(); //输入要判断的多大值
find(n); //调用方法返回结果
}

public static void find(int n){
boolean[] res=new boolean[n+1];
for(int i=0;i<res.length;i++){
res[i]=true;
}
for(int i=2;i<=n/i;i++){ //从2 3 4 5开始
if(res[i]){ //如果i这个位置是true说明是素数
for (int j=i;j<=n/i;j++) { //从i的1倍 2倍 3倍开始
res[i*j]=false;
}
}
}
for(int i=2;i<res.length;i++){ //从第二位开始输出 (0和1都不是素数)
if(res[i]){
System.out.print(i+" "); //输出素数
}
}
}

}

结果


16进制转10进制

分析

1. 将字符串转大写[toUpperCase()方法]之后分隔成字符数组[toCharArray()方法]
2. 倒序遍历这样方便计算每一次16的倍数[利用math的pow函数解决]
3. 判读就使用Character类的isDigit和isUpperCase判断是不是大写字母和数字,然后根据res=res原来的值+当前值*16的倍数
4. 最后结果是double需要转int即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.nextLine();
System.out.println(zhuan(s));
}

public static int zhuan(String s){
char[] c=s.toUpperCase().toCharArray(); //字符串全转为大写之后拆开 假设输入AbC --> [A,B,C]
double res=0; //最后接结果
int flag=0; //控制每次应该乘多少个16
for(int i=c.length-1;i>=0;i--){ //倒序方便利用flag计算当前位置是16的几次方
char temp=c[i]; //获取当前值
if(Character.isUpperCase(temp)){
if(temp>='A'&&temp<='F'){
res=(temp-'A'+10)*Math.pow(16.0,(double)(flag++))+res; //当前的字母对应的数字*16的倍数+之前的值
}else{
System.out.println("false!"); //出现的字符不在A-F之间
}
}else if(Character.isDigit(temp)){ //出现是0-9的范围内
res=res+(int)(temp-48)*Math.pow(16.0,(double)(flag++)); //当前的字母对应的数字*16的倍数+之前的值
}
}
return (int)res; //因为使用pow函数返回值是double 最后我们需要转为int
}

}

结果


10进制转16进制

分析

1. 就跟10进制转2进制一样
2. 不断地n/16拆分 每一个余数n%16就是结果集(但是要反序)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt(); //输入10进制
System.out.println(find(n));
}

public static String find(int n){
StringBuilder sb=new StringBuilder();
//就跟10进制转2进制一样(直到10进制的数字拆为0结束)
while(n!=0){
int temp=n%16; //获取每一次的余数(要拼接的数字 只不过要反序)
//是普通的0-9的数字
if(temp>=0&&temp<=9){
sb.append(temp); //直接填进去
}else if(temp>=10&&temp<=15){
sb.append((char)(temp-10+'a')); //是10-15的范围内 需要转为a-f
}
else{
System.out.println("false"); //不符合16进制的要求
break;
}
n=n/16; //不断将数字拆小
}
return sb.reverse().toString(); //倒序粘贴在一起才是最终答案
}

}

结果


旅行终点站(找某个路径的终点不是其他的起点)

题目

分析

1.思路: 如果某个路径的终点不是另一个路径的起点,则该点就是旅行终点.
2.实现: 
    2.1 使用map存储所有路径的出发点[path.get(0)]
    2.2 判断set里面没有哪个get(1)的值就说明是终点

代码

1
2
3
4
5
6
7
8
9
10
11
12
13

public String destCity(List<List<String>> paths) {
HashSet<String> set = new HashSet<>();
for (List<String> path : paths) {
set.add(path.get(0)); //所有出发点放入set
}
for (List<String> path : paths) {
if (!set.contains(path.get(1))) { //如果某个终点没有在出发点集合中 说明就是终点
return path.get(1); //返回终点即可
}
}
return null;
}

实现


汉明距离(异或性质/布赖恩·克尼根算法)

题目

分析

1. 其实就是两个数字变二进制,找二进制不同的两个位置之间距离
2. 使用异或的性质:相同为0不同为1
3. 执行步骤:
    1. 先将x和y异或之后的结果 只要找到这个结果出现1的位置就说明是两个数不同的位置
    2. 不断变小,不断判断%2是否为1记录距离

https://leetcode-cn.com/problems/hamming-distance/solution/yi-ming-ju-chi-by-leetcode/

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println(hammingDistance(1,4));
}

public static int hammingDistance(int x, int y) {
int xor = x ^ y; // 0001异或0100 --> 0101就是5
System.out.println("异或结果"+xor);
int distance = 0; //测量距离
while (xor != 0) {
if (xor % 2 == 1) //不同的话就是异或的结果为1的地方
distance += 1;
xor = xor /2; //变小
}
return distance; //返回距离
}

}

结果


增减字符串匹配(双指针移动)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public static int[] diStringMatch(String s) {
int n=s.length(); //获取字符串长度n
int[] res=new int[n+1]; //结果长为n+1
//确定左右指针
int left=0;
int right=n;
for (int i = 0; i < n; i++) {
char temp = s.charAt(i); //获取字符串当前字符
if (temp == 'I') { //递增
res[i]=left++;
}
if (temp == 'D') { //递减
res[i]=right--;
}
}
res[n]=left; //最后一个n+1的位置没有判断 添加left进去
return res;
}

结果


最长特殊序列I(判断字符串长度)

题目

分析

代码

1
2
3
4
5
6

public int findLUSlength(String a, String b) {
if (a.equals(b))
return -1; //如果两个字符串相同 返回-1
return Math.max(a.length(), b.length()); //如果不相同就看谁的更长度更长了
}

结果


非递增顺序的最小子序列(数学问题)

题目

分析

1. 其实就是找一段序列是大于总和/2的
2. 存的话就用list集合存
3. 先排序之后倒序遍历插入找即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public List<Integer> minSubsequence(int[] nums) {
Arrays.sort(nums); //排序
int left=0; //左指针
int right=nums.length-1; //右指针
int sum=0, sum_right=0;
for(int i=0; i<nums.length; i++){
sum += nums[i]; //求总和sum
}
List<Integer> ans = new ArrayList<>(); //接住结果
while(sum_right <= sum/2){
ans.add(nums[right]);
sum_right += nums[right]; //倒序相加 超过总和的一半就弹出
left++;
right--;
}
return ans; //返回结果
}

结果


换酒问题(不断缩小)

题目

分析

1. 其实很像进制转换,不过需要考虑换完之后要加上换了的数量
2. 只要sum剩下的酒瓶 < 要兑换的酒瓶就弹出

代码

1
2
3
4
5
6
7
8
9
10
11
12
13

public int numWaterBottles(int sum, int numExchange) {
int total = sum; //首先将sum加入
while (sum >= numExchange) { //手里的酒瓶数 < 要兑换酒瓶数量
//change表示的是兑换成酒的数量
int change = sum / numExchange;
//total再加上兑换的酒
total += change;
//瓶子数量变为兑换成酒的数量加上没有兑换成酒的数量
sum = change + sum % numExchange; //更新手里的总数
}
return total;
}

结果


托普利茨矩阵(斜对角线数唯一)

题目

分析

1. 斜对角线hang-lie肯定是相同的,使用map存入第一个要判断的,然后对比后面的每一个hang-lie
2. 从1,1开始判断hang-1,lie-1的是否相同

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

//第一种 每一个斜对角线的hang-lie是相同的
// 将第一个hang-lie的元素存入map 后面判断错误返回false
public boolean isToeplitzMatrix(int[][] matrix) {
Map<Integer, Integer> groups = new HashMap();
for (int r = 0; r < matrix.length; ++r) {
for (int c = 0; c < matrix[0].length; ++c) {
if (!groups.containsKey(r-c))
groups.put(r-c, matrix[r][c]);
else if (groups.get(r-c) != matrix[r][c])
return false; //
}
}
return true;
}


//第二种 从(1,1)开始只是对照上一层的(i-1,j-1)元素
public boolean isToeplitzMatrix(int[][] matrix) {
int column = matrix[0].length;
for (int i = 1; i < matrix.length; i++) {
for (int j = 1; j < column; j++) {
if (matrix[i][j] != matrix[i - 1][j - 1])
return false; //如果不同就弹出false
}
}
return true;
}

结果


最小移动次数使数组元素相等

题目

分析

1. 每次使n-1个元素+1 == 每次使1个元素-1然后所有元素+1
2. 所以只需要找到:每次让1个元素-1,多少次后让所有元素相等

代码

1
2
3
4
5
6
7
8
9
10

public static int minMoves(int[] a) {
Arrays.sort(a); //对a数组进行排序
int res=0;
int count=a.length-1; //每次能够使n-1个元素增加1
for (int i = 0; i < a.length; i++) {
res+=a[i]-a[0]; //计算比a【0】多多少
}
return res;
}

结果


LeetCode递归

面试08.05 递归乘法(乘法变加法)

题目

分析

1. 不需要乘法的话我就进行多次加法进行操作

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ChengFa {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int a=input.nextInt();
int b=input.nextInt();
System.out.println(multiply(a,b));
}

public static int multiply(int a, int b) {
int sum=0;
for(int i=0;i<b;i++){
sum+=a; //乘法就相当于多次加法
}
return sum;
}

}

结果


面试08.06 汉诺塔(递归)

题目

分析

1. n=1 : 直接将a移除一个到c
2. n!=1: 先a通过c到b 然后b通过a到c(中间a移除一个到c)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class HanNuoTa {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
List<Integer> A=new ArrayList<>();
A.add(2);
A.add(1);
A.add(0);
List<Integer> B=new ArrayList<>();
List<Integer> C=new ArrayList<>();
hanota(A,B,C);
System.out.println(C.toString());
}

public static void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
hanota(A.size(),A,B,C);
}
//总体的思路:要把A的n个盘移到C上,首先要借助B把n-1个盘移开,然后把A最下面的最大的移到C
//然后把B的n-1个盘移到C上,按照同样的逻辑
//这个函数的意思就是将A的n个盘移到C上
public static void hanota(int n,List<Integer> A, List<Integer> B, List<Integer> C){
//如果A上只有一个盘,直接移到C上
if(n==1) {
C.add(A.remove(A.size()-1));
return;
}
//如果不是,借助c将N-1个盘移到B上
hanota(n-1,A,C,B);
//然后把剩下的一个移到C
C.add(A.remove(A.size()-1));
//把B的移到C上
hanota(n-1,B,A,C);
}

}

结果


面试16.11 跳水板(等差数列)

题目

分析

1. 假设是最短是1 最长是2 有k=3个模板求结果。
2. 取值就是 111 112 122 222(中间每次差了2-1的值),取值的个数就是k+1个。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class TiaoShuiBan {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int shorter=input.nextInt();
int longer=input.nextInt();
int k=input.nextInt();
System.out.println(Arrays.toString(divingBoard(shorter,longer,k)));
}

public static int[] divingBoard(int shorter, int longer, int k) {
// 最小值
int min = shorter * k; //就是可能性中最小取值
// 最大值
int max = longer * k; //就是可能性中最大取值
// 差值
int difference = longer - shorter; //每一次可能中差的一次结果
//数组长度
int len=0;
if (k != 0) {
if (difference == 0) { //最长和最短一样: 可能性就只有一种
len = 1;
} else {
len = k+1; // 最长和最短不一样: 长度是k+1
}
}

int[] nums = new int[len]; //存放可能性的值
for (int i = 0; i <len; i++) {
if (i == 0) {
nums[i] = min; //最小的可能就是k次都是最短的
} else {
nums[i] = nums[i-1] + difference; //后面的每一次可能就是前面的加上两者差
}
}
return nums;
}

}

结果


面试17.12 BiNode

题目

分析

代码

1
2


结果


面试10-I 斐波那契数列(递归/动态规划)

题目

分析

1. 递归:return fib(n-1)+fib(n-2)
2. 动态规划:就是abc三个数字不断后移
    sum = (a + b) % 1000000007;  //第三个数字
    a = b;    //原来的第二个是下一轮的第一个
    b = sum;  //原来的第三个是下一轮的第二个

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class FeiBo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println("递归:"+fib(n));
System.out.println("动态规划:"+dongtai(n));
}
//递归
public static int fib(int n) {
if(n==0){
return 0;
}
if(n==1){
return 1;
}
return fib(n-1)+fib(n-2);
}
//动态规划
public static int dongtai(int n) {
int a = 0, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}

}

结果


面试10-II 青蛙跳台阶(递归/动态规划)

题目

分析

1. 和斐波那契数列一样!!!!

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class QingWa {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(numWays(n));
System.out.println(numWayser(n));
}

//递归
public static int numWays(int n) {
if(n==1){
return 1; //1
}
if(n==2){
return 2; // 11/2
}
return numWays(n-1)+numWays(n-2);
}

//动态规划
public static int numWayser(int n) {
int a=1;
int b=2;
int c=0;
for(int i=1;i<n;i++){
c=(a+b)%1000000007;
a=b;
b=c;
}
return a;
}

}

结果


面试16. 数值的整数次方(分析特殊情况)

题目

分析

1. 底为0结果就是0
2. 指为0结果就是1
3. 底不为0指数是正数(>1)  正常for循环计算
4. 底不为0指数是负数  先负数转正数后for循环计算

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ShuZhengCiFang {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
Double x=input.nextDouble();
int n=input.nextInt();
System.out.printf("%.5f",cifang(x,n));
}

public static double cifang(double base,int exponent){
double c=1;
//底为0
if(base==0){
return 0;
}
if(exponent==0){ //任何数的0次方都是1
return 1;
}
//底不为0 指数是负数
if(exponent<0){
exponent=-exponent;
while (exponent--!=0) { //有多少m就乘多少次n
c*=base; //c=c*n 在前面的基础上乘n
}
c=1.0/c; //取倒数
}
//底不为o 指数大于等于1
if(exponent>=1) {
while (exponent--!=0) { //有多少m就乘多少次n
c*=base; //c=c*n 在前面的基础上乘n
}
}
return c;
}

}

结果


生成最小高度树(递归)

题目

分析

1. 使用递归法解决
2. 因为要高度最小所以尽量左右子树要多,所以选数组中间元素为根节点
3. 然后生成根节点n,n的值为数组中间节点
4. n的左节点为递归调用(0--nums.length/2)
5. n的右节点为递归调用(nums.length/2+1--nums.length)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13

class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if(nums.length==0) {
return null; //数组为空
}
TreeNode n = new TreeNode(nums[nums.length/2]); //取最中间的值作为根节点
//使用Arrays.copyOfRange()方法进行递归
n.left = sortedArrayToBST(Arrays.copyOfRange(nums,0,nums.length/2));
n.right = sortedArrayToBST(Arrays.copyOfRange(nums,nums.length/2+1,nums.length));
return n; //返回n节点
}
}

结果


二叉搜索树范围和(深度优先遍历)

题目

分析

https://leetcode-cn.com/problems/range-sum-of-bst/solution/hua-jie-suan-fa-938-er-cha-sou-suo-shu-de-fan-wei-/

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public static int rangeSumBST(TreeNode root, int L, int R) {
if(root==null){ //为空
return 0;
}
if(root.val<L){
return rangeSumBST(root.right,L,R); //当前节点<L 返回右子树之和
}
if(root.val>R){
return rangeSumBST(root.left,L,R); //当前节点>R 返回左子树之和
}
//当前节点值+左子树之和+右子树之和
return root.val + rangeSumBST(root.left, L, R) + rangeSumBST(root.right, L, R);
}

结果


单值二叉树

题目

分析

1. 左子树和根节点不同 返回fasle
2. 右子树和根节点不同 返回false
3. 递归return返回 左子树&&右子树

代码

1
2
3
4
5
6
7
8
9
10
11
12
13

public boolean isUnivalTree(TreeNode root) {
if (root == null) {
return true; //空树就是true
}
if (root.left != null && root.val != root.left.val) { //左树不为空 && 根节点值!=左子树节点
return false;
}
if(root.right != null && root.val != root.right.val) { //右树不为空 && 根节点值!=右子树节点
return false;
}
return isUnivalTree(root.left) && isUnivalTree(root.right); //递归分析左右节点
}

结果


各位相加(递归)

题目

分析

1. 使用进制取每一位增加给temp,然后递归找到temp<10就弹出

代码

1
2
3
4
5
6
7
8
9
10
11
12

public int addDigits(int num) {
if (num < 10) {
return num; //如果是小于10的值就返回
}
int temp = 0;
while (num != 0) {
temp=temp+num%10; //每一位增加
num/=10; //数组缩小
}
return addDigits(temp); //递归找temp
}

结果


数字翻译成字符串(递归)

题目

分析

1. 这道题其实就是一个递归:递归出口是num是只有一位数,
2. 以xyzcba为例,先取最后两位(个位和十位)即ba,
    2.1 如果ba>=26,必然不能分解成f(xyzcb)+f(xyzc),此时只能分解成f(xyzcb);
    2.2 但还有一种情况,就是ba<=9,也就是该数十位上为0,此时只能分解成f(xyzcb);

代码

1
2
3
4
5
6
7
8
9
10
11
12
13

public int translateNum(int num) { //递归
if (num<=9) {
return 1; //最后num是个位数小于等于9
}
//xyzcba为例
int ba = num%100; //取最后两位
if (ba<=9||ba>=26) {
return translateNum(num/10); //如果ba>=26,必然不能分解成f(xyzcb)+f(xyzc),此时只能分解成f(xyzcb)
//但还有一种情况,就是ba<=9,也就是该数十位上为0 也只能分解成f(xyzcb)
}
return translateNum(num/10)+translateNum(num/100); //其他情况下就是最后一位和倒数第二位
}

结果


#

LeetCode排序

面试16.16 部分排序(排序后找不同的两边下标)

题目

分析

1. a.clone()复制一个数组然后排序
2. 最左边不同的值和最右边不同的值就是要求的两边

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class BuFenPaiXu {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(Arrays.toString(subSort(a)));
}

public static int[] subSort(int[] a) {
int[] b=a.clone(); //复制一个a数组
int[] jie=new int[]{-1,-1}; //结果的值默认是-1 -1
Arrays.sort(b); //对于b数组进行排序
//从左到右找最左边的不同的值
for(int i=0;i<a.length;i++){
if(a[i]!=b[i]){
jie[0]=i;
break;
}
}
//如果找不到就输出 -1 -1
if(jie[0]<0){
return jie;
}
//从右到左找最右边的不同的值
for(int i=a.length-1;i>=0;i--){
if(a[i]!=b[i]){
jie[1]=i;
break;
}
}
return jie;
}

}

结果


面试16.21 交换和(set去重)

题目

分析

1. 两数组间差要是偶数(偶数才可以补之间差)
2. 计算出两者差cha=sum1—sum2
3. 计算出两者平均要补aver=cha/2
4. 使用set存储数组a,然后进行去重set.contains(b[i]+aver)是否存在就说明两者之间可以互换!!

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class JiaoHuanHe {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int m=input.nextInt();
int[] a=new int[n];
int[] b=new int[m];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
for(int i=0;i<m;i++){
b[i]=input.nextInt();
}
System.out.println(Arrays.toString(findSwapValues(a,b)));
}

public static int[] findSwapValues(int[] a, int[] b) {
int sum1 = 0; //a数组的和
int sum2 = 0; //b数组的和
Set<Integer> set = new HashSet<>();
//遍历获得a数组的和
for(int num:a){
sum1 += num;
set.add(num); //将a数组的内容加到set集合
}
//遍历获得b数组的和
for(int num:b) {
sum2 += num;
}
int diff = sum1 - sum2; //获取之间的差
if(diff % 2 != 0) {
return new int[]{}; //如果差不是偶数的话肯定不能找到
}
int target = diff / 2; //中间差/2就是两边需要补的数字
for(int num:b){
if(set.contains(num + target)){ //如果a数组里面有b数组加上中间补的数字 说明可以互换
return new int[]{num + target, num}; //反着输出
}
}
return new int[]{}; //返回空
}

}

结果


面试17.08 马戏团人塔(map/最长子序列)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
public int bestSeqAtIndex(int[] height, int[] weight) {
if (height.length <= 1) {
return height.length;
}

Integer[] indexs = new Integer[height.length];
for (int i = 0; i < indexs.length; i++) {
indexs[i] = i;
}

Arrays.sort(indexs, (o1, o2) -> {
if (height[o1] == height[o2]) {
return weight[o2] - weight[o1];
}
return height[o1] - height[o2];
});

int[] tail = new int[height.length + 1];
int end = 0;
tail[0] = weight[indexs[0]];
for (int i = 1; i < height.length; i++) {
if (weight[indexs[i]] > tail[end]) {
++end;
tail[end] = weight[indexs[i]];
} else {
int left = 0;
int right = end;
int target = weight[indexs[i]];
while (left < right) {
int mid = (left + right) >>> 1;
if (target > tail[mid]) {
left = mid + 1;
} else {
right = mid;
}
}
tail[left] = target;
}
}
// 因为是从0 开始的 所以需要加1
end++;
return end;
}

结果


面试17.14 最小k个数(Arrays.sort())

题目

分析

1. 使用数组排序之后输出前k位即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MinK {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
int k=input.nextInt();
System.out.println(Arrays.toString(smallestK(a,k)));
}

public static int[] smallestK(int[] arr, int k) {
Arrays.sort(arr); //原数组进行排序
int[] b=new int[k];
for(int i=0;i<k;i++){
b[i]=arr[i]; //前k个数给b数组并且输出
}
return b;
}

}

结果


面试45 数组排成最小的数(sort内置函数)

题目

分析

主要使用内置函数:Arrays.sort(strs,(x,y)->(x+y).compareTo(y+x))

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class PaiZuiXiao {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=input.nextInt();
}
System.out.println(minNumber(a));
}

public static String minNumber(int[] nums) {
StringBuilder sb=new StringBuilder();
String[] strs=new String[nums.length];
for(int i=0;i<strs.length;i++){
strs[i]=String.valueOf(nums[i]); ///所有数字转为字符串
}
Arrays.sort(strs,(x,y)->(x+y).compareTo(y+x)); //内置函数比较排序
for(String s:strs){
sb.append(s); //按序将所有内容添加到sb对象内
}
return sb.toString();
}

}

结果


LeetCode字符串

面试01.03 URL化(空格替换)

题目

分析

1. string.substring(x,y).replaceAll("x","y"):
2. StringBuilder.append("X")
3. 字符数组char[]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class URLhua {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.nextLine();
int length=input.nextInt();
System.out.println(replaceSpaces(s,length));
}

public static char[] replaceSpaces(String s, int length) {
char[] a=new char[length*3];
int i=0;
char c;
for(int j=0;j<length;j++) { //控制长度length
c=s.charAt(j); //获取字符串的当前字符
if (c == ' '){ //如果当前位置为空
a[i++] = '%'; //空替换为 %20
a[i++] = '2';
a[i++] = '0';
}else{
a[i++] =c; //不为空就它填进去
}
}
return a;
}

}

结果


面试01.04 回文排列(字符出现次数)

题目

分析

每个字符出现的次数为偶数 / 有且只有一个字符出现的次数为奇数

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HuiWenPaiLie {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.next();
System.out.println(canPermutePalindrome(s));
}

public static boolean canPermutePalindrome(String s) {
Set<Character> set = new HashSet<>();
for(char c:s.toCharArray()) {
if (!set.add(c)) { //如果c字符不能添加就说明是重复出现
set.remove(c); //移除第一次添加的c字符
}
}
return set.size()<=1; //看最后是不是只有那个奇数的一个/全是偶数的被移除了
}

}

结果


面试01.05 一次编辑(统计变换次数)

题目

分析

1. |a.length-b.length|>1 说明不可能通过一次编译完成
2. 两层循环如果到了不相等的位置
3. 判断是前面的多一个还是后面的多一个
    3.1 前面多 后面的字符串需要多添一个(j--)
    3.2 后面多 前面的字符串需要多添一个(i--)
    3.3 最后一次执行count--
4. 最后我们通过判断count变化几次来判断

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class YiCiBianYi {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String first=input.nextLine();
String second=input.nextLine();
System.out.println(oneEditAway(first,second));
}

public static boolean oneEditAway(String first, String second) {
int len=first.length()-second.length();
if(len>1||len<-1){
return false; //长度差大于1 肯定不可能
}
int count=1;
for(int i=0,j=0;i<first.length()&&j< second.length();i++,j++){
if(first.charAt(i)!=second.charAt(j)){
if(len==1){ //前面的多一个
j--; //second要不要添加一个字符
}else if(len==-1){ //后面的多一个
i--; //first要不要添加一个字符
}
count--; //反正不相等肯定要操作一次 count--变成0
}
if(count<0){ //最多能改一次(最大也是0)
return false;
}
}
return true;
}

}

结果


面试01.06 字符串压缩(StringBuilder添加)

题目

分析

1. 使用StringBuilder字符串对象sb
2. 步骤:
    2.1 当前字符添加进去
    2.2 计算出现次数
        2.2.1 相邻相等计数器++
        2.2.2 相邻不相等直接break
    2.3 将重复次数添加进去
    2.4 返回长度最小的结果(s/sb)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StringYaSuo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.nextLine();
System.out.println(compressString(s));

}

public static String compressString(String s) {
StringBuilder sb=new StringBuilder(); //新建sb对象
for(int i=0;i<s.length();i++){
int count=1; //记录重复出现次数
char c=s.charAt(i); //获取当前字符串的字符
sb.append(c); //将其加入到sb对象
for(int j=i+1;j<s.length();j++){ //找后面的用于判断是否相同count++?
char c2=s.charAt(j); //获取当前字符串的字符(c之后一位)
if(c==c2){ //如果前后一样 就count++
count++;
i++; //成功了就一定要记得往后推!!!!!!!!
}else{
break; //如果不相等 说明重复结束了
}
}
sb.append(count); //循环之后我们将重复次数加进去
}
return s.length()>sb.length()?sb.toString():s;
}

}

结果


面试01.09 字符串轮转(contains判断是否包含子串)

题目

分析

1. 原来的s1重复添加(S1+=S1;)
2. 使用String.contains(s2); //判断现在的s1里面有没有s2

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class StringLunZhuan {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s1=input.nextLine();
String s2=input.nextLine();
System.out.println(isFlipedString(s1,s2));
}

public static boolean isFlipedString(String s1, String s2) {
if(s1.length()!=s2.length()){ //如果不一样长肯定有问题
return false;
}
if(s1.equals(s2)){ //如果是aaa这种肯定是正确
return true;
}
s1+=s1; //将s1重复添加一次(abc+abc=abcabc)
return s1.contains(s2); //查找s1里面有没有s2
}

}

结果


面试05.02 二进制转字符串(StringBuilder添加)

题目

分析

1. 只需要考虑小数点后的乘2问题
2. 思路:每次乘2将整数部分按需存进去直到乘2为0

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ErjinzhizhuanString {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
Double num=input.nextDouble();
System.out.println(printBin(num));
}

public static String printBin(double num) {
if(num <= 0 || num >= 1){
return "ERROR";
}
StringBuilder sb = new StringBuilder(); //用于粘贴结果
sb.append("0."); //因为介于0-1之间
while(num != 0){
num *= 2;
sb.append(num - 1 >= 0 ? 1 : 0);
if(num >= 1){
num --;
}
if(sb.length() > 32){
return "ERROR";
}
}
return sb.toString();
}

}

结果


面试08.09 括号(深度优先遍历(回溯))

题目

分析

https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/

深度优先遍历(回溯):

  • 以n=2为例:

画图以后,可以分析出的结论:

1. 当前左右括号都有大于 00 个可以使用的时候,才产生分支;

2. 产生左分支的时候,只看当前是否还有左括号可以使用;

3. 产生右分支的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支;

4. 在左边和右边剩余的括号数都等于 00 的时候结算。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class KuoHao {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
System.out.println(generateParenthesis(n)); //调用方法
}

public static List<String> generateParenthesis(int n) {
List<String> res=new ArrayList();
//特定判断
if (n == 0) {
return res;
}
//执行深度优先遍历
dfs("",n,n,res);

return res;
}

/**
* @param curStr 当前递归得到的结果
* @param left 左括号还有几个可以使用
* @param right 右括号还有几个可以使用
* @param res 结果集
*/
private static void dfs(String curStr, int left, int right, List<String> res) {
//因为每一次尝试,都使用新的字符串变量,所以无需回溯
if(left==0&&right==0){
res.add(curStr); // 在递归终止的时候,直接把它添加到结果集即可
return ; //void不需要返回
}

// 剪枝(左<右就符合左边用的多这样不需要剪枝)
if (left > right) {
return; //void不需要返回
}

//左分支(左边用了一个“(”)
if(left>0){
dfs(curStr+"(", left-1, right, res);
}

//右分支(右边用了一个“)”)
if(right>0){
dfs(curStr+")", left, right-1, res);
}

}

}

结果


面试16.18 模式匹配(KMP)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class Solution {
private char[] global_pattern, global_value;
private int value_len;

public boolean patternMatching(String pattern, String value) {
value_len = value.length();
if (value_len == 0) return pattern.length() < 2;
global_pattern = pattern.toCharArray();
global_value = value.toCharArray();
int pattern_len = pattern.length();
if (pattern_len == 0) return false;
int count_a = 0, count_b = 0;
for (char ch : global_pattern) {
if (ch == 'a') ++count_a;
else ++count_b;
}
if (count_a == 0) return single_count(count_b);
if (count_b == 0) return single_count(count_a);
return count_a < count_b ? muti_count(count_a, count_b, 'a') : muti_count(count_b, count_a, 'b');
}

private boolean single_count(int count) {
if (value_len % count != 0) return false;
String base = new String(global_value, 0, value_len / count);
int base_len = base.length(), idx = base_len;
while (idx < value_len) {
if (!check(base, idx)) return false;
idx += base_len;
}
return true;
}

private boolean muti_count(int small_count, int big_count, char small_ch) {
int small_sum = 0;
search:
while ((small_sum += small_count) < value_len) {
int remain = value_len - small_sum;
if (remain % big_count != 0) continue;
int big_len = remain / big_count, small_len = small_sum / small_count;
String small_str = null, big_str = null;
int value_idx = 0;
for (char cur_ch : global_pattern) {
if (cur_ch == small_ch) {
if (small_str == null)
small_str = new String(global_value, value_idx, small_len);
else if (!check(small_str, value_idx)) continue search;
value_idx += small_len;
} else {
if (big_str == null)
big_str = new String(global_value, value_idx, big_len);
else if (!check(big_str, value_idx)) continue search;
value_idx += big_len;
}
}
return true;
}
return single_count(small_count) || single_count(big_count);
}

private boolean check(String str, int value_idx) {
int idx = 0;
for (char ch : str.toCharArray())
if (ch != global_value[value_idx + idx++])
return false;
return true;
}
}

结果


面试16.26 计算器(Stack栈)

题目

分析

1. 使用字符数组arr接住字符串s的每一位字符,str接住每一次的数字,num用于将每一次的数字转为int,prev用于接住+-*/,用stack栈进行结果的存放。
2. 对于+-运算直接将数字放入stack栈
3. 对于*/运算需要将结果计算好放入stack栈

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class JiSuanQi {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.next();
System.out.println(calculate(s));
}

public static int calculate(String s) {
char[] arr=s.replaceAll(" ","").toCharArray(); //将要判断的字符串转字符数组arr
Stack<Integer> stack=new Stack<>(); //新建stack栈
int res=0; //最终结果
char prev=' '; //每一位字符
for(int i=0;i<arr.length;i++){
StringBuilder str=new StringBuilder();
//1. 每一轮将所对应的数字放到str内!!!!!!!!!!!!
while(i<arr.length&&arr[i]>='0'&&arr[i]<='9'){ //在0-9之内
str.append(arr[i]); //将符合的添加到str
i++; //添加往后挪
}
System.out.println("str的值:"+str.toString());
int num=Integer.parseInt(str.toString()); //假如是3-6-8*5+6/8: num每一次将str中的数字转为int数字
System.out.print("num的值:"+num+" ");
//2. 判断四则运算!!!!!!!!!!!!!!!!!!
if(prev == '+'){
stack.push(num); //将数值放入栈内
}else if(prev == '-'){
stack.push(-num); //将数值相反数放入栈内
}else if(prev == '*'){
stack.push(stack.pop() * num); //乘法算出结果放入栈
}else if(prev == '/'){
stack.push(stack.pop() / num); //除法算出结果放入栈
}else{
stack.push(num);
}
if(i < arr.length) {
prev = arr[i]; //将数组的每一个字符都放入prev内用于判断是不是+-*/中一个
}
}
while (!stack.isEmpty()) { //只要栈不为空
res += stack.pop(); //依次将数组结果弹出后加入到res中
}

return res;
}

}

结果


面试17.13 恢复空格(动态递归+contains包含方法)

题目

分析

1. 使用set集合存储字典内所有单词
2. 定义dp数组每一个位置存放当前最优的答案
3. 定义两层循环:如果set.contains包含方法内有句子中的单词我们就去考虑i当前位置的答案和j位置的答案取最小值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class HuiFuKongGe {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String[] dictionary={"looked","just","like","her","brother"};
String sentence=input.next();
System.out.println(respace(dictionary,sentence));
}

public static int respace(String[] dictionary, String sentence) {
Set<String> set = new HashSet<>();
for(String str: dictionary) {
set.add(str); //将字典里面的所有单词放入set集合
}
int n = sentence.length(); //获取长度
//dp[i]表示sentence前i个字符所得结果
int[] dp = new int[n+1];
for(int i=1; i<=n; i++){
dp[i] = dp[i-1]+1; //先假设当前字符不在字典
for(int j=0; j<i; j++){
if(set.contains(sentence.substring(j,i))){
dp[i] = Math.min(dp[i], dp[j]); //如果set里面有要判断的单词 就只要最小的长度
}
}
}
return dp[n]; //返回最后一个位置的长度即可
}

}

结果


面试58-I 翻转单词顺序(split()分割/trim()去除首尾空格)

题目

分析

1. 先删除首尾空格然后分割字符串生成一个单词表strs
2. 然后倒序遍历单词表strs存储到StringBuilder对象内

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ZuiChangDanCi {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.nextLine();
System.out.println(reverseWords(s));
}

public static String reverseWords(String s) {
String[] strs = s.trim().split(" "); // 删除首尾空格,分割字符串
StringBuilder res = new StringBuilder();
for(int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
if(strs[i].equals("")){
continue; // 遇到空单词则跳过
}
res.append(strs[i]+" "); // 将单词拼接至 StringBuilder
}
return res.toString().trim(); // 转化为字符串,删除尾部空格,并返回
}

}

结果


面试58-II 左旋转字符串(substring取特定位置字符串)

题目

分析

1. 使用substring获取n之前的和n之后两部分字符串
2. 然后stringbuilder对象去append链接两个部分

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ZuoXuanZhuanString {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s=input.nextLine();
int n=input.nextInt();
System.out.println(reverseLeftWords(s,n));
}

public static String reverseLeftWords(String s, int n) {
String s1=s.substring(0,n); //前n个长度的字符串
String s2=s.substring(n,s.length()); //后面的正常字符串
StringBuilder sb=new StringBuilder();
sb.append(s2).append(s1); //sb去接s2然后s1
return sb.toString();
}

}

结果


Z字形变换(StringBuilder对象)

题目

分析

参考思路

https://leetcode-cn.com/problems/zigzag-conversion/solution/zzi-xing-bian-huan-by-jyd/

1. 怎么存: 每层都是一个sb对象 最后每一层都append在最终的一个sb对象
2. 怎么上下走: 定义i控制i上下走,上下走就是通过flag的变化(这个操作绝了)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String s=input.next();
int n=input.nextInt();
System.out.println(convert(s,n));
}

public static String convert(String s, int numRows) {
if(numRows<2){ //如果是0行或者1行 就直接输出s即可
return s;
}
//存储每一行的字符(要拼接所以要用StringBuilder类)
List<StringBuilder> rows = new ArrayList<StringBuilder>();
//根据行数每行添加一个StringBuilder
for(int i=0;i<numRows;i++){
rows.add(new StringBuilder()); //每一行都有一个
}
int i=0;
int flag=-1;
for(char c:s.toCharArray()){
rows.get(i).append(c); //对应行添加进去字符
if(i==0||i==numRows-1){
flag=-flag; //到了拐角要变换flag
}
i=i+flag; //1的话就是往下加 -1的话就是往上加
}
StringBuilder res = new StringBuilder(); //新建接住结果
for(StringBuilder row:rows) {
res.append(row); //添加每一行的结果集合
}
return res.toString();//最后记得转字符串
}

}

结果


最常见的单词(map集合去重)

题目

分析

1. 使用正则表达式[^a-z]可以筛选单词
2. 使用map存储出现单词和单词次数
3. 使用remove删除禁用词
4. 使用entrySet()方法取出出现次数最多的单词

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

class Solution {
//返回次数最多的单词
public static String mostCommonWord(String s, String[] str) {
s=s.toLowerCase();
String[] temp=s.split("[^a-z]"); //根据空格转字符串

Map<String,Integer> map=new HashMap<>();
for(int i=0;i< temp.length;i++){
String ts=temp[i]; //获取每一个单词的
if(map.containsKey(ts)){
map.put(ts,map.get(ts).intValue()+1); //出现次数+1
}else{
map.put(ts,1); //没出现过就设置出现为1
}
}
for(int i=0;i<str.length;i++){
str[i]=str[i].toLowerCase();
map.remove(str[i]); //依次移除所有的禁用词
}
map.remove("");
Integer max=0;
String res=""; //一定要设置成-1 /0 可能会出错!!!!!!!!!!!
for (Map.Entry<String, Integer> entry : map.entrySet()) {
int count = entry.getValue();
if (count > max) {
max = count;
res = entry.getKey();
}
}
return res;
}
}

结果


字符的最短距离(左右两边最短)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println(Arrays.toString(shortestToChar("loveleetcode",'e')));
}

public static int[] shortestToChar(String S, char C) {
int N = S.length(); //获取字符串长度
int[] ans = new int[N]; //返回结果的ans数组

int prev=Integer.MIN_VALUE/2; //先取负数
//从左往右取结果
for (int i = 0; i < N; ++i) { //正序
if (S.charAt(i) == C) prev = i;
ans[i] = i - prev; //找到相近的字符的距离
}
//从右往左取结果
prev = Integer.MAX_VALUE / 2; //先取正数
for (int i = N-1; i >= 0; --i) { //倒序
if (S.charAt(i) == C) prev = i;
ans[i] = Math.min(ans[i], prev - i); //找到相近的字符的距离
}
return ans; //返回ans数组
}

}

结果


删除回文子序列(因为只有a和b字符)

题目

分析

代码

1
2
3
4
5
6
7
8
9
10

public int removePalindromeSub(String s) {
if ("".equals(s)) {
return 0; //空字符串
}
if (s.equals(new StringBuilder(s).reverse().toString())){
return 1; //本身就是回文串
}
return 2; //其他情况就是一次性删除所有a 然后删除b
}

结果


字符串的最大公因子(辗转相除法)

题目

分析

使用辗转相除法得到最大公因子!!!

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public String gcdOfStrings(String str1, String str2) {
// 假设str1是N个x,str2是M个x,那么str1+str2肯定是等于str2+str1的。
if (!(str1 + str2).equals(str2 + str1)) {
return "";
}
int l1=str1.length();
int l2=str2.length();
// 辗转相除法求gcd。
return str1.substring(0, gcd(l1, l2)); //截取str1的部分字符串
}

public static int gcd(int a, int b) {
return b == 0? a: gcd(b, a % b); //b是0就返回a b不是0就返回a%b
}

结果


仅仅反转字母(字母压栈)

题目

分析

1. 个人感觉倒序输入/反向的都需要栈顶
2. 第一次遍历将所有字符倒序压栈到栈stack  其实就反序放进去
3. 第二次遍历只要是特殊符号就放到遍历的位置  其他位置就反序出栈即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public String reverseOnlyLetters(String S) {
Stack<Character> letters = new Stack();
for (char c: S.toCharArray())
if (Character.isLetter(c))
letters.push(c); //所有数字反序压栈

StringBuilder ans = new StringBuilder();

//第二次再次遍历
for (char c: S.toCharArray()) {
if (Character.isLetter(c))
ans.append(letters.pop()); //如果当前位是字母 就出栈 刚好是反序
else
ans.append(c); //特殊位置继续放入当前位置
}

return ans.toString();
}

结果


翻转数位(只能把1个0改成1)

题目

分析

1. 使用方法将n转成二进制字符串形式s
2. 使用方法将字符串通过0隔开成字符串数组 每个子数组的长度就是1的长度
3. 最后通过for循环遍历找到第哪两个之间+1最大即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public static int reverseBits(int n) {
if(n==-1){
return 32; //负数就是32个
}
String s = Integer.toBinaryString(n);
String[] arr = s.split("0");
if(arr.length<1) {
return arr.length+1; //如果没有1就返回+1
}
int res=arr[0].length()+1; //暂定最终结果
for (int i = 1; i < arr.length; i++) { //从第二位开始
int temp=arr[i-1].length()+arr[i].length()+1; //获取当前值和上一位值+1 就相当于连起来试
res=Math.max(res,temp); //每次找最大的res
}
return res;
}

结果


,