2022年4月10日 作者 zeroheart

算法基础第一天

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

这题回顾一下二分法,要找最左和最右的边界,在mid = target之后,继续移动mid的位置即可。

/**
 34. 在排序数组中查找元素的第一个和最后一个位置
 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

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

 进阶:

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


 示例 1:

 输入:nums = [5,7,7,8,8,10], target = 8
 输出:[3,4]
 示例 2:
 
 输入:nums = [5,7,7,8,8,10], target = 6
 输出:[-1,-1]
 示例 3:

 输入:nums = [], target = 0
 输出:[-1,-1]


 提示:

 0 <= nums.length <= 10^5
 -10^9 <= nums[i] <= 10^9
 nums 是一个非递减数组
 -10^9 <= target <= 10^9

 * @author zeroheart
 *
 */
public class Question34 {

    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[]{-1,-1};
        res[0] = binarySearch(nums, target, 1);
        res[1] = binarySearch(nums, target, 0);
        return res;
    }

    public int binarySearch(int[] nums, int target, int left){
        int res = -1;
        int low = 0; int high = nums.length - 1;
        int mid = 0;

        while (low <= high){
            mid = low + (high-low) / 2;
            if(nums[mid]<target){
                low = mid + 1;
            }else if(nums[mid]>target){
                high = mid - 1;
            }else {
                // 虽然已经找到了,不过不一定是最左边的或者最右边的
                res = mid;
                if(left == 1){
                    // 如果要找的是最左的,右边的指针继续减小。
                    high = mid - 1;
                }else {
                    low = mid + 1;
                }
            }
        }

        return res;
    }

    public int[] searchRange1(int[] nums, int target) {
        // 通过了,但是没用到算法
        int[] res = new int[]{-1,-1};

        for(int i = 0; i<nums.length; i++){
            if(nums[i] == target && res[0] == -1){
                res[0] = i;
            }

            if(nums[i] == target && res[0] != -1 && i == nums.length - 1){
                res[1] = i;
                break;
            }

            if(nums[i] > target && res[0] != -1){
                res[1] = i-1;
                break;
            }
        }

        if(res[0] == -1){
            res[1] = -1;
        }

        if(res[1] < res[0]){
            res[1] = res[0];
        }

        return res;
    }

}

33. 搜索旋转排序数组

旋转排序数组,nums[0]是一个分界点。这题也是利用二分法,确定是在高的一侧还是低的一层之后,在比较target的大小来移动指针。


/**
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 。



示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:

输入:nums = [1], target = 0
输出:-1


提示:

1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4


进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?

* @author zeroheart
*
*/
public class Question33 {
public int search(int[] nums, int target) {
int res = -1;
int low = 0;
int high = nums.length - 1;
int mid = 0;

while (low <= high) {
mid = low + (high - low) / 2;

if (nums[mid] == target) {
return mid;
}

// nums[0] 是一个分界点

if (nums[0] <= nums[mid]) {
// mid 出现在高的一侧

// 看target的情况,移动指针
if (nums[0] <= target && target < nums[mid]) {
// 注意这里的<=
high = mid - 1;
} else {
low = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[nums.length - 1]) {
// 注意这里的<=
low = mid + 1;
} else {
high = mid - 1;
}
}
}

return res;
}

}

74. 搜索二维矩阵

这也是二分法的变种,二位数组的坐标要转一下。注意二分的high = len -1;


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

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。


示例 1:


输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:


输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false


提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104

* @author zeroheart
*
*/
public class Question74 {

public boolean searchMatrix(int[][] matrix, int target) {
// 实际就是二分法,就是要做一次坐标转化
int m = matrix.length;
int n = matrix[0].length;

int low = 0; int high = m * n - 1;
while (low <= high){
int mid = low + (high - low) / 2;
int tmp = matrix[mid/n][mid%n];
if(tmp == target) return true;
if(tmp < target) {
low = mid + 1;
}else {
high = mid - 1;
}
}
return false;
}


}