求重复数算法思路

关于求重复数,我们先来看看两道常见的题

1、有101个数,为[1,100]之间的数,其中一个数是重复的,如何寻找这个重复的数,其时间复杂度和空间复杂度是多少?

2、1-N(N最大32000,且未知),内存只有4K,找出其中的重复数

找重复数,基本的思路有四个

  1. 第一种思路,建立布尔数组,用每一位的下标来表示某个数的值,用每一位的值来表示该数是否重复。这种思路适合数组中最大数不大的情况。一旦最大数过大,那么我们需要建立的布尔数组便会过大,这时候空间消耗就非常大了,有可能会超出内存需求。时间复杂度为
    o(n),空间复杂度不稳定。
  2. 第二种思路,进行 hash 运算或取余运算,利用冲突来找出相同数。这种思路比较简单,对空间和时间消耗都不大,但是实现比较麻烦,因为要判断冲突,解决冲突。时间复杂度为 0(n),空间复杂度为 0(n)
  3. 第三种思路,根据高位或低位,采用二分或者四分递归找出重复值。适用于数据量过大的情况。时间复杂度为 0(nlogn),空间复杂度 0(1)
  4. 第四种思路,可以先对数组进行堆排序或者快速排序,然后遍历排过序的数组。时间复杂度为 0(nlogn),空间复杂度 0(n)。

首先看第一题,

  1. 采用第一种思路的话,我们要建立一个大小为100的布尔数组b,并且都初始化为 false 。然后对待排序的数组进行遍历,假如数组前四个元素的值为{62,53,88,62},那么,我们就将 b[62]=true , b[53]=true , b[88]=true ,当进行第四次赋值的时候,因为 b[62]之已经赋过值了,对再次赋值时经判断其值为 true,那么,62 便是我们要找的重复数。
  2. 采用第二种思路的话,假设待处理数组a 长度为 n ,那么我们可以新建一个长度为n的对象数组b,把数组a中的每一个元素对 n 进行取余结果为i,然后将该元素储存到下标为 b[i]。采用链表的方式解决冲突。当发生冲突的时候,检查冲突位置链表内是否有这个元素。如果有的话,说明它为重复值。
  3. 第三种思路,可以根据数组中每个值最高位为0还是1,进行二分。然后循环递归一直到最低位相等。相等的两个数便是重复数。
  4. 第四种思路,对这101个数进行堆排序或快速排序,然后遍历排好序的数组便可得到相等数。

接下来来看第二个问题,因为对空间大小有要求,解决就稍微复杂一点。

  1. 采用第一个思路:要建立布尔数组,每一个元素占 1bit,(1*32000)/(8*1024)=3.9KB,正好在4K 之内,所以第一种思路有效。
  2. 采用第二种思路:由于数组个数未知,所以不能判断可行性(元素个数超过1024个便无法处理了)。
  3. 采用第三中思路:假设所有的数据都储存在硬盘上,一次读一个或者多个(4K内存范围内),分别按最高位或最高两位等进行2分或多分。然后循环递归,再2分或多分已被分的数组,知道找到一样的数。
  4. 采用第四种思路:因为不知道数组大小,不能判断可行性(元素个数超过1024个便无法处理了)。

对比这四个思路,各有各的优势和劣势,也各有各的使用场景。实际使用时一定要灵活变通。

时间: 2024-12-25 19:42:06

求重复数算法思路的相关文章

Modified LCS (扩展欧几里德)细写了对这个算法思路的理解

题目:Modified LCS 为过此题去仔细研究了下扩展欧几里德算法,领悟了一些精华. 模板为: void gcd(ll a, ll b, ll& d, ll& x, ll& y) { if(!b) {d = a; x = 1; y = 0;} else{ gcd(b, a%b, d, y, x); y -= x*(a/b);} } 这里算出来的x,y是对于方程ax+by=gcd(a,b)而言的一组解. 为什么叫扩展欧几里德说明肯定用了欧几里德算法的原理即:gcd(a,b)=gc

【常用算法思路分析系列】字符串高频题集

