文章目录
- 面经总结
- 1. 编程:股票最大利润
- 2. 算法.数组中出现次数超过一半的数字
- 3. 自己创建一个单链表,返回中间结点的值
- 4. 算法题青蛙跳格子
- 5. 算法题:罗马字符转整数
- 6. 两个list合并成一个有序无重复的list
- 7. 二叉树的遍历
- 8. 算法题:一个排列好的数组输出所有不相同数字的个数
- 9. 算法题:输入{''abc'',''bac'',''cab'',''atc'',''act'',''aac'',''fbc''};
- 10. 按序增长的一个数组求出小于m的最大的数
- 11. 一个链表中删除一个值返回这个链表
- 12. 单例设计模式
- 13. 快排
- 14. 排序算法及时间复杂度,写快排,快排是否稳定
- 15. 手撕代码:给一个字符串,找出最长的不重复的子串;
- 16. 手撕代码:力扣41 困难难度,缺失的第一个整数
- 17. 对含有重复数字的数组去重并排序
- 18. 最大不重复子串,说思路,时间复杂度
- 19. 输入一个字符串,如“127.0.0.1”,判断是否是十进制表示的ipv4地址
- 20. 输入一个数组{1,2,3,2,2,4,4,1};输出数组中数字重复出现的最大次数,写出它的测试用例
- 21. 代码题:滑动窗口最大值
- 22. 给定一个数组,求和为s的所有子数组
- 23. 代码题:两个字符串求最长公共子串
- 24. 字符串变形。"Hello World"变形后就变成了"wORLD hELLO"
- 考前必刷
面经总结
1. 编程:股票最大利润
public int maxProfit(int[] prices) {
int minprice = prices[0];
int maxProfit =0;
for(int i=1; i<prices.length;i++){
int min = Math.min(minprice, min);
maxProfit = Math.max(maxProfit, prices[i]-min);
}
return maxProfit;
}
2. 算法.数组中出现次数超过一半的数字
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
3. 自己创建一个单链表,返回中间结点的值
>先求size,然后求出中间的位置,遍历得到中间节点。
4. 算法题青蛙跳格子
public int numWays(int n) {
if(n <=1) return 1;
int[] arr = new int[n+1];
arr[0] = 1;
arr[1] = 1;
for (int i=2;i<arr.length;i++){
arr[i] = arr[i-1] + arr[i-2];
arr[i] %= 1000000007;
}
return arr[n];
}
5. 算法题:罗马字符转整数
public int romanToInt(String s){
int pre =getChar(s.charAt(0) );
int sum =0;
// 如果左边比右边小,就前一个作为负数
// for循环是加减前一个元素
for(int i=1; i<s.length(); i++){
int num = getChar(s.charAt(i));
if( num > pre)
sum -= pre;
else
sum += pre;
pre = num;
}
//前面的循环,没有加上最后一个元素,所以现在要加上最后一个元素
sum += pre;
return sum;
}
public int getChar(char c){
switch (c){
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;
}
}
6. 两个list合并成一个有序无重复的list
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
ListNode l = null;
if(l1.val <= l2.val){
l = l1;
l.next = mergeTwoLists(l1.next, l2);
}else {
l=l2;
l.next = mergeTwoLists(l1, l2.next);
}
return l;
}
7. 二叉树的遍历
8. 算法题:一个排列好的数组输出所有不相同数字的个数
>用set
9. 算法题:输入{’‘abc’’,’‘bac’’,’‘cab’’,’‘atc’’,’‘act’’,’‘aac’’,’‘fbc’’};
输出二维数组每一行为相同字母拼接成的字符串{{’‘abc’’,’‘bac’’,’‘cab’’},{’‘atc’’,’‘act’’
},{’‘aac’’},{’‘fbc’’}}
利用排序,然后hashmap
public List<List<String>> groupAnagrams(String[] strs) {
//先对每个字符排序,然后相同的则为一组,需要map做映射
HashMap<String, List> map = new HashMap<>();
// String存储排序好的字符串,list存储原数组中的乱序元素,一一对应
for(String str:strs){
char[] ch = str.toCharArray();
Arrays.sort(ch);
String temp = String.valueOf(ch);
if(!map.containsKey(temp)) map.put(temp,new ArrayList());
map.get(temp).add(str);
}
return new ArrayList(map.values());
}
10. 按序增长的一个数组求出小于m的最大的数
11. 一个链表中删除一个值返回这个链表
public class LinkListUtli {
public static void deleteNode(LNode L,LNode p)
{
if(p==null||L==null) return;//如果p为空或单链表L为空不符合题意,直接返回
if(p.next != null) //如果p不是最后一个结点
{
LNode q=p.next;//q为p的后继结点
p.data=q.data;
p.next=q.next;//从单链表中删除结点q
}
else //如果p是最后一个结点
{
LNode pre=L;//用pre表示p的前驱结点
while(pre.next != null) //保持pre有后继结点
{
if(pre.next==p)//如果pre是p的前驱结点
{
pre.next=p.next;//将结点p从单链表中删除
}
}
}
}
}
12. 单例设计模式
详细介绍参考
>饿汉式:三步走,创建时直接实例化
懒汉式:修改上面
13. 快排
14. 排序算法及时间复杂度,写快排,快排是否稳定
参考
冒泡排序
快排
排序算法稳定性的理解
插入排序法
选择排序算法
归并排序
堆排序算法
15. 手撕代码:给一个字符串,找出最长的不重复的子串;
public int lengthOfLongestSubstring(String s) {
//滑动窗口法
HashMap<Character, Integer> map = new HashMap<>();
int left =0;//窗口的起始位置
int max =0; //窗口的最大长度,每次都要比较原窗口的最大长度和新窗口的长度,返回较大的那一个窗口
for (int i=0; i<s.length(); i++){
if(map.containsKey(s.charAt(i))){
// 如果map中有这个元素了,说明有重复元素,则需要移动窗口,到不包含这个重复元素,且需要注意窗口不能会退,所以需要max函数保证窗口不回退
left = Math.max(left, map.get(s.charAt(i))+1);
//如果没有max,类似abba这种情况,到最后一个元素a的时候,会使得left = 1,导致窗口回退,那样就包括了重复元素b
}
map.put(s.charAt(i), i);
max = Math.max(max, i-left+1);
}
return max;
}
16. 手撕代码:力扣41 困难难度,缺失的第一个整数
17. 对含有重复数字的数组去重并排序
去重参考
>可以用hashset么
18. 最大不重复子串,说思路,时间复杂度
19. 输入一个字符串,如“127.0.0.1”,判断是否是十进制表示的ipv4地址
20. 输入一个数组{1,2,3,2,2,4,4,1};输出数组中数字重复出现的最大次数,写出它的测试用例
>注意hashmap的遍历~以及包装类和基本数据类型的拆箱和装箱
21. 代码题:滑动窗口最大值
22. 给定一个数组,求和为s的所有子数组
23. 代码题:两个字符串求最长公共子串
24. 字符串变形。“Hello World"变形后就变成了"wORLD hELLO”
考前必刷
1. 反转链表
public ListNode ReverseList(ListNode head){
//需要三个节点之间得交换来反转链表
ListNode pre=null;
ListNode next=null;
while(head!=null){//head没到null就说明,head指向得不是最后一个节点,就要一直交换
next=head.next;//首先head指向得是当前节点,那么在最开始head是头节点得时候,pre=null没问题,但是next不应该是null,所以先给next赋值
head.next=pre;//head是当前节点,因为要反转链表,所以当前节点得next应该是前一个节点,也就是pre
pre=head; //前两行代码就算转换完毕了,接下来就是移动节点,所有得节点后移,pre就应该是刚才得当前节点
head=next; //next应该给当前节点赋值
}
return pre;
//最终返回得应该是pre,因为当head指向到最后一个节点的时候,head并不是null,循环执行以后,head指向了null,
// 在判断发现循环条件不满足,
// 但是此时的head指向的是null,pre才是最后一个节点,也就是现在的头节点,所以返回pre
}
2. 递归:斐波那切数列简单
3. 递归:两个链表合并递归
public ListNode Merge(ListNode list1,ListNode list2){
ListNode list=null;
if(list1==null){
return list2;
}else if(list2==null){
return list1;
}
if(list1.val<=list2.val){
// list.val=list1.val;
list=list1; // 上面那样写法不对,因为list最开始是null,所以不存在val,你要直接将list1给list,然后再修改next得值
list.next=Merge(list1.next,list2);
}
if(list1.val>list2.val){
// list.val=list2.val;
list=list2; // 上面那样写法不对,因为list最开始是null,所以不存在val,你要直接将list2给list,然后再修改next得值
list.next=Merge(list1,list2.next);
}
return list;
}
4. 栈
5. 二叉树打印
6.链表深拷贝
7. 回溯法:全排列
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> permute(int[] nums){
int len = nums.length;
if(len == 0) return res;
boolean used[] = new boolean[len];
List<Integer> path = new ArrayList<>();
dfs(nums,len,0,used,res,path);
return res;
}
public void dfs(int[] nums, int len, int depth, boolean[] used, List<List<Integer>> res, List<Integer> path){
if(depth == len){
res.add(new ArrayList<>(path)); //如果路径中的参数到了3个,就可以将这个路径存到最后的结果当中了
}
//开始回溯
for(int i =0; i<len; i++){
if(!used[i]){ //如果数字没用过,则将这个数字存入到path中,同时记得将used[i]修改为已使用
path.add(nums[i]);
used[i] = true;
dfs(nums, len, depth+1, used, res, path); //然后递归下一个位置的数字,同样使用这个方法
// 回溯法,一次的path记录完成以后,要回溯到上一个状态
// 注意:这里是状态重置,是从深层结点回到浅层结点的过程,代码在形式上和递归之前是对称的
used[i] = false;
path.remove(path.size()-1);
}
}
}
8. 最大连续子序列的和
public int FindGreatestSumOfSubArray(int[] array) {
int[] dp = new int[array.length];
dp[0]=array[0];
int result =dp[0];
for(int i = 1;i<array.length;i++){
dp[i] = Math.max(dp[i-1]+array[i],array[i]);
result = Math.max(dp[i],result);
}
return result;
}
9. 找到第一个只出现一次的字符
10. 求二叉树深度
11. 和为S的连续正数序列
12. 扑克牌构成顺子
13.链表环的入口节点
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead == null){
return null;
}
//先判断是否有环,
boolean isHuan=false; //默认不是环
ListNode slow=pHead;
ListNode fast=pHead;
while (fast!=null&&fast.next!=null){ //没环的链表,就是fast和fast.next最终会为null
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
isHuan=true;
break;
}
}
if(isHuan==false){
return null;
}else {
//找环的个数,从刚才的那个节点开始,再回到这个节点,就是环的个数了
int count = 1;
fast = fast.next;
while (fast != slow) {
count++;
fast=fast.next;
}
//然后让一个指针从头开始,另一个从count开始,相遇就是环的首节点
fast=pHead;
slow =pHead;
for(int i=0;i<count;i++){
fast=fast.next;
}
while (fast!=slow){
fast=fast.next;
slow=slow.next;
}
}
return fast;
}
14.矩阵中路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
代码
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(dfs(board, words, i, j, 0)) return true;
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k) {
if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
if(k == word.length - 1) return true;
char tmp = board[i][j];
board[i][j] = '/';
boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = tmp;
return res;
}
}
15. 只出现一次的数字
16. 股票最大利润
// https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/best-time-to-buy-and-sell-stock-ii-zhuan-hua-fa-ji/
//贪心算法,每次得到局部最优解
// 该算法仅可以用于计算,但计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。
public int maxProfit(int[] prices) {
int res =0;
for(int i =0; i<prices.length-1;i++){
int temp = prices[i+1]-prices[i];
if(temp > 0) res = res + temp;
}
return res;
}
17. 判断子串
判断子串
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
public boolean isSubsequence(String s, String t) {
//采用双指针法
int i =0;
int j=0;
while (i<s.length() && j<t.length()){
if(s.charAt(i) == t.charAt(j))
i++;
j++;
}
return i==s.length();
}
18. 打家劫舍
19. 有效括号
20.回文字符串
回文字符串
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
public boolean validPalindrome(String s) {
char[] arr = s.toCharArray();
int left = 0;
int right = arr.length-1;
while (left <= right){
if(arr[left] == arr[right]){
left++;
right--;
}else{
boolean flag1=true;
boolean flag2=true;
//然后分两种情况去计算[left+1,right]和[left,right-1],为了不轻易改变left和right,我们多引入变量i和j
for(int i=left+1,j=right;i<=j;i++,j--){
if(arr[i] != arr[j]){
flag1 = false;
break;
}
}
for(int i=left,j=right-1;i<=j;i++,j--){
if(arr[i] != arr[j]){
flag2 = false;
break;
}
}
return flag1 || flag2;
}
}
return true;
}
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
public int countSubstrings(String s) {
int res =0;
for(int center=0; center<s.length()*2-1; center++){
int left = center/2;
int right = left + center%2;
while (left >=0 && right<s.length() && s.charAt(left) == s.charAt(right)){//这个得用while,因为while才能一直往外扩散
// if (left >=0 && right<s.length() && s.charAt(left) == s.charAt(right)){
res++;
left--;
right++;
}
}
return res;
}
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
public String longestPalindrome(String s) {
// 在回文子串那题的基础上修改
String res = "";
for(int center = 0; center<2*s.length()-1; center++){
int left = center/2;
int right = left + center%2;
while (left >=0 && right<s.length() && s.charAt(left) == s.charAt(right)){ //记住这一定得是while,因为要不断的 移动的,直到不是回文才不移动
String temp = s.substring(left,right+1); // 把每次的回文子字符串先暂时保存,如果最常则应当返回这个
if(temp.length() > res.length())
res = temp;
left--;
right++;
}
}
return res;
}
21. 无重复最长子串
22.最短无序连续子数组
输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
public int findUnsortedSubarray(int[] nums) {
int[] snums = nums.clone();
Arrays.sort(snums);
int start = nums.length-1;
int end = 0;
for(int i=0;i<nums.length;i++){
if(snums[i] != nums[i]){
start = Math.min(start, i);
end = Math.max(end, i);
}
}
return end-start >0? end-start+1:0;
}
23. 动态规划:最小路径和
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
public int minPathSum(int[][] grid) {
//https://leetcode-cn.com/problems/minimum-path-sum/solution/zui-xiao-lu-jing-he-by-leetcode/
int[][] dp = new int[grid.length][grid[0].length];
for(int i=grid.length-1;i>=0;i--){
for(int j=grid[0].length-1;j>=0;j--){
if(i == grid.length-1 && j!=grid[0].length-1)
dp[i][j] = grid[i][j] + dp[i][j+1];
else if(i!=grid.length-1 && j==grid[0].length-1)
dp[i][j] = grid[i][j] + dp[i+1][j];
else if(i!=grid.length-1 && j!=grid[0].length-1)
dp[i][j] = grid[i][j] + Math.min(dp[i+1][j],dp[i][j+1]);
else
dp[i][j] = grid[i][j];
}
}
return dp[0][0];
}
动态规划:不同路径
问总共有多少条不同的路径?
public int uniquePaths(int m, int n) {
//https://blog.youkuaiyun.com/weixin_44550963/article/details/107282087
//f(i,j) = f(i,j-1) + f(i-1,j)
int[][] dp = new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(i==0 || j==0) dp[i][j] = 1; //第一行或者第一列,只有一种横着走 或者竖着走一种可能
else dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}