【大厂高频算法题】字节后端春招(2)

【动规】1、零钱兑换

在这里插入图片描述

分析:用回溯用的不好的话很容易超时,最好是用动态规划。

class Solution {
    public int coinChange(int[] coins, int amount) {
        // 自底向上的动态规划
        if(coins.length == 0){
            return -1;
        }

        // memo[n]的值: 表示的凑成总金额为n所需的最少的硬币个数
        int[] memo = new int[amount+1];
        memo[0] = 0;
        for(int i = 1; i <= amount;i++){
            int min = Integer.MAX_VALUE;
            for(int j = 0;j < coins.length;j++){
            //兑换的条件:加上这枚硬币的值后amount>=0,这枚
                if(i - coins[j] >= 0 && memo[i-coins[j]] < min){
                    min = memo[i-coins[j]] + 1;//拿上该硬币时的最小硬币数
                }
            }
            // memo[i] = (min == Integer.MAX_VALUE ? Integer.MAX_VALUE : min);
            memo[i] = min;
        }

        return memo[amount] == Integer.MAX_VALUE ? -1 : memo[amount];
    }
}

【栈】2、有效的括号

在这里插入图片描述

class Solution{
    public boolean isValid(String s){
        LinkedList<Character> stack = new LinkedList<>();
        for(char c:s.toCharArray()){
            if(c =='[') stack.push(']');
            else if(c == '(') stack.push(')');
            else if(c == '{') stack.push('}');
            else if(stack.isEmpty() || c!= stack.pop()) return false;
        }
        return stack.isEmpty();
    }
}

括号的生成

【排序】3、排序链表

给定头结点,按升序排列并返回排序后的链表。

方法1:优先队列;
方法2:放入数组,排序后重建链表;
方法3:归并排序
方法4:快排

class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null){return head;}
        PriorityQueue<ListNode> q = new PriorityQueue<>(new Comparator<ListNode>(){
            public int compare(ListNode o1,ListNode o2){
                return o1.val-o2.val;
            }
        });
        ListNode cur = head;
        while(cur!=null){
            q.add(cur);
            cur = cur.next;
        }
        //System.out.println(q.size());
        ListNode newHead = new ListNode(0);
        ListNode curr = newHead;
        while(!q.isEmpty()){
            ListNode node = q.poll();
            node.next = null;
            curr.next = node;
            curr = node;
        }
        return newHead.next;
    }
}

【回溯】4、组合总和

在这里插入图片描述

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        if(candidates.length <= 0){return res;}
        Arrays.sort(candidates);
        dfs(candidates,target,new ArrayList<Integer>(),res,0);
        return res;
    }
    private void dfs(int[] candidates,int target,ArrayList<Integer> tmp,List<List<Integer>> res,int start){
        //递归出口
        if(target == 0){
        	//判重
            Collections.sort(tmp);
            if(!res.contains(tmp)){
                res.add(new ArrayList<>(tmp));
            }           
            return;
        }
        if(target < 0){return;}
        for(int i = start;i < candidates.length;i++){
            //剪枝           
            tmp.add(candidates[i]);
            dfs(candidates,target-candidates[i],tmp,res,i);
            tmp.remove(tmp.size()-1);           
        }
    }
}
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        if(target < candidates[0]){return res;}
        dfs(candidates,target,new ArrayList<Integer>(),res,0);
        return res;
    }
    private void dfs(int[] candidates,int target,ArrayList<Integer> tmp,List<List<Integer>> res,int start){
        //递归结束条件
        if(target == 0){          
            res.add(new ArrayList<>(tmp));         
            return;
        }
        if(target < 0){return;}
        for(int i = start;i < candidates.length;i++){
            if(target > 0){
                tmp.add(candidates[i]);
                dfs(candidates,target-candidates[i],tmp,res,i);
                tmp.remove(tmp.size()-1);
            }else{//判重
                break;
            }
        }
    }
}

【动规】5、打家劫舍

class Solution {
    public int rob(int[] nums) {
        if(nums.length == 0){return 0;}
        if(nums.length == 1){return nums[1];}
        if(nums.length == 2){return Math.max(nums[0],nums[1]);}
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0],nums[1]);
        for(int i = 2;i < nums.length;i++){
            dp[i] = Math.max(nums[i]+dp[i-2],dp[i-1]);
        }
        return dp[nums.length-1];
    }
}

【非数字相加】6、字符串相加

class Solution {
    public String addStrings(String num1, String num2) {
        int c = 0;
        StringBuilder res = new StringBuilder();
        int i = num1.length()-1,j = num2.length()-1;
        while(i >= 0 || j >= 0 || c!=0){
            int n1 = i >=0? num1.charAt(i) - '0' : 0;
            int n2 = j >=0? num2.charAt(j) - '0' : 0;
            res.append((n1+n2+c)%10);
            c = (n1+n2+c)/10;
            i--;
            j--;
        }
        return res.reverse().toString();
    }
}

