当前位置: 首页 > news >正文

【Algorithm】Day-11

本篇文章主要讲解算法练习题


1  数组中的 k-diff 数对

链接:532. 数组中的 k-diff 数对 - 力扣(LeetCode)

题目描述

给你一个整数数组 nums 和一个整数 k,请你在数组中找出 不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。

k-diff 数对定义为一个整数对 (nums[i], nums[j]) ,并满足下述全部条件:

  • 0 <= i, j < nums.length
  • i != j
  • |nums[i] - nums[j]| == k

注意|val| 表示 val 的绝对值。

示例 1:

输入:nums = [3, 1, 4, 1, 5], k = 2
输出:2
解释:数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。
尽管数组中有两个 1 ,但我们只应返回不同的数对的数量。

示例 2:

输入:nums = [1, 2, 3, 4, 5], k = 1
输出:4
解释:数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5) 。

示例 3:

输入:nums = [1, 3, 1, 5, 4], k = 0
输出:1
解释:数组中只有一个 0-diff 数对,(1, 1) 。

提示:

  • 1 <= nums.length <= 104
  • -107 <= nums[i] <= 107
  • 0 <= k <= 107

题目解析

        这道题目会给你一个整数数组 nums 以及一个整数 k,题目要求你求出 nums 中不同的 k-diff 数对。其中 k-diff 数对是指:abs(nums[i] - nums[j]) == k,并且 i != j。比如 nums = [1, 3, 1, 5, 7, 9],k = 2,返回值就是4,数对分别为 (1, 3)、(3, 5)、(5, 7)、(7, 9)。值得注意的是,虽然 nums 中有两个1,但是返回的是不同的数对。

算法讲解

        我们可以先来想一个暴力解法,我们利用一个 i 来遍历 nums,然后 j 从 i + 1 开始遍历nums,当 abs(nums[i] - nums[j]) == k 时,我们就可以 ++ret,但是在遍历过程中,因为要求出不同的数对,所以我们需要对遍历到的元素进行去重,我们可以采用 unordered_set (底层结构是哈希表,可以通过 count 函数快速判断元素的个数)哈希表来进行去重,在去重过程中采用 st1 对 nums[i] 进行去重,采用 st2 对 nums[j] 进行去重。但是这样的话还是可能出现重复数对,比如 nums = [1,2,4,4,3,3,0,9,2,3],当 i = 4 时,此时会添加进一个数对 (3, 0),但是当 i = 6 时,依然会添加进 (0, 3) 这个数对,所有就重复了,为了避免这种情况,所以我们先对数组进行排序,将所有重复的元素放到一起,这样就会避免这种情况:

