面试经典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;

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

相关推荐
魔道不误砍柴功35 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_23435 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
pianmian136 分钟前
python数据结构基础(7)
数据结构·算法
闲晨38 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
好奇龙猫3 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
sp_fyf_20243 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express