面试经典150题【131-140】

文章目录

面试经典150题【131-140】

123.买卖股票的最佳时机III

buy1代表第一次买,sell1代表第一次卖。

buy2代表第二次买,sell2代表第二次卖。

每个值的最大值/迭代值,都与上一个商业操作有关。

同一天买入卖出不影响,因为利润为0

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        int buy1=-prices[0],sell1=0;
        int buy2=-prices[0],sell2=0;
        for(int i=0;i<prices.length;i++){
            buy1=Math.max(buy1,-prices[i]);
            sell1=Math.max(sell1,buy1+prices[i]);
            buy2=Math.max(buy2,sell1-prices[i]);
            sell2=Math.max(sell2,buy2+prices[i]);
        }
        return sell2;
    }
}

188.买卖股票的最佳时机IV

java 复制代码
class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices.length == 0) {
            return 0;
        }

        int n = prices.length;
        //k = Math.min(k, n / 2);
        int[] buy = new int[k];
        int[] sell = new int[k];
        Arrays.fill(buy,-prices[0]);
        Arrays.fill(sell,0);
       
        for (int i = 0; i < n; ++i) {
            buy[0]=Math.max(buy[0],-prices[i]);
            sell[0]=Math.max(sell[0],buy[0]+prices[i]);
            for (int j = 1; j < k; ++j) {
                buy[j] = Math.max(buy[j], sell[j-1] - prices[i]);
                sell[j] = Math.max(sell[j], buy[j] + prices[i]);
            }
        }

        return Arrays.stream(sell).max().getAsInt();
    }
}

无需考虑同一天的买卖和k值与n/2的问题,直接模版梭哈。

在一轮i的循环中,对无数个buy和sell赋值即可

注意要对buy和sell初始化。

二分查找的板子:

小于等于的

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length - 1; // 注意
        while(left <= right) { // 注意
            int mid = (left + right) / 2; // 注意
            if(nums[mid] == target) { // 注意
                // 相关逻辑
            } else if(nums[mid] < target) {
                left = mid + 1; // 注意
            } else {
                right = mid - 1; // 注意
            }
        }
        // 相关返回值
        return ?;
    }
}

小于的

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length; // 注意
        while(left < right) { // 注意
            int mid = (left + right) / 2; // 注意
            if(nums[mid] == target) {
                // 相关逻辑
            } else if(nums[mid] < target) {
                left = mid + 1; // 注意
            } else {
                right = mid; // 注意
            }
        }
        // 相关返回值
        return ?;
    }
}

然后我们看一下Java内置的Arrays.binarySearch的方法:

java 复制代码
    private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                     int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

我们可以发现他就是用的小于等于的模版,只不过最后的return的时候有些特别。

35.搜索插入位置

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target)
                return mid;
            if (nums[mid] > target)
                right = mid - 1;
            if (nums[mid] < target)
                left = mid + 1;
        }
        return left;

    }
}

74.搜索二维矩阵


先往下搜索找到具体的行,再往右搜索找具体的值。

但是这个题咔咔错

首先用找到target左边的元素的方法,然后再用标准二分查找有没有这个元素即可。

java 复制代码
class Solution {
    public  boolean searchMatrix(int[][] matrix, int target) {
        int len1 = matrix.length, len2 = matrix[0].length;
        int index = binarySearchFirstColumn(matrix,target);
        if(index<0) return false;
        if (matrix[index][0] == target)
            return true;
        int[] matrixRow = new int[len2];
        for (int i = 0; i < len2; i++) {
            matrixRow[i] = matrix[index][i];
        }
        int ans = Arrays.binarySearch(matrixRow, target);
        if (ans < 0)
            return false;
        else
            return true;
    }

    public int binarySearchFirstColumn(int[][] matrix, int target) {
        int low = 0, high = matrix.length - 1;
        while (low <= high) {
            int mid = (high - low ) / 2 + low;
            if(matrix[mid][0]==target) return mid;
            if (matrix[mid][0] < target) {
                low = mid+1 ;
            } else {
                high = mid - 1;
            }
        }

        return high<0? 0:high;
    }


}

尤其是这个找第一列,最后的return很特别。是判断high是否小于0

这种是寻找小于等于目标值的最右边的索引。return high<0? 0:high

162.寻找峰值

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

    }
}

对于1,4,5,6,2,7,8,9,10来说,

只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值。2<7.则最后答案是10.

33.搜索旋转排序数组

