0
点赞
收藏
分享

微信扫一扫

第22章-路由协议概述

东林梁 04-05 20:31 阅读 1

文章目录

面试经典150题【131-140】

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

在这里插入图片描述
buy1代表第一次买,sell1代表第一次卖。
buy2代表第二次买,sell2代表第二次卖。
每个值的最大值/迭代值,都与上一个商业操作有关。
同一天买入卖出不影响,因为利润为0

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

在这里插入图片描述

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初始化。

二分查找的板子:

小于等于的

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 ?;
    }
}

小于的

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的方法:

    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.搜索插入位置

在这里插入图片描述

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左边的元素的方法,然后再用标准二分查找有没有这个元素即可。

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.寻找峰值

在这里插入图片描述

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是升序的。
在这里插入图片描述

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.在排序数组中查找元素的第一个和最后一个位置

在这里插入图片描述

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]就是最小值了。

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)))

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;
需要控制好很多边界变量的设置,即使会了板子也不一定能搞出来。

举报

相关推荐

0 条评论