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

力扣题解2576

大家好,欢迎来到无限大的频道。

今日继续给大家带来力扣题解。

题目描述:

给你一个下标从 0 开始的整数数组 nums 。

一开始,所有下标都没有被标记。你可以执行以下操作任意次:

选择两个 互不相同且未标记 的下标 i 和 j ,满足 2 * nums[i] <= nums[j] ,标记下标 i 和 j 。

请你执行上述操作任意次,返回 nums 中最多可以标记的下标数目。

解题思路:

算法的核心思路是:

  1. 对数组进行排序。

  2. 使用双指针的方式遍历排序后的数组,寻找符合条件的下标。

  3. 每当找到一对符合条件的下标时,就将它们标记

        分析题目和示例,我们以更深刻地理解下标标记的逻辑。原先整数数组的下标在被标记的之后,只是数据被标记,而非其固定的位置被标记,也就是说,是因为这个数据符合条件,从而标记该位置,而不是因为该位置符合条件,而去标记数据。

    题目的关键在于标记的是数组中元素的特性,而非其在数组中的位置固定性。(这个是我个人总结出来的,在很多的算法问题中都有所体现)

    ​所以当大家理解了这个条件之后,就可以把原先的数组进行排序,从而简化问题。

排序的经典函数如下

int compare(const void *a, const void *b) {return (*(int *)a - *(int *)b);
}

之后便是在解决问题的函数中进行调用

    qsort(nums, numsSize, sizeof(int), compare);

在这之后,我们的数组num,就变成了一个非递减排列的数组。

排序之后,就是采用双指针遍历数组了(因为涉及两个变量的比较,所以我采用了双指针的方式)

int compare(const void *a, const void *b) {return (*(int *)a - *(int *)b);
}
​
int maxMarkableIndices(int* nums, int size) {// 先对数组进行排序qsort(nums, size, sizeof(int), compare);int markedCount = 0;     // 标记的下标数量int i = 0;                // 指向较小数字的指针int j = 0;                // 指向较大数字的指针
​// 使用双指针遍历数组while (i < size && j < size) {// 找到一对符合条件的 (i, j)if (2 * nums[i] <= nums[j] && i != j) {markedCount += 2; // 每找到一对就标记两个下标i++;              // 移动指针 ij++;              // 移动指针 j} else {// 如果不符合条件,移动 j 指针j++;}// 如果 j 指针已经达到数组的末尾,上面条件再检查时需要重置 j,给下一个 i 对应的 jif (j == size && i < size) {j = i + 1; // 对下一个 i 找 j}}
​return markedCount; // 返回最多可以标记的下标数量
}
//双指针:使用两个指针 i 和 j,
//其中 i 指向较小的元素,j 指向较大的元素。
//每当找到符合条件的 (i, j) 对时,就将 markedCount 增加 2,
//并移动两个指针

但是写完之后发现,这个代码的时间复杂度过高,不在题目的考虑范围之内,所以在此基础上继续优化。

在长度为 n 的数组当中,最理想的情况下只会产生 n/2 个下标数目,因为题目要求“互不相同且未标记”

由此对数组进行排序后,我们将其一分为二,左侧的元素只与右侧的元素进行匹配,其余思路同上,得出代码

int compare(const void* a, const void* b) {return (*(int*)a - *(int*)b);
}
​
int maxNumOfMarkedIndices(int* nums, int numsSize){qsort(nums, numsSize, sizeof(int), compare);int n = numsSize;int m = n / 2;int res = 0;for (int i = 0, j = m; i < m && j < n; i++) {while (j < n && 2 * nums[i] > nums[j]) {j++;}if (j < n) {res += 2;j++;}}return res;
}

时间复杂度:

1. 排序操作:

   - 使用 `qsort` 函数对数组进行排序,时间复杂度为 (O(n log n)),其中 (n) 是数组的大小。排序是整个算法中最耗时的部分。

2. 双指针遍历:

   - 在双指针的部分,外层循环的指针 `i` 最多会遍历 (n/2) 次(即左半边的元素),而内层循环的指针 `j` 也最多遍历 (n) 次。

   - 在最坏情况下,指针 `j` 可能会从中间走到数组的末尾,但每次内层的 `while` 循环只会向前移动 `j` 指针,因此每个元素最多只会被遍历一次。

   - 因此,双指针部分的时间复杂度为 (O(n))。

结合以上两部分,我们可以得出总的时间复杂度:

O(n log n) + O(n) = O(n log n)

 空间复杂度:

1. 排序的空间复杂度:

   - `qsort` 在排序过程中可能会使用额外的空间,具体取决于实现方式。通常情况下,快速排序的空间复杂度为 O(log n),用于递归调用的栈空间。

   - 但在某些实现中,可能会使用 O(n) 的额外空间来存储临时数组(例如归并排序),然而在这里我们使用的是快速排序。

2. 其他变量:

   - 除了排序外,其他变量(如 `n`, `m`, `res`, `i`, `j`)只占用常量空间 (O(1))。​

因此,整体的空间复杂度主要由排序决定:

O(log n)

 总结:

- 时间复杂度:(O(n log n))

- 空间复杂度:(O(log n))


http://www.mrgr.cn/news/25631.html

相关文章:

  • 计算各种图形的周长(多态)
  • MBD_入门篇_24_SimulinkSources
  • [001-02-001]. 第07-03节:理解线程的安全问题
  • 【AI绘画】Midjourney进阶:景别详解
  • 6.3排序——冒泡排序+快速排序
  • 2024第三届大学生算法大赛 真题训练2 解题报告 | 珂学家 | FFT/NTT板子
  • 先攒一波硬件,过几年再给电脑升级,靠谱吗?想啥呢?
  • 嵌入式边缘计算:融合创新与未来展望
  • C语言实现一个24点游戏
  • GD32/STM32启动过程
  • 深入理解Java中的Lambda表达式
  • DAY14信息打点-JS 架构框架识别泄漏提取API 接口枚举FUZZ 爬虫插件项目
  • C++面向对象结构改进
  • 【隐私计算】Paillier半同态加密算法
  • WInform记录的添加和显示
  • C#中的集合
  • 虚拟存储器“大观”,讲解核心逻辑知识和408大题方法
  • ​​操作系统 ---- 进程调度的时机、切换与过程
  • KV260 进阶开发(PYNQ驱动开发+Pixel Pack)
  • 【时时三省】c语言例题----华为机试题<统计字符>