链表相加

【二分查找】7、寻找峰值

class Solution {
    public int findPeakElement(int[] nums) {
        int left = 0,right = nums.length-1;
        while(left < right){
            int mid = (left+right)/2;
            if(nums[mid] > nums[mid+1]){
                right = mid;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
}

【双指针】8、合并两个有序数组

从两个数组末尾开始比较,谁大谁就放在最后
这道题做了很多次,贴一下之前的代码和这次做的升级代码

//升级版
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int k = nums1.length-1;
        int i = m-1,j = n-1;
        while(k >=0){
            int num1 = i>=0?nums1[i]:Integer.MIN_VALUE;
            int num2 = j>=0?nums2[j]:Integer.MIN_VALUE;
            nums1[k] = Math.max(num1,num2);
            if(num1 > num2){i--;}
            else{j--;}
            k--;
        }
    }
}
//之前的代码
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(n ==0){return;}
        if(m == 0){
            for(int i = 0;i < n;i++){
                nums1[i] = nums2[i];
            }
            return;
        }
        //从后边开始比较,谁大谁就到最后去
        int k = nums1.length-1;
        int i = m-1,j = n-1;
        while(i >= 0 && j >=0 && k >=0){
            if(nums1[i] >= nums2[j]){
                nums1[k--] = nums1[i--];
            }else{
                nums1[k--] = nums2[j--];
            }
        }          
        while(j>=0){
            nums1[k--] = nums2[j--];
        }      
    }
}

【栈】9、反转每对括号间的子串

class Solution {
    public String reverseParentheses(String s) {
        StringBuilder sb = new StringBuilder();
        char[] arr = s.toCharArray();
        Stack<Integer> stack = new Stack<>();
        for(int i = 0;i < arr.length;i++){
            if(arr[i] == '(')
                stack.push(i);
            if(arr[i] == ')'){
                reverse(arr,stack.pop()+1,i-1);
                // for(char ch:arr){
                //     System.out.print(ch);
                // }
                // System.out.println();
            }
        }
        for(int i = 0;i < arr.length;i++){
            if(arr[i] !=')' && arr[i] !='(')
                sb.append(arr[i]);
        }
        return sb.toString();
    }
    public void reverse(char[] arr,int left,int right){
        while(right > left){
            char tmp = arr[left];
            arr[left] = arr[right];
            arr[right] = tmp;
            right--;
            left++;
        }
    }
}

【dfs】10、单词搜索

感觉和岛屿数量那个题目有点像

class Solution {
    private boolean find;
    public boolean exist(char[][] board, String word) {
        if(board == null) return false;
        int m = board.length,n = board[0].length;
        boolean[][] visited = new boolean[m][n];
        find = false;

        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                backtracking(i,j,board,word,visited,0);
            }
        }
        return find;
    }
    public void backtracking(int i,int j,char[][] board,String word,boolean[][] visited,int pos){
        //超出边界、已经访问过、已找到目标单词、棋盘格中当前字符已经和目标字符不一致了
        if(i < 0|| i>=board.length || j<0 || j>=board[0].length || visited[i][j] || find || board[i][j]!=word.charAt(pos)){return;}
        if(pos == word.length()-1){
            find = true;
            return;
        }
        visited[i][j] = true;
        backtracking(i+1,j,board,word,visited,pos+1);
        backtracking(i-1,j,board,word,visited,pos+1);
        backtracking(i,j+1,board,word,visited,pos+1);
        backtracking(i,j-1,board,word,visited,pos+1);
        visited[i][j] = false;
    }
}

【位运算】11、汉明距离

在这里插入图片描述

class Solution {
    public int hammingDistance(int x, int y) {
        int count = 0;
        while(x!=0 || y!=0){
            if((x&1) != (y&1)){
                count++;
            }
            x>>=1;
            y>>=1;
        }
        while(x!=0){
            x>>=1;
            count++;
        }
        while(y!=0){
            y>>=1;
            count++;
        }
        return count;
    }
}

