4. LintCode 二分法题目(二)

4. LintCode 二分法题目(二)

3. LintCode 二分法题目(一)

LintCode 458:目标最后位置

https://www.lintcode.com/problem/last-position-of-target

描述

给一个升序数组,找到target最后一次出现的位置,如果没出现过返回-1。

样例
输入:nums = [1,2,2,4,5,5], target = 2
输出:2

输入:nums = [1,2,2,4,5,5], target = 6
输出:-1
解题思路

简单二分,只是当mid=target时,还要继续找下去。

AC代码
public int lastPosition(int[] nums, int target) {
    // 临界条件直接返回
    if (nums == null || nums.length < 1) {
        return -1;
    }
    
    int left = 0, right = nums.length - 1;
    while (left + 1 < right) {
        int mid = (left + right) / 2;
		// 当nums[mid] == target的时候并不停止二分,left右移至mid位置继续二分查找
        if (nums[mid] == target) {
            left = mid;
        } else if (nums[mid] < target) {
            // 数字小于target,left右移
            left = mid + 1;
        } else {
            // 数字大于target,right左移
            right = mid - 1;
        }

    }
	// right值等于target
    if (nums[right] == target) {
        return right;
    }
	// left值等于target
    if (nums[left] == target) {
        return left;
    }

    return -1;        
}

LintCode 585:山脉序列中的最大值

https://www.lintcode.com/problem/maximum-number-in-mountain-sequence

描述

n 个整数的山脉数组,即先增后减的序列,找到山顶(最大值)
数组严格递增,严格递减

样例
输入: nums = [1, 2, 4, 8, 6, 3] 
输出: 8
    
输入: nums = [10, 9, 8, 7], 
输出: 10
解题思路

和前面的寻找峰值题一样一个思路。
对于位置P:

  • nums[p - 1] < nums[p] > nums[p + 1]:P点为山顶最大值
  • nums[p - 1] < nums[p] < nums[p + 1]:山顶在P点右侧
  • nums[p - 1] > nums[p] > nums[p + 1]:山顶在P点左侧

由此二分找到答案。

AC代码
public int mountainSequence(int[] nums) {
    // 极端情况直接判断
    if (nums.length == 1 || nums[0] > nums[1]) {
        return nums[0];
    }

    if (nums[nums.length - 1] > nums[nums.length - 2]) {
        return nums[nums.length];
    }

    int left = 0, right = nums.length - 1;
    while (left + 1 < right) {
        int mid = left + (right - left) / 2;
		// 找到山顶
        if (nums[mid] > nums[mid + 1] && nums[mid] > nums[mid - 1]) {
            return nums[mid];
        }
		// 山顶在右侧
        if (nums[mid] > nums[mid - 1]) {
            left = mid + 1;
        } else {
            // 山顶在左侧
            right = mid - 1;
        }
    }
	// 判断山顶
    if (nums[right] > nums[right + 1] && nums[right] > nums[right - 1]) {
        return nums[right];
    }
    if (nums[left] > nums[left + 1] && nums[left] > nums[left - 1]) {
        return nums[left];
    }
    return 0;
}

LintCode:447:在大数组中查找

https://www.lintcode.com/problem/search-in-a-big-sorted-array

描述

给一个按照升序排序的非负整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数(或者C++里是ArrayReader->get(k)),并且你也没有办法得知这个数组有多大。
找到给出的整数target第一次出现的位置。你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。
如果找不到target,返回-1。
如果你访问了一个不可访问的下标(比如越界),ArrayReader 会返回2,147,483,647

样例
输入: [1, 3, 6, 9, 21, ...], target = 3
输出: 1
    
输入: [1, 3, 6, 9, 21, ...], target = 4
输出: -1
挑战

O(logn)的时间复杂度,n是target第一次出现的下标。

解题思路

首先第一想法就是二分,但粗看找不到有边界,挑战也是只用logk的时间复杂度。所以思考怎么找到右边界。
将初始有边界定位1,然后不断*2,直到大于target,这所用的时间复杂度是O(logn),满足题目要求,找到有边界后,再简单二分即可。

AC代码
public int searchBigSortedArray(ArrayReader reader, int target) {
    if (reader.get(0) == target) {
        return 0;
    }
	
    // 寻找右边界
    int right = 1;
    while (reader.get(right) < target) {
        right *= 2;
    }
    // 左边界=右边界/2
    int left = right / 2;
    while (left + 1 < right) {
        int mid = left + (right - left) / 2;
        // == target,right指针左移
        if (reader.get(mid) == target) {
            right = mid;
        } else if (reader.get(mid) < target) {
            // 小于target,left指针右移
            left = mid + 1;
        } else {
            // 大于target,right指针右移
            right = mid - 1;
        }
    }
    if (reader.get(left) == target) {
        return left;
    }
    if (reader.get(right) == target) {
        return right;
    }
	// 没有答案,返回-1    
    return -1;
}

LintCode 62:搜索旋转排序数组

https://www.lintcode.com/problem/search-in-rotated-sorted-array

描述

假设有一个排序的按未知的旋转轴旋转的数组(比如,0 1 2 4 5 6 7 可能成为4 5 6 7 0 1 2)。给定一个目标值进行搜索,如果在数组中找到目标值返回数组中的索引位置,否则返回-1。你可以假设数组中不存在重复的元素。

样例
输入: [4, 5, 1, 2, 3] and target=1, 
输出: 2.
    
输入: [4, 5, 1, 2, 3] and target=0, 
输出: -1.    
解题思路
解法一

前面做过查找旋转排序数组的最小值:https://www.lintcode.com/problem/find-minimum-in-rotated-sorted-array
可以先用前面的方法找到最小值,这样就把数组分成了两段有序排序的数组,在包含target的区间内在进行一次二分即可。都不包含返回-1。

解法二

第一个方法需要两次二分,还是有点繁琐。思考只用一次二分的方法。
对于位置k:

  • A[k] = targe,直接返回位置k
  • 否则要么 A[left] < A[k],A[k] > A[right] 左半段是有序的
    • 若target在A[left]和A[k]中,就在left和k的区间中简单二分查找,右指针左移
    • 不在,左指针右移
  • 要么 A[k] < A[right],A[k] < A[left] 右半段是有序的
    • 若target在A[k]和A[right]中,就在k和right的区间中简单二分查找,左指针右移
    • 不在,右指针左移
AC代码(解法二)
public int search(int[] A, int target) {
	// 边界条件
    if (A.length == 0) {
        return -1;
    }
    
    int left = 0, right = A.length - 1;
    while (left + 1 < right) {        
        int mid = left + (right - left) / 2;
        // 找到target直接返回
        if (A[mid] == target) {
            return mid;
        }
        // left和mid有序上升,判断target是否在这半段
        if (A[mid] > A[left]) {
            if (A[left] <= target && target < A[mid]) {
                // 在这半段,右指针左移
                right = mid - 1;
            } else {
                // 不在这半段,左指针右移
                left = mid + 1;
            }
        } else {
            // mid和right区间有序上升,判断target是否在这半段
            if (A[mid] < target && target <= A[right]) {
                // 在这半段,左指针右移
                left = mid + 1;
            } else {
                // 不在这半段,右指针左移
                right = mid - 1;
            }
        }
    }
    if (A[left] == target) {
        return left;
    }
    if (A[right] == target) {
        return right;
    }
    // 没有找到返回-1
    return -1;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值