leetcode刷题第15天-34、33、74、2013

第十五天

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

给定一个按照升序排列的整数数组nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值target,返回[-1, -1]

进阶:

你可以设计并实现时间复杂度为O(log n) 的算法解决此问题吗?

方法

使用两次二分查找,第一次查找出target第一次出现的位置,第二次查找出target最后一次出现的位置。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int l1 = 0;
        int r1 = nums.length - 1;
        int mid1;
        boolean flag = false;
        while (l1 <= r1){
            mid1 = (l1 + r1) / 2;
            if (nums[mid1] == target) flag = true;
            if (nums[mid1] >= target) r1 = mid1 - 1;
            else l1 = mid1 + 1;
        }
        if (!flag) return new int[]{-1, -1};
        int l2 = l1;
        int r2 = nums.length - 1;
        int mid2;
        while (l2 <= r2){
            mid2 = (l2 + r2) / 2;
            if (nums[mid2] == target) l2 = mid2 + 1;
            else r2 = mid2 - 1;
        }
        return new int[]{l1, r2};
    }
}

33 搜索旋转排序数组

整数数组nums按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标k(0 <= k < nums.length)上进行了 旋转,使数组变为[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组nums和一个整数target ,如果nums中存在这个目标值target,则返回它的下标,否则返回-1

方法一

先使用二分查找的变体找出递增顺序发生终端的位置,具体的算法如下:

  • 定义cutLcutRmid=(cutL+cutR)/2
  • 判断一下nums[mid]和相邻元素的关系
  • 如果nums[mid]<num[mid+1]nums[mid]>=nums[0],说明当前mid的位置位于前一段有序区间,此时将cutL=mid+1
  • 如果发现nums[mid]>mid[mid+1],此时说明mid的位置就是递增顺序中断发生的地方,直接记录下来返回即可
  • 同理,如果nums[mid-1]<nums[mid]并且nums[mid] <=nums[last],令cutR=mid-1
  • 如果nums[i-1]>nums[i],记录终端位置返回

当找到了中断发生的位置之后,只需要判断一下target在哪个区间,然后在对应区间上使用二分搜索即可。

class Solution {
    public int search(int[] nums, int target) {
        int cutL = 0, cutR = nums.length - 1;
        if (nums[cutL] <= nums[cutR]) return binarySearch(nums, 0, cutR, target);
        while (true){
            int mid = (cutL + cutR) / 2;
            if (nums[mid] >= nums[cutL])
                if (nums[mid] < nums[mid + 1]) cutL = mid + 1;
                else{
                    cutL = mid;
                    cutR = mid + 1;
                    break;
                }
            else if(nums[mid] <= nums[cutR])
                if (nums[mid] > nums[mid-1]) cutR = mid - 1;
                else{
                    cutR = mid;
                    cutL = mid - 1;
                    break;
                }
        }
        return target >= nums[0] ? binarySearch(nums, 0, cutL, target) : binarySearch(nums, cutR, nums.length - 1, target);
    }

    public static int binarySearch(int[] nums, int l , int r, int target){
        while (l <= r){
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            else if (nums[mid] > target) r = mid - 1;
            else l = mid + 1;
        }
        return -1;
    }
}
方法二

注意到,虽然整个数组并不是递增的,但是实际上是在两个不同的区间上递增的。注意到如果将整个数组一分为二,那么一定有一半的区间是有序的,而另一半的区间是部分有序的。

所以,我们依照二分查找的思路,设计如下类二分查找的算法:

  • 如果nums[mid]>=nums[0],说明在mid左半部分,该段区间是有序的。如果target在该区间内,那么直接在该区间进行二分查找即可
  • 如果nums[mid]<=nums[last],说明在mid的右半部分是有序的,如果target在该区间内部,直接对该区间进行二分查找即可。
  • 如果target不在上述区间内,通过调整l=mid+1r=mid-1,最终让target落在某个有序区间即可。
class Solution {
    public int search(int[] nums, int target) {
        int l = 0, r = nums.length - 1;
        while (l <= r){
            int mid = (l + r) / 2;
            if (target == nums[mid]) return mid;
            if (nums[mid] >= nums[l]){
                if (target < nums[mid] && target >= nums[l]) return binarySearch(nums, l, mid, target);
                else l = mid + 1;
            }
            else if(nums[mid] <= nums[r]){
                if (target > nums[mid] && target <= nums[r]) return binarySearch(nums, mid, r, target);
                else r = mid - 1;
            }
        }
        return -1;
    }

    public static int binarySearch(int[] nums, int l , int r, int target){
        while (l <= r){
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            else if (nums[mid] > target) r = mid - 1;
            else l = mid + 1;
        }
        return -1;
    }
}

74 搜索二位矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。

  • 每行的第一个整数大于前一行的最后一个整数。

方法

进行两次二分查找,第一次查找目标值在哪一行,第二次查找目标值在哪个具体位置。

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if (target < matrix[0][0] || target > matrix[matrix.length - 1][matrix[0].length - 1]) return false;
        int layerL = 0, layerR = matrix.length - 1;
        while (layerL <= layerR){
            int mid = (layerL + layerR) / 2;
            if (matrix[mid][0] == target) return true;
            if (matrix[mid][0] > target) layerR = mid - 1;
            else layerL = mid + 1;
        }
        int l = 0, r = matrix[0].length - 1;
        while (l <= r){
            int mid = (l + r) / 2;
            if (matrix[layerR][mid] == target) return true;
            if (matrix[layerR][mid] > target) r = mid - 1;
            else l = mid + 1;
        }
        return false;
    }
}