class Solution 
{
public:int findPairs(vector<int>& nums, int k) {sort(nums.begin(), nums.end());int ret = 0;unordered_set<int> st1;for (int i = 0; i < nums.size() - 1; i++){//先判断之前是否已经用过该数字了if (st1.count(nums[i])) continue;st1.emplace(nums[i]);//每次都需要重新创建 st2unordered_set<int> st2;for (int j = i + 1; j < nums.size(); ++j){if (st2.count(nums[j])) continue;st2.emplace(nums[j]);if (abs(nums[i] - nums[j]) == k) ++ret;}}return ret;}
};

  显然,时间复杂度是 O(n^2) 的,会超时,所以我们必须对该算法进行优化。

        在暴力解法中,我们对数组进行了排序,那么我们就可以利用这一特性来进行优化。我们假设 i 走到了 nums[i],j 走到了 nums[j],由于数组有序,所以如果 nums[j] - nums[i] > k,那么 j 及 j 之后的元素都不可能与 nums[i] 组成 k-diff 数对,所以此时我们直接让 i 向前走就可以了;但是如果 nums[i] - nums[j] < k,那么 i 及 i 之后的元素就不可能与 nums[j] 组成 k-diff 数对,所以我们直接让 j 向后走就可以了。分析到这,我们就可以发现其优化算法为排序 + 双指针

        我们先定义两个指针 prev 与 cur,我们初始让 prev = 0, cur = 1,因为数对不能是一个数字,然后我们进行判断,如果 nums[cur] - nums[prev] > k,那我们就直接 ++prev,但是需要注意一点,就是 prev 相加之后不能与 cur 相等,所以我们还需要加一个判断条件,那就是 prev < cur - 1;如果 nums[cur] - nums[prev] < k,那我们就 ++cur,如果 nums[cur] - nums[prev] == k,那么我们就 ++count,但是这个时候是需要去重的,也就是判断 nums[cur] == nums[cur + 1],++cur,但是需要判断是否越界,也就是 cur + 1 < nums.size(),cur 去重完成之后,会处于使得 nums[cur] - nums[prev] == k 的最后一个 cur 相等的位置;同时我们也需要对 prev 进行去重,也就是判断 nums[prev] == nums[prev + 1],++prev,但是 prev 需要小于 cur,也就是 prev < cur;去重完成之后,需要 ++cur,因为 cur 还处于最后一个与之前元素相同的位置。除了上面三种情况,直接 ++cur 就好了。上述过程如图所示:

class Solution 
{
public:int findPairs(vector<int>& nums, int k) {if (nums.size() < 2) return 0;//先对数组排序sort(nums.begin(), nums.end());int prev = 0, cur = 1;int count = 0;while (cur < nums.size()){if  (prev < cur - 1 && nums[cur] - nums[prev] > k) ++prev;else if (nums[cur] - nums[prev] == k){++count;while (cur + 1 < nums.size() && nums[cur] == nums[cur + 1]) ++cur;while (prev < cur && nums[prev] == nums[prev + 1]) ++prev;++cur;}else ++cur;}return count;}
};

 显然,上述代码的时间复杂度为 O(nlogn)。

        那么我们可不可以只利用 unordered_set 这种数据结构呢?当然是可以的,因为 unordered_set 这种数据结构可以去重,而且可以快速判断出某个数字是否存在在集合当中,那么我们就可以创建两个 unordered_set,st1 与 st2,st1 用来存储 nums 中的元素,st2 用来存储与 nums[i] 的差的绝对值等于 k 的元素,当遍历到 nums[i] 时,我们就判断 st1 中是否存在 nums[i] - k 与 nums[i] + k,如果存在,说明 nums[i] - k 可以与 nums[i] 构成一个 k-diff 数对,此时将 nums[i] - k 插入到 st2 中,这样就保存了一个数对的较小值;nums[i] + k 与 nums[i] 也可以构成一个 k-diff 数对,我们就可以将 nums[i] 插入到 st2 中,这样也保存了一个数对的较小值,最后返回 st2.size() 就可以了。

代码

class Solution 
{
public:int findPairs(vector<int>& nums, int k) {unordered_set<int> st1, st2;for (auto& x : nums){if (st1.count(x - k)) st2.emplace(x - k);if (st1.count(x + k)) st2.emplace(x);st1.emplace(x);}return st2.size();}
};

时间复杂度为 O(n)。


2  验证回文串 ||

链接:680. 验证回文串 II - 力扣(LeetCode)

题目描述

给你一个字符串 s最多 可以从中删除一个字符。

请你判断 s 是否能成为回文字符串:如果能,返回 true ;否则,返回 false 。

示例 1:

输入:s = "aba"
输出:true

示例 2:

输入:s = "abca"
输出:true
解释:你可以删除字符 'c' 。

示例 3:

输入:s = "abc"
输出:false

提示:

  • 1 <= s.length <= 105
  • s 由小写英文字母组成

题目解析

        该题目是在是否是回文串的基础上来进阶了一下。我们依然是判断一个字符串 s 是否是回文串,如果其不是回文串,那就删除一个字符,看剩下的 s 是否是回文串。比如 s = "abcdba",返回 true,因为删除字符 c 或者字符 d 之后,剩下的字符就是一个回文串。

算法讲解

        之前我们判断一个字符串是否是回文串,采用的是双指针算法,定义一个 left 与 right 指针,如果 s[left] == s[right],那么我们就 ++left,--right,如果 s[left] != s[right],那就 return false,直到 left >= right 了都没有返回 false,那就返回 true。

        那么这个题目可不可以使用双指针算法呢?那么我们试一下,我们依然采用 left 和 right 两个指针,要判断 s 是否是回文串,前面是一样的,当 s[left] == s[right] 时,我们依然 ++left,--right,当 s[left] != s[right] 时,要删除一个字符,其实就是要么删除 s[left],要么删除 s[right] 嘛,所以我们就接着判断 [left + 1, right] 区间或者 [left, right -1] 区间是不是回文串就可以了。所以这个题目依然可以采用双指针算法,只不过要额外编写一个判断 s 字符串 [left, right] 区间是不是回文串的函数。

代码

class Solution 
{
public:bool isReverse(string& s, int left, int right){while (left < right){if (s[left] == s[right]) ++left, --right;else return false;}return true;}bool validPalindrome(string s) {int left = 0, right = s.size() - 1;int count = 1;while (left < right){if (s[left] == s[right]) ++left, --right;else return isReverse(s, left + 1, right) || isReverse(s, left, right - 1);}return true;}
};


3  寻找旋转排序数组中的最小值

链接:153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

题目解析

        题目会给你一个数组 nums,这个数组是由一个单调递增且没有重复元素的数组旋转了 n 次得来的,比如:arr = [0, 1, 2, 3, 4, 5, 6, 7],那么 nums 可能是由 arr 旋转一次得来,那么 nums = [7, 0, 1, 2, 3, 4, 5, 6],如果旋转两次,那么 nums = [6, 7, 0, 1, 2, 3, 4, 5]。题目要求让我们找到 nums 中的最小元素并且返回。比如 nums = [6, 7, 0, 1, 2, 3, 4, 5],那么返回值就是 0。

算法讲解

        暴力解法很简单,就是我们使用一个 minnum 变量,遍历一遍数组,minnum = min(minnum, nums[i]),这样遍历一遍 nums 之后,minnum 中保存的就是最小值。显然,时间复杂度是 O(n) 的。

        在暴力解法中并没有应用数组的特性,分析数组,我们可以发现数组中前面是单调递增的,到达一段之后,后面也是单调递增的,显然数组具有二段性,所以我们就可以采用二分查找算法了。那么具体的二段性是什么呢?由于 nums 中没有重复的数字,而且是由一个单调递增的数组旋转而来,假设两段分别为 [0, i] 与 [i + 1, n - 1],那么 [0, i] 中的 nums[j] >= nums[0],而 [i + 1, n - 1] 中的 nums[j] < nums[0],所以我们就可以依次来划分 nums。当 mid 落在左边区间时,也就是 nums[mid] >= nums[0] 时,此时 mid 及 mid 之前的元素绝对不可能是最小值,因为 nums[0] 会大于最小值,所以要 left = mid + 1,而如果 nums[mid] < nums[0],那是可能出现最小值的,所以需要 right = mid。分析出了两个区间的指针移动情况,那么判断条件就是 left < right,而且 left = mid + 1,出现了 + 1,上面求中点就不应该 + 1,也就是 mid = left + (right - left) / 2。

        但是还有一种特殊情况我们需要进行判断,那就是 nums == arr 的情况,这种情况下 nums 是一个单调递增的数组,nums[mid] 始终大于 nums[0],所以 left 会始终等于 mid + 1,最后会与 right 重合,所以此时返回的是最大值。所以在返回结果时我们需要特殊判断一下,如果 nums[left] > nums[0],那就返回 nums[0],否则就返回 nums[left]。

代码

class Solution 
{
public:int findMin(vector<int>& nums) {int n = nums.size();int left = 0, right = n - 1;while (left < right){int mid = left + (right - left) / 2;if (nums[mid] >= nums[0]) left = mid + 1;else right = mid;}return nums[left] > nums[0] ? nums[0] : nums[left];}
};
http://www.dtcms.com/a/574953.html

相关文章:

  • dw做的网站有什么缺陷四川平台网站建设哪里有
  • 张琦加盟 2025 创始人 IP+AI 万人大会:AI 时代,IP 破局增长的实战方法都在这
  • 南京建设网站的公司dw网页制作成品12页
  • 手机轻松控制电脑:局域网内远程操控B站/抖音实战教程
  • 做网站要求高吗最新wordpress模板
  • 企业网站现状wordpress 安全狗
  • Spring Cloud 总览:微服务的生态基石
  • 网站制作最新技术的新媒体网站建设十大的经典成功案例
  • 20251105在荣品RD-RK3588-MID开发板的Android13系统的导航栏左右两边增加音量+-按钮
  • 湘潭做网站 要到磐石网络电商平台怎么加入
  • wordpress单页导出广东企业网站seo哪里好
  • 鲜花网站素材网站建设与管理结课论文
  • 线性表之链表的介绍和使用
  • 企业网站管理系统螺栓球网架
  • 做的网站在百度找不到wordpress更换皮肤
  • 网络传输协议的介绍——SSE
  • 河南省建设厅网站 吴浩浙江省建设执业注册中心网站
  • 桂林网站客户管理系统免费
  • 基于ssm的实验室耗材管理系统
  • wordpress做分类信息网站东莞市住建局官网
  • Rust 练习册 4:Deref trait 与智能指针
  • 8.【NXP 号令者RT1052】开发——实战-外部中断
  • 国内出名的设计网站有哪些老网站做seo能不能重新注册
  • ETL 清洗在某平台中的概念与实践解析
  • obsidian1.9.14_win中文_Markdown编辑器_安装教程
  • 网站建设财务怎么入账的wordpress主题
  • yaml配置文件和语法说明
  • 成都网站建设 天空在线wordpress机械模板下载
  • 广州公司制作网站小型网站建设步骤
  • 致同研究:附有质量保证条款的销售的披露示例