剑指offer 第4天 查找算法(简单)C++题解

一、数组中重复的数字

剑指 Offer 03. 数组中重复的数字icon-default.png?t=M85Bhttps://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

限制:

2 <= n <= 100000

解题思路:

1.暴力解法:i从0~n-1遍历数组,j从i~n-1遍历数组,nums[i]等于nums[j]时便找到了重复数字,但此方法时间复杂度O(N^2),还有更优解。

2.根据所有数字都在0~n-1的范围内的特性,可以将数值作为索引映射到数组中,如数组已有元素则说明此元素重复。

3.利用哈希表建立映射,遍历一遍数组即可找到重复数字。

具体细节:

1.初始化哈希表,遍历数组将数字添加至哈希表中。

2.若数字已存在则返回此数字,若不存在则继续遍历直至结束。

3.若都无重复数字则返回-1.

复杂度分析:

1.时间复杂度:遍历一遍数组,且哈希表存储和查找都为O(1),所以整体时间复杂度为O(N)。

2.空间复杂度:哈希表占用O(N)的空间复杂度。

代码实现:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        unordered_map<int, bool> mymap;
        for (int i = 0; i < nums.size(); i++) {
            if (mymap[nums[i]]) return nums[i];
            mymap[nums[i]] = true;
        }
        return -1;
    }
};

二、在排序数组中查找数字

剑指 Offer 53 - I. 在排序数组中查找数字 Iicon-default.png?t=M85Bhttps://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/

 统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

提示:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

解题思路:

1.暴力解法:遍历数组统计相同元素的次数,但时间复杂度和数组非递减的特性并没有用到。

2.二分查找:排序数组首先想到二分查找,所有目标元素在一起排列,故有左右两个下标,统计目标元素的个数便可转化为右下标减去左下标即可。

具体细节:

1.初始化左下标为left = 0,右下标为right = n - 1.

2.循环查找目标元素的左右边界,计算mid = (left + right) / 2;

若 nums[mid] < target ,则 target 在闭区间 [mid + 1, right] 中,因此执行 left = mid + 1;

若 nums[mid] > target ,则 target 在闭区间 [left, mid - 1]中,因此执行 right = mid - 1;

若 nums[mid] == target ,则右边界 right 在闭区间 [mid + 1, right] 中;左边界 left在闭区间 [left, mid - 1] 中。

因此分为以下两种情况:

若查找 右边界 right,则执行 left = mid + 1 ;(跳出时 left 指向左边界);

若查找 左边界 left ,则执行 right = mid - 1 ;(跳出时 right 指向右边界)。

3.算法优化:找到右边界后,若无目标元素则直接返回0,无需查找左边界。若有目标元素则左边界可以直接从[0, right]之间查找。

复杂度分析:

1.时间复杂度:两次二分查找需O(logN)的时间复杂度。

2.空间复杂度:只需常数级别的额外变量,故只需O(1)的空间复杂的。

代码实现:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.size() == 0) return 0;
        int left, right;
        int l = 0, r = nums.size() - 1;
        while (l <= r) {
            int m = (l + r) / 2;
            if (nums[m] > target) r = m - 1;
            else l = m + 1;
        }
        if (r >= 0 && nums[r] != target) return 0;
        right = r;
        l = 0;
        while (l <= r) {
            int m = (l + r) / 2;
            if (nums[m] >= target) r = m - 1;
            else l = m + 1;
        }
        left = l;
        return right - left + 1;
    }
};

三、0 ~ n-1 中缺失的数字

剑指 Offer 53 - II. 0~n-1中缺失的数字icon-default.png?t=M85Bhttps://leetcode.cn/problems/que-shi-de-shu-zi-lcof/

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2

示例 2:

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

限制:

1 <= 数组长度 <= 10000

解题思路:

1.暴力解法:循序遍历数组,若数组元素不等于下标则返回下标,但还有时间复杂度更优的解法。

2.二分查找:二分查找下标不等于元素的首位元素就是要找的数字。

具体细节:

1.初始化下标left = 0 ,right = n - 1。

2.循环查找下标不等于元素的最左元素,计算 mid = (left + right) / 2;

若 nums[m] = m ,则 “下标不等于元素区间的最左元素” 一定在闭区间 [mid + 1, right] 中,因此执行 left = m + 1;

若 nums[m] ≠ m,则 “下标不等于元素区间的最左元素” 一定在闭区间 [left, mid - 1] 中,因此执行 right = m - 1;

3.循环过后left指向下标不等于元素区间的最左元素即要找的数字。

复杂度分析:

1.时间复杂度:二分查找O(logN)。

2.空间复杂度:常数的额外变量故O(1)。

代码实现:

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == mid) left = mid + 1;
            else right = mid - 1; 
        }
        return left;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨小木木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值