【智力题】12、每日温度

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] res = new int[temperatures.length];
        res[temperatures.length-1] = 0;
        for(int i = temperatures.length-2;i >= 0;i--){
            int index = i+1;
            while(res[index]!=0 && temperatures[i] >= temperatures[index]){
                if(res[index]!=0){
                    index += res[index];
                }else{
                    res[i] = index-i;
                }
            }
            if(temperatures[i] >= temperatures[index]){
                res[i] = 0;
            }else{
                res[i] = index-i;
            }  
        }
        return res;
    }
}
/**
 * 根据题意,从最后一天推到第一天,这样会简单很多。因为最后一天显然不会再有升高的可能,结果直接为0。
 * 再看倒数第二天的温度,如果比倒数第一天低,那么答案显然为1,如果比倒数第一天高,又因为倒数第一天
 * 对应的结果为0,即表示之后不会再升高,所以倒数第二天的结果也应该为0。
 * 自此我们容易观察出规律,要求出第i天对应的结果,只需要知道第i+1天对应的结果就可以:
 * - 若T[i] < T[i+1],那么res[i]=1;
 * - 若T[i] > T[i+1]
 *   - res[i+1]=0,那么res[i]=0;
 *   - res[i+1]!=0,那就比较T[i]和T[i+1+res[i+1]](即将第i天的温度与比第i+1天大的那天的温度进行比较)
 */
public int[] dailyTemperatures(int[] T) {
    int[] res = new int[T.length];
    res[T.length - 1] = 0;
    for (int i = T.length - 2; i >= 0; i--) {
        for (int j = i + 1; j < T.length; j += res[j]) {
            if (T[i] < T[j]) {
                res[i] = j - i;
                break;
            } else if (res[j] == 0) {
                res[i] = 0;
                break;
            }
        }
    }
    return res;
}

【动规】13、买卖股票的最佳时机(购入抛售一次)

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length <= 1)
            return 0;
        int min = prices[0], max = 0;
        for(int i = 1; i < prices.length; i++) {
            max = Math.max(max, prices[i] - min);
            min = Math.min(min, prices[i]);
        }
        return max;
    }
}

买卖股票的最佳时机(可购入、抛售多次)

只要今天比昨天大,就抛出!
例如:1 2 3,最佳是第一天购入,第三天抛出(3-1),但是第二天抛出,第二天再买入,然后第三天抛出——(2-1)+(3-2)
两种情况的收益是一样的。
再比如:1 7 3,直接就是第一天买入,第三天抛出

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length == 0){return 0;}
        int ans = 0;
        for(int i = 1;i < prices.length;i++){
            if(prices[i] > prices[i-1]){
                ans+=prices[i] - prices[i-1];
            }
        }
        return ans;
    }
}

买卖股票的最佳时机(含手续费)

买卖股票的最佳时机(最多两笔交易)

14、旋转图像

矩阵顺时针旋转90°
方法一:先转置,再镜像对称。转置就是i和j互换,镜像就是找一个对称轴,前后翻转
在这里插入图片描述

class Solution {
    public void rotate(int[][] matrix) {
    	//转置
        for(int i = 0;i < matrix.length;i++){
            for(int j = 0;j < i;j++){
                int tmp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = tmp;
            }
        }
        //镜像
        for(int i = 0;i < matrix.length;i++){
            for(int j = 0;j < matrix.length/2;j++){
                int tmp = matrix[i][matrix.length-j-1];
                matrix[i][matrix.length-j-1] = matrix[i][j];
                matrix[i][j] = tmp;
            }
        }
    }
}

15、用用 Rand7() 实现 Rand10()

/**
 * The rand7() API is already defined in the parent class SolBase.
 * public int rand7();
 * @return a random integer in the range 1 to 7
 */
class Solution extends SolBase {
    public int rand10() {
        int a = rand7();
        int b = rand7();
        if(a > 4 && b < 4){
            return rand10();
        }else{
            return (a+b)%10+1;
        }
    }
}

16、两数相加

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1 == null){return l2;}
        if(l2 == null){return l1;}
        int c = 0;
        ListNode head = new ListNode(0);
        ListNode cur = head;
        while(l1!=null || l2!=null || c!=0){
            int num1 = l1==null?0:l1.val;
            int num2 = l2==null?0:l2.val;
            //切记:先取余填入节点,再计算c!!
            ListNode node = new ListNode((num1+num2+c)%10);
            cur.next = node;
            cur = node;
            c = (num1+num2+c)/10;
            if(l1!=null){l1 = l1.next;}
            if(l2!=null){l2 = l2.next;}
        }
        return head.next;
    }
}

17、爬楼梯

