牛客Leetcode高频题解(下)

买卖股票的最好时机(一)

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

买卖股票的最好时机(二)

public int maxProfit(int[] prices) {
    if (prices == null || prices.length < 2)
        return 0;
    int length = prices.length;
    //初始条件
    int hold = -prices[0];  //持有股票
    int noHold = 0;         //没持股票
    for (int i = 1; i < length; i++) {
        //递推公式转化的
        noHold = Math.max(noHold, hold + prices[i]);
        hold = Math.max(hold, noHold - prices[i]);
    }
    //最后一天肯定是手里没有股票的时候利润才会最大,
    //所以这里返回的是noHold
    return noHold;
}

买卖股票的最好时机(三)

最多可以买入卖出2次,那实际上相当于它的状态多了几个,对于每天有到此为止的最大收益和持股情况两个状态,持股情况有了5种变化,我们用:
dp[i][0]表示到第i天为止没有买过股票的最大收益
dp[i][1]表示到第i天为止买过一次股票还没有卖出的最大收益
dp[i][2]表示到第i天为止买过一次也卖出过一次股票的最大收益
dp[i][3]表示到第i天为止买过两次只卖出过一次股票的最大收益
dp[i][4]表示到第i天为止买过两次同时也买出过两次股票的最大收益

import java.util.*;
public class Solution {
    public int maxProfit (int[] prices) {
        int n = prices.length;
        int[][] dp = new int[n][5];
        //初始化dp为最小
        Arrays.fill(dp[0], -10000); 
        //第0天不持有状态
        dp[0][0] = 0; 
        //第0天持有股票
        dp[0][1] = -prices[0]; 
        //状态转移
        for(int i = 1; i < n; i++){ 
            dp[i][0] = dp[i - 1][0]; 
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }
        //选取最大值,可以只操作一次
        return Math.max(dp[n - 1][2],Math.max(0, dp[n - 1][4])); 
    }
}

分糖果问题

public int candy (int[] arr) {
    int n=arr.length;
    if(n<=1)
        return n;
    int[] nums = new int[n];
    //初始化
    for(int i = 0; i < n; i++)
        nums[i] = 1;
    //从左到右遍历
    for(int i = 1; i < arr.length; i++){ 
        //如果右边在递增,每次增加一个
        if(arr[i] > arr[i - 1]) 
            nums[i] = nums[i - 1] + 1; 
    }
    //记录总糖果数
    int res = nums[arr.length - 1]; 
    //从右到左遍历
    for(int i = arr.length - 2; i >= 0; i--){ 
         //如果左边更大但是糖果数更小
        if(arr[i] > arr[i + 1] && nums[i] <= nums[i + 1])
            nums[i] = nums[i + 1] + 1; 
        //累加和
        res += nums[i]; 
    }
    return res;
}

主持人调度(二)

import java.util.*;
public class Solution {
    public int minmumNumberOfHost (int n, int[][] startEnd) {
        int[] start = new int[n];
        int[] end = new int[n];
        //分别得到活动起始时间
        for(int i = 0; i < n; i++){
            start[i] = startEnd[i][0];
            end[i] = startEnd[i][1];
        }
        //单独排序
        Arrays.sort(start, 0, start.length);
        Arrays.sort(end, 0, end.length);
        int res = 0;
        int j = 0;
        for(int i = 0; i < n; i++){
            //新开始的节目大于上一轮结束的时间,主持人不变
            if(start[i] >= end[j]) 
                j++;  
            else
                //主持人增加
                res++;  
        }
        return res;
    }
}

矩阵的最小路径和