2013 检测正方形

给你一个在 X-Y 平面上的点构成的数据流。设计一个满足下述要求的算法:

  • 添加 一个在数据流中的新点到某个数据结构中。可以添加 重复 的点,并会视作不同的点进行处理。

  • 给你一个查询点,请你从数据结构中选出三个点,使这三个点和查询点一同构成一个 面积为正 的 轴对齐正方形 ,统计 满足该要求的方案数目。

轴对齐正方形 是一个正方形,除四条边长度相同外,还满足每条边都与 x-轴 或 y-轴 平行或垂直。

实现 DetectSquares 类:

DetectSquares()使用空数据结构初始化对象
void add(int[] point)向数据结构添加一个新的点 point = [x, y]
int count(int[] point)统计按上述方式与点 point = [x, y]共同构造 轴对齐正方形 的方案数。

方法

重新定义一个Point类,建立一个Point->Count的映射,当有一个新的点到来时,遍历一遍整个映射表,找到能够构成正方形的所有点。然后将对应的其他三个点的count值相乘,最后将结果累加就是所有的方案数。

class DetectSquares {

    private Map<Point, Integer> pointMap;
    public DetectSquares() {
        pointMap = new HashMap<Point, Integer>();
    }
    
    public void add(int[] point) {
        Point p = new Point(point);
        if (pointMap.containsKey(p)) pointMap.put(p, pointMap.get(p) + 1);
        else pointMap.put(p, 1);
    }

    public int count(int[] point) {
        int x = point[0];
        int y = point[1];
        int total = 0;
        for (Point key : pointMap.keySet()){
            if (key.getX() == x && key.getY() != y){
                int length = Math.abs(key.getY() - y);
                int left = pointMap.getOrDefault(new Point(x - length, y), 0);
                int top = pointMap.getOrDefault(new Point(x, y + length), 0);
                int right = pointMap.getOrDefault(new Point(x + length, y), 0);
                int bottom = pointMap.getOrDefault(new Point(x, y - length), 0);
                int leftTop = pointMap.getOrDefault(new Point(x - length, y + length), 0);
                int rightTop = pointMap.getOrDefault(new Point(x + length, y + length), 0);
                int rightBottom = pointMap.getOrDefault(new Point(x + length, y - length), 0);
                int leftBottom = pointMap.getOrDefault(new Point(x - length, y - length), 0);
                total = total + ((key.getY() - y) > 0 ? (left * top * leftTop + right * top * rightTop): (left * bottom * leftBottom + right * bottom * rightBottom)); 
            }
        }
        return total;
    }
}

class Point{
    private int x;
    private int y;
    
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
    
    public Point(int[] a){
        x = a[0];
        y = a[1];
    }
    @Override
    public boolean equals(Object p){
        return x == ((Point) p).x && ((Point) p).y == y;
    }
    //加入到map中的类 需要重写equals和hashcode方法
    @Override
    public int hashCode(){
        return x * 1000000 + y;
    }
    
    @Override
    public String toString(){
        return "x=" + x + ",y=" + y;
    }

    public int getY() {
        return y;
    }

    public int getX() {
        return x;
    }
}

/**
 * Your DetectSquares object will be instantiated and called as such:
 * DetectSquares obj = new DetectSquares();
 * obj.add(point);
 * int param_2 = obj.count(point);
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值