nums是升序的。

java 复制代码
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length - 1, mid = 0;
        while (left <= right) {
            mid = (left + right) / 2;
            if (target == nums[mid])
                return mid;
            // mid在左区间里
            if (nums[mid] >= nums[left]) {

                if (target >= nums[left] && target < nums[mid]) {
                    // target在mid的左边
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }

            } else {
                if (target > nums[mid] && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        return -1;

    }
}

旋转以后,肯定是4567123,nums[0]一定比后面的右段大

先判断nums[mid]和nums[0]的关系,判断是在左段(数值比较大的一段)还是右段(数值比较小的一段)

然后比较target和nums[mid]的关系,判断他在mid的左边还是右边。

34.在排序数组中查找元素的第一个和最后一个位置

java 复制代码
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int first = -1;
        int last = -1;
        // 找第一个等于target的位置
        while (left <= right) {
            int middle = (left + right) / 2;
            if (nums[middle] == target) {
                first = middle;
                right = middle - 1; // 重点
            } else if (nums[middle] > target) {
                right = middle - 1;
            } else {
                left = middle + 1;
            }
        }
        // 最后一个等于target的位置
        left = 0;
        right = nums.length - 1;
        while (left <= right) {
            int middle = (left + right) / 2;
            if (nums[middle] == target) {
                last = middle;
                left = middle + 1; // 重点
            } else if (nums[middle] > target) {
                right = middle - 1;
            } else {
                left = middle + 1;
            }
        }
        return new int[] { first, last };
    }
}

以找最左边的first为例,即使找到了,也要再做一次right = mid-1;继续遍历。直到不等于(即越界)

153.寻找旋转排序数组中的最小值

一定要理解旋转数组是一个数字大的前半段和一个数字小的后半段。

如果nums[mid]>nums[right] ,说明mid在左半段,则left=mid+1

右边不能-1,万一右半段只有一个数字。

当然如果nums[left]<nums[mid],这说明nums[left]就是最小值了。

java 复制代码
class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[right]) {          
                left = mid + 1;
            } else {                                
                right = mid;
            }
        }
        return nums[left];


    }
}

4.寻找两个正序数组的中位数

当 [ [a1],[b1,b2,b3] | [a2,...an],[b4,...bn] ]

我们只需要比较 b3 和 a2 的关系的大小,就可以知道这种分法是不是准确的!

例如:我们令:

nums1 = [-1,1,3,5,7,9]

nums2 = [2,4,6,8,10,12,14,16]

当 m1 = 4,m2 = 3 ,它的中位数就是median = (num1[m1] + num2[m2])/2

时间复杂度:O(log(min(m,n)))

java 复制代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n1 = nums1.length;
        int n2 = nums2.length;
        if (n1>n2)
            return findMedianSortedArrays(nums2, nums1);
        //对于6+8而言,k=7 0-7和8-15. 对于6+7而言,K还是7,0-6,7,8-14
        int k = (n1 + n2 + 1)/2;
        int left = 0;
        //这个right也设置的很巧妙。
        int right = n1;
        //这里的left和right都是对于短的nums1而言的。
        while(left < right){
            int m1 = left +(right - left)/2;
            int m2 = k - m1;
            if (nums1[m1] < nums2[m2-1])
                left = m1 + 1;
            else
                right = m1;
        }
        //这样对于7+7的来说,m1=7,m2=0
        //对于 6+7而言,K=7,m1=6,m2=1
        int m1=left,m2=k-left;
        int c1 = Math.max(m1 <= 0 ? Integer.MIN_VALUE : nums1[m1-1],
                         m2 <= 0 ? Integer.MIN_VALUE : nums2[m2-1]);
        if ((n1 + n2) % 2 == 1)
            return c1;
        int c2 = Math.min( m1 >= n1 ? Integer.MAX_VALUE :nums1[m1],
                         m2 >= n2 ? Integer.MAX_VALUE : nums2[m2]);
        return (c1 + c2) * 0.5;

    }
}

感觉二分这边就很恶心。上一道题就需要设置right=n1才行。然而大部分题是设置right=n1-1;

需要控制好很多边界变量的设置,即使会了板子也不一定能搞出来。

相关推荐
吾日三省吾码3 小时前
JVM 性能调优
java
LNTON羚通3 小时前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
弗拉唐4 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi774 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3435 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀5 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
哭泣的眼泪4085 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
蓝黑20205 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深5 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++