public int minPathSum (int[][] matrix) {
    int m = matrix.length;
    int n = matrix[0].length;
    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[0][0] = matrix[0][0];
            } else if (i == 0) {
                //第一行只能从左边走过来
                dp[i][j] = matrix[i][j] + dp[i][j - 1];
            } else if (j == 0) {
                //第一列只能从上面走下来
                dp[i][j] = matrix[i][j] + dp[i - 1][j];
            } else {
                //递推公式,取从上面走下来和从左边走过来的最小值+当前坐标的值
                dp[i][j] = matrix[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return dp[m - 1][n - 1];
}

把数字翻译成字符串(二)

import java.util.*;
public class Solution {
    public int solve (String nums) {
        //排除0
        if (nums.equals("0"))
            return 0;
        //排除只有一种可能的10 和 20
        if (nums == "10" || nums == "20")
            return 1;
       
        //当0的前面不是1或2时,无法译码,0种
        for (int i = 1; i < nums.length(); i++) {
            if (nums.charAt(i) == '0')
                if (nums.charAt(i - 1) != '1' && nums.charAt(i - 1) != '2')
                    return 0;
        }
        
        int[] dp = new int[nums.length() + 1];
        //辅助数组初始化为1
        Arrays.fill(dp, 1);
        for (int i = 2; i <= nums.length(); i++) {
            //在11-19,21-26之间的情况
            if ((nums.charAt(i - 2) == '1' && nums.charAt(i - 1) != '0') ||
                    (nums.charAt(i - 2) == '2' && nums.charAt(i - 1) > '0' && nums.charAt(i - 1) < '7'))
                dp[i] = dp[i - 1] + dp[i - 2];
            else
                dp[i] = dp[i - 1];
        }
        return dp[nums.length()];
    }
}

兑换零钱(一)

import java.util.*;
public class Solution {
    public int minMoney (int[] arr, int aim) {
        //小于1的都返回0
        if (aim < 1)  return 0;
        
        int[] dp = new int[aim + 1];
        //dp[i]表示凑齐i元最少需要多少货币数
        Arrays.fill(dp, aim + 1);
        dp[0] = 0;
       
        //遍历1-aim元
        for (int i = 1; i <= aim; i++) {
            //每种面值的货币都要枚举
            for (int j = 0; j < arr.length; j++) {
                //如果面值不超过要凑的钱才能用
                if (arr[j] <= i)
                    //维护最小值
                    dp[i] = Math.min(dp[i], dp[i - arr[j]] + 1);
            }
        }
        //如果最终答案大于aim代表无解
        return dp[aim] > aim ? -1 : dp[aim];
    }
}

最长公共子序列(二)

public static String LCS(String s1, String s2) {
    int row = s1.length();
    int column = s2.length();
    // `dp[i][j]`表示从左到右,当处理到s1的第i个元素和s2的第j个元素时的公共子序列长度
    // 默认初始化为0,因此base case的 i == 0 和 j == 0 的情况不需要手动初始化
    int[][] dp = new int[row + 1][column + 1];
    // 状态转移
    for (int i = 1; i < row + 1; i++) {
        for (int j = 1; j < column + 1; j++) {
            // 字符相等,则通过前一个状态+1获得当前状态
            if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                // 字符不相等,则通过两个前面的情况的最大值获得当前最大公共子序列长度
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
   
    //目前dp记录了s1和s2的最长子序列的长度
    if (dp[row][column] == 0) 
        return "-1";
   
    // lcs用来储存结果数组
    char[] lcs = new char[dp[row][column]];
    // 因为要对dp倒着查看,所以lcs也倒着插入,cur表示当前插入的位置
    int cur = lcs.length - 1;
    // 通过双指针,遍历s1和s2字符串
    while (true) {
        // 相等则添加结果,两个指针左移
        if (s1.charAt(row - 1) == s2.charAt(column - 1)) {
            //记录字符
            lcs[cur--] = s1.charAt(row - 1);
            if (cur < 0) {
                return new String(lcs);
            }
            row--;
            column--;
        } else {
            // 不相等,则根据dp保存的状态,进行指针移动
            if (dp[row - 1][column] > dp[row][column - 1]) {
                // 忽略掉s1[row-1]
                row--;
            } else {
                // 忽略掉s2[column-1]
                column--;
            }
        }
    }
}

二分查找-I

public int search (int[] nums, int target) {
    int l = 0;
    int r = nums.length - 1;
    //从数组首尾开始,直到二者相遇
    while(l <= r){ 
        //每次检查中点的值
        int m = (l + r) / 2; 
        if(nums[m] == target)
            return m;
        //进入左的区间
        if(nums[m] > target) 
            r = m - 1;
        //进入右区间
        else 
            l = m + 1;
    }
    //未找到
    return -1; 
}

二维数组中的查找

(1)从左下角找

public boolean Find(int target, int [][] array) {
    int rows = array.length;
    if(rows == 0){
        return false;
    }
    int cols = array[0].length;
    if(cols == 0){
        return false;
    }
    // 左下
    int row = rows-1;
    int col = 0;
    while(row>=0 && col<cols){
        if(array[row][col] < target){
            col++;
        }else if(array[row][col] > target){
            row--;
        }else{
            return true;
        }
    }
    return false;
}

(2)从右上角找

public boolean Find(int target, int [][] array) {
    int rows = array.length;
    if(rows == 0){
        return false;
    }
    int cols = array[0].length;
    if(cols == 0){
        return false;
    }
    // 右上
    int row = 0;   //注意
    int col = cols-1;    //注意
    while(row<rows && col>=0){    //注意
        if(array[row][col] < target){
            row++;        //注意
        }else if(array[row][col] > target){
            col--;      //注意
        }else{
            return true;
        }
    }
    return false;
}

寻找峰值

public int findPeakElement (int[] nums) {
    int left = 0;
    int 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 right; 
}

数组中的逆序对

public class Solution {
    public int mod = 1000000007;
    public int mergeSort(int left, int right, int [] data, int [] temp){
        //停止划分
        if(left >= right)    
            return 0;
        //取中间
        int mid = (left + right) / 2; 
        //左右划分合并
        int res = mergeSort(left, mid, data, temp) + mergeSort(mid + 1, right, data, temp); 
        //防止溢出
        res %= mod;  
        int i = left, j = mid + 1;
        for(int k = left; k <= right; k++)
            temp[k] = data[k];
        for(int k = left; k <= right; k++){
            if(i == mid + 1)
                data[k] = temp[j++];
            else if(j == right + 1 || temp[i] <= temp[j])
                data[k] = temp[i++];
            //左边比右边大,答案增加
            else{ 
                data[k] = temp[j++];
                // 统计逆序对
                res += mid - i + 1; 
            }
        }
        return res % mod;
    }
    
    public int InversePairs(int [] array) {
        int n = array.length;
        int[] res = new int[n];
        return mergeSort(0, n - 1, array, res);
    }
}

旋转数组

public class Solution {
    public int[] solve (int n, int m, int[] a) {
        //取余,因为每次长度为n的旋转数组相当于没有变化
        m = m % n; 
        //第一次逆转全部数组元素
        reverse(a, 0, n - 1); 
        //第二次只逆转开头m个
        reverse(a, 0, m - 1); 
        //第三次只逆转结尾m个
        reverse(a, m, n - 1); 
        return a;
    }
    //反转函数
    public void reverse(int[] nums, int start, int end){ 
        while(start < end){
            swap(nums, start++, end--);
        }
    }
    //交换函数
    public void swap(int[] nums, int a, int b){ 
        int temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }
}

旋转数组的最小数字

(1)二分法

public int minNumberInRotateArray(int [] array) {
    int left = 0;
    int right = array.length - 1;
    while(left < right){
        int mid = (left + right) / 2;
        //最小的数字在mid右边
        if(array[mid] > array[right])
            left = mid + 1;
        //无法判断,一个一个试
        else if(array[mid] == array[right])
            right--;
        //最小数字要么是mid要么在mid左边
        else
            right = mid;
    }
    return array[left];
}

(2)遍历查找

public int minNumberInRotateArray(int [] array) {
    //数组一定有元素
    int res = array[0]; 
    //遍历数组
    for(int i = 1; i < array.length; i++) {
        //每次维护最小值
        res = Math.min(res, array[i]); 
    }
    return res;
}

比较版本号

public int compare(String version1, String version2) {
    String s1[] = version1.split("\\.");
    String s2[] = version2.split("\\.");

    int n1 = s1.length;
    int n2 = s2.length;
    int n = Math.max(n1, n2);
    int k1[] = new int[n];
    for (int i = 0; i < s1.length; i++) {
        k1[i] = Integer.valueOf(s1[i]);
    }

    int k2[] = new int[n];
    for (int i = 0; i < s2.length; i++) {
        k2[i] = Integer.valueOf(s2[i]);
    }

    for (int i = 0; i < n; i++) {
        if (k1[i] > k2[i]) {
            return 1;
        } else if (k1[i] < k2[i]) {
            return -1;
        }
    }
    return 0;
}

螺旋矩阵

public ArrayList<Integer> spiralOrder(int[][] matrix) {
    ArrayList<Integer> list = new ArrayList<>();

    if(matrix.length == 0) {
        return list;
    }

    int left = 0; 
    int right = matrix[0].length - 1;
    int top = 0;
    int bottom = matrix.length - 1; 
    int x = 0;


    while(true) {
        for(int i = left; i <= right; i++) {  //从左到右
           list.add(matrix[top][i]) ; 
        }

        if(++top > bottom){
            break;
        } 
        for(int i = top; i <= bottom; i++){
             list.add( matrix[i][right]);    //从上到下
        } 

        if(left > --right){
            break;
        } 
        for(int i = right; i >= left; i--){
              list.add(matrix[bottom][i]); //从右到左
        } 

        if(top > --bottom){
            break;
        }
        for(int i = bottom; i >= top; i--){
             list.add(matrix[i][left]);   //从下到上
        } 

        if(++left > right){
           break;
        } 
    }
    return list;
}

顺时针旋转矩阵

(1) 矩阵转置

import java.util.*;
public class Solution {
    public int[][] rotateMatrix(int[][] mat, int n) {
        int length = mat.length;
        //矩阵转置
        for(int i = 0; i < length; ++i){
            for(int j = 0; j < i; ++j){
                //交换上三角与下三角对应的元素
                int temp = mat[i][j];
                mat[i][j] = mat[j][i];
                mat[j][i] = temp;
            }
        }
        //每行翻转
        for (int i = 0; i < length; i++) {
            for (int j = 0; j < length/2; j++){
                int temp = mat[i][j];
                mat[i][j] = mat[i][length - j - 1];
                mat[i][length - j - 1] = temp;
            }
        }
        return mat;
    }
}

(2)先上下交换,在对角线交换

 public int[][] rotateMatrix(int[][] matrix, int n) {
    int length = matrix.length;
    //先上下交换
    for (int i = 0; i < length / 2; i++) {
        int temp[] = matrix[i];
        matrix[i] = matrix[length - i - 1];
        matrix[length - i - 1] = temp;
    }
    //在按照对角线交换
    for (int i = 0; i < length; ++i) {
        for (int j = i + 1; j < length; ++j) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }
    return matrix;
}

你可能感兴趣的:(1024程序员节)