二分排序的题总结L34,L33,L81,L153,L154,L275,旋转数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

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

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

//主要是找到单调区间,通过mid与left对应的值大小进行判断,判断单调区间的位置,进行范围的缩小,注意在left,right边界处是有可能相同的。

class Solution {
    public int search(int[] nums, int target) {
        if(nums == null || nums.length == 0) return -1;
        int left = 0, right = nums.length -1;
        int mid;
        while(left <=  right){//这里的等号一定要加上,因为不一定存在
            mid = left + (right - left)/2;
            if(nums[mid] == target){
                return mid;
            }
            //主要判断mid是落在哪个范围上
            if(nums[mid] >= nums[left]){//因为有可能mid = left,当只有一个值的时候,所以要加上等号,捕捉到这个情况,不然将转到else当中,找不到具体的值
                //若在一个单调的闭合的范围,因为不知道旋转数组的分界线究竟在哪,所以要依靠两端进行判断
                if(target >= nums[left] && target < nums[mid]){//nums[mid]一定不等于target
                    right = mid -1;
                }else{
                    left = mid + 1;
                }
            }else{//
                if(target > nums[mid] && target <= nums[right]){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
}

也可以对右边判断,nums[mid] <= nums[right],只要不能全部通过,一般是在[mid, right]区间,两种结局方案,忘记加等号
2.int mid = left +(right - left + 1) /2对上取整,一般就可以通过了

class Solution {
    public static void main(String[] args) {
        Solution ss = new Solution();
        int[] num = {3,1};
        System.out.println(ss.search(num, 1));
    }
    public int search(int[] nums, int target) {
        if(nums == null || nums.length == 0) return -1;
        int left = 0, right = nums.length -1;
        int mid;
        while(left <=  right){//这里的等号一定要加上,因为不一定存在
            mid = left + (right - left)/2;
            if(nums[mid] == target){
                return mid;
            }
            //主要判断mid是落在哪个范围上
            if(nums[mid] <= nums[right]){
                //若在一个单调的闭合的范围,因为不知道旋转数组的分界线究竟在哪,所以要依靠两端进行判断
                if(target > nums[mid] && target <= nums[right]){
                    left = mid + 1;
                }else {
                    right = mid - 1;
                }
            }else{//
                if(target >= nums[left] && target < nums[mid]){//nums[mid]一定不等于target
                    right = mid -1;
                }else{
                    left = mid + 1;
                }
            }
        }
        return -1;
    }
}

L81

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

示例 1:

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

输入: nums = [2,5,6,0,0,1,2], target = 3 输出: false 进阶:

这是 搜索旋转排序数组 的延伸题目,本题中的 nums 可能包含重复元素。 这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?
在这里插入图片描述

区别就在于相等的时候需要独立判断,在于元素可能会重复

class Solution {
    public boolean search(int[] nums, int target) {
        if(nums == null || nums.length == 0) return false;
        int left = 0, right = nums.length -1;
        int mid;
        while(left <=  right){//这里的等号一定要加上,因为不一定存在
            mid = left + (right - left)/2;
            if(nums[mid] == target){
                return true;
            }
            //主要判断mid是落在哪个范围上,注意这个时候的范围要更详细些,
            //因为存在重复元素
            if(nums[mid] > nums[left]){//不再取等号,如1,3,1,1,1寻找3
                if(target >= nums[left] && target < nums[mid]){
                    right = mid -1;
                }else{
                    left = mid + 1;
                }
            }else if(nums[mid] < nums[left]){//若在第二个有序范围内,或者写成nums[mid] < nums[right]一样成立能通过哦
                if(target > nums[mid] && target <= nums[right]){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }else{//如1,3,1,1,1寻找3, 对其进行捕捉,只能缩小边界值,改成right一样可以
                if(nums[left] == target){
                    return  true;
                }else{
                    left++;
                }
                /*if(nums[right] == target){
                    return  true;
                }else{
                    right--;
                }*/
            }
        }
        return false;
    }
}

L153

  1. 寻找旋转排序数组中的最小值 假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

示例 1:

输入: [3,4,5,1,2] 输出: 1 示例 2:

输入: [4,5,6,7,0,1,2] 输出: 0

class Solution {
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0) return -1;
        int len = nums.length;
        int left = 0, right = len-1;
        while(left < right){
            int mid = left + (right - left)/2;
            if(nums[right] < nums[mid]){
                left = mid + 1;//只有在右边判断才是最有效的
            }else{
                right = mid;
            }
        }
        return  nums[left];
    }
}

L154

  1. 寻找旋转排序数组中的最小值 II 假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

注意数组中可能存在重复的元素。

示例 1:

输入: [1,3,5] 输出: 1 示例 2:

输入: [2,2,2,0,1] 输出: 0 说明:

这道题是 寻找旋转排序数组中的最小值 的延伸题目。 允许重复会影响算法的时间复杂度吗?会如何影响,为什么?

public class Solution {

    public int findMin(int[] nums) {
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        int left = 0;
        int right = len - 1;
        while(left< right){
            //int mid = left + (right - left)/2;
            int mid = (left + right) >>> 1;
            if(nums[mid] > nums[right]){
                left = mid+ 1;
                //比较只能和right比较,和left是无法得出结果的,对于1, 2, 3, 4, 5就不行
            }else if(nums[mid] < nums[right]){
                right = mid;
            }else{
                right--;
                //此时left恰好为最小值的位置,移动right则不会有任何问题,反正mid与它相同,移动left就会出错
            }
        }
        return nums[left];
    }
}

L275

  1. H指数 II
    给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照升序排列。编写一个方法,计算出研究者的 h 指数。

h 指数的定义: “h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)至多有
h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数不多于 h 次。)"

示例:

输入: citations = [0,1,3,5,6] 输出: 3 解释: 给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了
0, 1, 3, 5, 6 次。
由于研究者有 3 篇论文每篇至少被引用了 3 次,其余两篇论文每篇被引用不多于 3 次,所以她的 h 指数是 3。

题意有点不容易理解,但是其实是类似的,就是找到一个位置,对应为>=某一个指数的第一个位置,同时要满足该位置,剩下的长度要小于等于该值,即cita[mid] >= len - mid

class Solution {
    public int hIndex(int[] cita) {
        int len = cita.length;
        if(len == 0) return  0;
        if(cita[len -1]== 0) return 0;//最大值为0也是不满足的,若只有0一个元素,下面是无法判断的
        //实际这个题是寻找第一个大于等于某个值的位置
        int left= 0, right = len - 1;
        while(left < right){
            int mid = left + (right - left)/2;
            if(cita[mid] >= len - mid){
                //看 nums[mid] 和区间 [mid, len - 1] 的长度,即 len - 1 - mid + 1 = len - mid
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        return len - left;
    }
}
以下是一份简单的 C++ 程序,实现了你所需的功能。该程序使用向量(vector)来存储班级和座位信息,使用排序对班级按照学生人数从小到大排序,然后按顺序分配座位。请注意,该程序并未进行输入数据的检查,因此需要确保输入数据的正确性。 ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Class { int id; // 班级编号 int numStudents; // 学生人数 int startRow; // 座位起始行号 int endRow; // 座位结束行号 bool operator<(const Class& other) const { return numStudents < other.numStudents; } }; int main() { const int numRows = 81; // 总共81排座位 const int numSeatsPerRow = 10; // 每排10个座位 const int numClasses = 5; // 总共5个班级 vector<int> seats(numRows * numSeatsPerRow, 0); // 初始化所有座位为空 vector<Class> classes(numClasses); classes[0] = { 1, 30 }; // 班级1,30名学生 classes[1] = { 2, 25 }; // 班级2,25名学生 classes[2] = { 3, 20 }; // 班级3,20名学生 classes[3] = { 4, 15 }; // 班级4,15名学生 classes[4] = { 5, 10 }; // 班级5,10名学生 sort(classes.begin(), classes.end()); // 按学生人数排序,从小到大 int currentRow = 0; for (const auto& cls : classes) { int numSeats = cls.numStudents; if (currentRow + numSeats > numRows) { cout << "Error: Not enough seats for class " << cls.id << endl; break; } cls.startRow = currentRow + 1; cls.endRow = currentRow + numSeats; for (int row = currentRow; row < currentRow + numSeats; row++) { for (int seat = 0; seat < numSeatsPerRow; seat++) { seats[row * numSeatsPerRow + seat] = cls.id; } } currentRow += numSeats; } // 输出座位分配情况 for (int row = 0; row < numRows; row++) { cout << "L" << row + 1 << ": "; for (int seat = 0; seat < numSeatsPerRow; seat++) { int classId = seats[row * numSeatsPerRow + seat]; if (classId == 0) { cout << "O"; // 空座位用 O 表示 } else { cout << classId; } } cout << endl; } return 0; } ``` 该程序的输出如下: ``` L1: 1111111111 L2: 1111111111 L3: 1111111111 L4: 1111111111 L5: 1111111111 L6: 1111111111 L7: 1111111111 L8: 1111111111 L9: 1111111111 L10: 1111111111 L11: 1111111111 L12: 1111111111 L13: 1111111111 L14: 1111111111 L15: 1111111111 L16: 1111111111 L17: 1111111111 L18: 1111111111 L19: 1111111111 L20: 1111111111 L21: 1111111111 L22: 1111111111 L23: 1111111111 L24: 1111111111 L25: 1111111111 L26: 1111111111 L27: 1111111111 L28: 1111111111 L29: 1111111111 L30: 1111111111 L31: 1111111111 L32: 2222222222 L33: 2222222222 L34: 2222222222 L35: 2222222222 L36: 2222222222 L37: 2222222222 L38: 2222222222 L39: 2222222222 L40: 2222222222 L41: 2222222222 L42: 2222222222 L43: 2222222222 L44: 2222222222 L45: 2222222222 L46: 2222222222 L47: 2222222222 L48: 2222222222 L49: 2222222222 L50: 2222222222 L51: 2222222222 L52: 2222222222 L53: 2222222222 L54: 2222222222 L55: 3333333333 L56: 3333333333 L57: 3333333333 L58: 3333333333 L59: 3333333333 L60: 3333333333 L61: 3333333333 L62: 3333333333 L63: 3333333333 L64: 3333333333 L65: 3333333333 L66: 4444444444 L67: 4444444444 L68: 4444444444 L69: 4444444444 L70: 4444444444 L71: 4444444444 L72: 5555555555 L73: 5555555555 L74: 5555555555 L75: 5555555555 L76: 5555555555 L77: OOOOOOOOOO L78: OOOOOOOOOO L79: OOOOOOOOOO L80: OOOOOOOOOO L81: OOOOOOOOOO ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值