剑指offer题解合集——Week1day2

剑指offerWeek1

周二:不修改数组找出重复的数字

题目链接:不修改数组找出重复的数字

给定一个长度为 n+1
 的数组nums,数组中所有的数均在 1∼n
 的范围内,其中 n≥1

请找出数组中任意一个重复的数,但不能修改输入的数组。

数据范围
1≤n≤1000
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

AC代码

class Solution {
public:
    int duplicateInArray(vector& nums) {
        int l = 1, r = nums.size() - 1; // 注意二分的范围得在实际的数集里,题目说范围为1 ~ n
        while (l < r)
        {
            int mid = l + r >> 1;
            int s = 0;
            for (auto x : nums)
                if (x <= mid && x >= l) s ++ ;
            if (s > mid - l + 1) r = mid;
            else l = mid + 1;
        }
        
        return r;
    }
};

思路:

整体思路

利用重复数值特性,得到性质:数的个数大于区间长度(区间是指数值的范围)
举例:1,2,3,4,4,数值范围:1~4,实际有5个数,可见数的个数大于区间长度
因此只要不断二分出数的个数大于区间长度的区间,具体见后续样例模拟

特别注意:二分的本质不是有序性,而是部分区间满足某个性质

代码思路

  • while循环,循环条件是区间里有数值
  • 找到mid值,也有人称之为标准值
    • 定义参数:cnt值,用于统计左边区间内数值个数,这里参数命名随意,cnt或者sum都可
    • 遍历nums集合,这里是c++11的语法糖:范围遍历
      • 对于nums中每个数值,统计出在左区间的数值范围的数值个数(有点绕,多读几遍)
    • 如果统计值cnt大于左边区间个数,那么表示在这个左边区间里,则修改二分区间的右端点,反之修改左端点
  • 至此,一直while循环即可

部分模拟

样例:nums = [2, 3, 5, 4, 3, 2, 6, 7]

  • l = 1, r = nums.size() - 1,即:7
  • while循环,循环条件是区间里有数值
  • mid = 1 + 7 >> 1,即:4
    • s = 0
    • 遍历nums
      • nums[0] = 2,(2>=1 && 2 <= 4),s++,s = 1
      • nums[1] = 3,(3 >=1 && 3 <= 4),s++,s = 2
      • nums[2] = 5,(5 >=1 && 5 不会<= 4),s不变,s = 2
      • nums[3] = 4,满足条件,s = 3
      • nums[4] = 3,满足条件,s = 4
      • nums[5] = 2,满足条件,s = 5
      • nums[6] = 6,不满足,s = 5
      • nums[7] = 7,不满足,s = 5
    • 因此,得出在数值1 ~ 4的范围里有5个数:2, 3, 4, 3, 2,显然,大于数的范围,因此在左边区间里,修改右端点,继续while循环

你可能感兴趣的:(剑指offer,c++,算法,剑指offer)