class Solution {
    public int climbStairs(int n) {
        if(n == 1){return 1;}
        int[] dp = new int[n+1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i=2;i<n+1;i++){
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

18、删除链表的倒数第N个节点

在这里插入图片描述

19、二叉树展开为链表

方法一:倒序的先序遍历(右左中)。聪明人用递归是真没错。
方法二:先序遍历+标记,遍历过的节点标记了。

class Solution {
    TreeNode last= null;
    public void flatten(TreeNode root) {
        if(root == null)return;
        flatten(root.right);
        flatten(root.left);
        root.right = last;
        root.left = null;
        last = root;
    }
}
//虽然很慢,但是很好理解
class Solution {
    List<TreeNode> list = new ArrayList<>();
    public void flatten(TreeNode root) {
        if(root == null){return;}
        preOrder(root);
        //System.out.println(list.size());
        root = list.get(0);
        for(int i = 1;i < list.size();i++){
            root.left = null;
            root.right = list.get(i);
            root = root.right;
        }
        root.right = null;
    }
    public void preOrder(TreeNode root){
        if(root == null){return;}
        if(!list.contains(root)){
            list.add(root);
        }
        preOrder(root.left);
        preOrder(root.right);
    }
}

20、搜索二维矩阵II

在这里插入图片描述
从右上角开始搜索可以避免判断边界

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int i = 0,j = matrix[0].length-1;
        while(i < matrix.length && j >=0){
            if(matrix[i][j] > target){
                j--;
            }else if(matrix[i][j] < target){
                i++;
            }else{
                return true;
            }
        }
        return false;
    }
}

21、删除排序链表中的重复元素II

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null){return head;}
        ListNode next = head.next;
        if(head.val == next.val){
            while(next!=null && head.val == next.val){
                next = next.next;
            }
            //这里的意思是,暂且将next作为head,然后递归判断,假如到了null还是重复的,那么null就设置为head
            head = deleteDuplicates(next);
        }else{
            //这里head因为没有重复,所以能够纳入
            head.next = deleteDuplicates(next);
        }
        return head;
    }
}

22、二叉搜索树中第K小的元素

中序遍历,然后到K停止。
一次AC,奈斯

class Solution {
    int count = 0;
    int res = 0;
    public int kthSmallest(TreeNode root, int k) {
        inOrder(root,k);
        return res;
    }
    private void inOrder(TreeNode root,int k){
        if(root == null){
            return;
        }
        inOrder(root.left,k);
        count++;
        if(count == k){
            res = root.val;
            return;
        }
        inOrder(root.right,k);
    }
}

23、平衡二叉树

定义:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
递归判断每个节点的左右子树高度,假如绝对值>1,那么返回false

今天状态真不戳,连续好几题都是一次AC

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null){return true;}
        if(Math.abs(depth(root.left)-depth(root.right)) > 1){
            return false;
        }
        return isBalanced(root.left) && isBalanced(root.right);
    }
    private int depth(TreeNode root){
        if(root == null){return 0;}
        int left = depth(root.left);
        int right = depth(root.right);
        return Math.max(left,right)+1;
    }
}

24、最小路径和

动态规划

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
        //把左边界填满
        for(int i = 1;i < m;i++){
            dp[i][0] = dp[i-1][0]+grid[i][0];
        }
        //把上边界填满
        for(int j = 1;j < n;j++){
            dp[0][j] = dp[0][j-1]+grid[0][j];
        }
        for(int i = 1;i < m;i++){
            for(int j = 1;j < n;j++){
                dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
            } 
        }
        return dp[m-1][n-1];
    }
}

25、二叉树的最大宽度

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private int maxW = 0;
    public int widthOfBinaryTree(TreeNode root) {
        dfs(root, 1, 1, new ArrayList<>());
        return maxW;
    }
    //left记录每一层的最左边结点的index,递归每个结点,用他们的index减去每一层最左边结点的index
    private void dfs(TreeNode r,int level,int index,List<Integer> left){
        if(r == null) return;
        System.out.println(level + " "+left.size()+" "+index+" "+maxW);
        if(level > left.size()) left.add(index);
        maxW = Math.max(maxW,index-left.get(level-1)+1);
        dfs(r.left,level+1,index*2,left);
        dfs(r.right,level+1,index*2+1,left);
        System.out.println("递归后"+level + " "+left.size()+" "+index+" "+maxW);
    }
}

26、盛最多水的容器

接雨水
在这里插入图片描述

class Solution {
    public int maxArea(int[] a) {
        if(a.length <=1){return 0;}
        int max = 0;
        for(int i = 0,j = a.length-1;i<j;){
            int minHeight = a[i] < a[j]?a[i++]:a[j--];
            max = Math.max(max,(j-i+1)*minHeight);
        }
        return max;
    }
}

27、下一个更大元素III

力扣官方题解


public class Solution {
    public int nextGreaterElement(int n) {
        char[] a = ("" + n).toCharArray();
        int i = a.length - 2;
        while (i >= 0 && a[i + 1] <= a[i]) {
            i--;
        }
        if (i < 0)
            return -1;
        int j = a.length - 1;
        while (j >= 0 && a[j] <= a[i]) {
            j--;
        }
        swap(a, i, j);
        reverse(a, i + 1);
        try {
            return Integer.parseInt(new String(a));
        } catch (Exception e) {
            return -1;
        }
    }
    private void reverse(char[] a, int start) {
        int i = start, j = a.length - 1;
        while (i < j) {
            swap(a, i, j);
            i++;
            j--;
        }
    }
    private void swap(char[] a, int i, int j) {
        char temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值