一道简单的数组遍历题,加上四个条件后感觉无从下手

2024-02-13 13:59

本文主要是介绍一道简单的数组遍历题,加上四个条件后感觉无从下手,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击蓝色“五分钟学算法”关注我哟

加个“星标”,天天中午 12:15,一起学算法

640?wx_fmt=jpeg

作者 | P.yh

来源 | 五分钟学算法

今天分享的题目来源于 LeetCode 第 287 号问题:寻找重复数。

题目描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

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

示例 2:

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

说明:

  • 不能更改原数组(假设数组是只读的)。

  • 只能使用额外的 O(1) 的空间。

  • 时间复杂度小于 O(n^2) 。

  • 数组中只有一个重复的数字,但它可能不止重复出现一次。

题目解析

题目的限制真多!!!

首先 不能改变数组 导致无法排序,也无法用 index 和元素建立关系;

只能使用 O(1) 的空间 意味着使用哈希表去计数这条路也走不通;

时间复杂度必须小于 O(n^2) 表示暴力求解也不行;

重复的元素可重复多次 这一条加上后,本来可以通过累加求和然后做差 sum(array) - sum(1,2,...,n) 的方式也变得不可行。

本来是非常简单的一道数组遍历的题目,加上了上面这四个条件后感觉有点无从下手

我们说做题要借助算法,而不是空想,因此对于这道题也不例外,我们可以问自己这样一个问题,就是 “什么样的算法可以不使用额外的空间解决数组上面的搜索问题?”

静静的思索一下。

你这时应该隐隐约约知道了,难道是 二分查找

什么?二分查找法?!

二分查找法不是对有序数组才适用么?

这里澄清一个误区,二分法的使用 并不一定 需要在排序好的数组上面进行,不要让常见的例题限制了你的思路,二分法还有一个比较高级的用法叫做 按值二分

这道题目交代的信息很少,我们只需要关注两个东西 - 数组,数组里的元素,利用二分我们需要去思考的是,我们要找符合条件的元素作为答案,那么 比答案小的元素具有什么样的特质,比答案大的元素又具有什么样的特质?,结合题目给我们的例子来看看:

( 说明:下面的  <=  符号表明 小于或者等于。)

例1:

[1,3,4,2,2]                      元素个数
<= 1 的元素:1                       1
<= 2 的元素:1, 2, 2                 3
<= 3 的元素:1, 2, 2, 3              4
<= 4 的元素:1, 2, 2, 3, 4           5

例2:

[3,1,3,4,2]
<= 1 的元素:1                        1
<= 2 的元素:1, 2                     2
<= 3 的元素:1, 2, 3, 3               4
<= 4 的元素:1, 2, 3, 3, 4            5

极端一点的例子 (必须保证数组的长度是 n + 1, 并且元素都在区间[1,n] 上, 有且只有一个重复)

[3,3,3,3,4]
<= 1 的元素:                        0
<= 2 的元素:                        0
<= 3 的元素:3, 3, 3, 3              4
<= 4 的元素:3, 3, 3, 3, 4           5

看完上面几个例子,相信你明白了一个事实:

  • “如果选中的数 小于 我们要找的答案,那么整个数组中小于或等于该数的元素个数必然小于或等于该元素的值;

  • 如果选中的数 大于或等于 我们要找的答案,那么整个数组中小于或等于该数的元素个数必然 大于 该元素的值”

而且你可以看到,我们要找的答案其实就处于一个分界点的位置,寻找边界值,这又是二分的一个应用,而且题目已经告诉我们数组里面的值只可能在 [1, n] 之间,这么一来,思路就是在 [1, n] 区间上做二分,然后按我们之前提到的逻辑去做分割。整个解法的时间复杂度是 O(nlogn),也是满足题目要求的。

上面的解法不是最优的,但是个人觉得是根据现有的知识比较容易想到的。

另外一种 O(n) 的解法借鉴快慢指针找交点的思想,算法非常的巧妙,也非常的有趣,但不太容易想到,这里把代码放上。

动画描述

代码实现一

//二分查找
class Solution {public int findDuplicate(int[] nums) {int len = nums.length;int start = 1;int end = len - 1;while (start < end) {int mid = start + (end - start) / 2;int counter = 0;for (int num:nums) {if (num <= mid) {counter++;}}if (counter > mid) {end = mid;} else {start = mid + 1;}}return start;}
}

代码实现二

//快慢指针
public int findDuplicate(int[] nums) {        int fast = nums[nums[0]];int slow = nums[0];while (fast != slow) {fast = nums[nums[fast]];slow = nums[slow];}slow = 0;while (fast != slow) {fast = nums[fast];slow = nums[slow];}return slow;
}

640?

有热门推荐?

1.【程序员】

2.【GitHub】

3.【算法】

4.【数据结构】

640?wx_fmt=jpeg

▼ 点击『阅读原文』解锁更多图解 LeetCode 题目

这篇关于一道简单的数组遍历题,加上四个条件后感觉无从下手的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/705718

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

uva 10387 Billiard(简单几何)

题意是一个球从矩形的中点出发,告诉你小球与矩形两条边的碰撞次数与小球回到原点的时间,求小球出发时的角度和小球的速度。 简单的几何问题,小球每与竖边碰撞一次,向右扩展一个相同的矩形;每与横边碰撞一次,向上扩展一个相同的矩形。 可以发现,扩展矩形的路径和在当前矩形中的每一段路径相同,当小球回到出发点时,一条直线的路径刚好经过最后一个扩展矩形的中心点。 最后扩展的路径和横边竖边恰好组成一个直

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1113 凸包+简单几何计算

题意: 给N个平面上的点,现在要在离点外L米处建城墙,使得城墙把所有点都包含进去且城墙的长度最短。 解析: 韬哥出的某次训练赛上A出的第一道计算几何,算是大水题吧。 用convexhull算法把凸包求出来,然后加加减减就A了。 计算见下图: 好久没玩画图了啊好开心。 代码: #include <iostream>#include <cstdio>#inclu

uva 10130 简单背包

题意: 背包和 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

C语言:柔性数组

数组定义 柔性数组 err int arr[0] = {0}; // ERROR 柔性数组 // 常见struct Test{int len;char arr[1024];} // 柔性数组struct Test{int len;char arr[0];}struct Test *t;t = malloc(sizeof(Test) + 11);strcpy(t->arr,