本文是[常用算法思路分析系列]的第二篇,分析字符串相关的高频题目.第一篇是关于排序相关的高频题,还没有看的同学请移步:[常用算法思路分析系列]排序高频题集 1.KMP字符匹配 对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同,即给定两棵二叉树的头结点A和B,请返回一个boolean值,代表A中是否存在一棵同构于B的子树.上述其实就是一个字符匹配的问题,我们将A.B两棵二叉树进行遍历,得到一个字符串,就是判断B串是否是A串的子串.而字符匹配常用的

【常用算法思路分析系列】与二分搜索相关高频题

本文是[常用算法思路分析系列]的第五篇,总结二分搜索相关的高频题目和解题思路.本文分析如下几个问题:1.求数组局部最小值问题:2.元素最左出现的位置:3.循环有序数组求最小值:4.最左原位:5.完全二叉树计算结点数:6.快速N次方. 本系列前四篇导航: [常用算法思路分析系列]排序高频题集 [常用算法思路分析系列]字符串高频题集 [常用算法思路分析系列]栈和队列高频题集(修改版) [常用算法思路分析系列]链表相关高频题集 二分搜索的重要提醒: 一般我们选择中点进行搜索,会写成mid = (lef

算法手记(2)Dijkstra双栈算术表达式求值算法

这两天看到的内容是关于栈和队列,在栈的模块发现了Dijkstra双栈算术表达式求值算法,可以用来实现计算器类型的app. 编程语言系统一般都内置了对算术表达式的处理,但是他们是如何在内部实现的呢?为了了解这个过程,我们可以自行搭建一套简易的算术表达式处理机制,这里就用到栈特性和本篇提到的Dijkstra算法. 概述:     算术表达式可能是一个数.或者是由一个左括号.一个算术表达式.一个运算符.另一个算术表达式和一个右括号组成的表达式.为了简化问题,这里定义的是未省略括号的算术表达式,它明确地

[算法整理]树上求LCA算法合集

1#树上倍增 以前写的博客:http://www.cnblogs.com/yyf0309/p/5972701.html 预处理时间复杂度O(nlog2n),查询O(log2n),也不算难写. 2#st表(RMQ) 首先对一棵树进行dfs,得到欧拉序列,记录下每个节点的第一次出现位置. (先序遍历这棵树,访问到的节点(无论是从深的一层返回还是父节点访问)就加入到序列中,序列长度为2 * n - 1) 根据欧拉序列神奇的特性,两个点第一次出现的位置之间,深度最小的一个点,是这两个点LCA(反正我是不

递归学习(一)最简单的C语言递归求年龄算法

递归是我们在学习编程中,必须要去学习的,虽然递归晦涩难懂 ,但是很多时候,递归的思想会很有用,但是在实际开发中,不建议使用递归,要用循环来代替递归,不然bug无穷. ----------------------------------------------------------- 问题描述: 有5个人坐在一起, 问第5个人,他说比第4个人大2岁, 问第4个人,他说比第3个人大2岁, 问第3个人,他说比第2个人大2岁, 问第2个人,他说比第1个人大2岁, 问最后一个人,他说10岁 第5个人多大

常见排序算法思路和简单代码实现

算法名称 算法思路 简单代码实现                        

求幂算法

1.简单递归 最简单的求幂算法是根据xn=x*xn-1,使用递归: def foo(x,n): if n==0: return 1 else: return x*foo(x,n-1) 这样求x的n次方,会进行n-1次乘法运算,n较大时效率很低. 2.高效递归 一种更高效的算法,可以将运算次数降到LogN的级别,由于: xn=xn/2*xn/2 , n为偶数时 xn=x(n-1)/2*x(n-1)/2*x , n为奇数时 def foo(x,n): if n==0: return 1 else:

【常用算法思路分析系列】排序高频题集

最近在牛客上整理常用的一些算法思路,[常用算法思路分析系列]主要是针对一些高频算法笔试.面试题目的解题思路进行总结,大部分也给出了具体的代码实现,本篇文章是对排序相关题目的思路分析. 1.简单分类 首先对一些常用算法按空间复杂度进行分类如下: O(1): 冒泡排序.选择排序.插入排序.希尔排序.堆排序 O(logN)~O(N): 快速排序 O(N): 归并排序 O(M): 计数排序.基数排序 2.对一个基本有序的有序的数组排序,选择哪种排序算法? 基本有序:指如果把数组排好序的话,每个元素移动的