数组问题(鸽巢原理、数字交换、链表寻环)

287. 寻找重复数

  1. 不能更改原数组(假设数组是只读的)。
  2. 只能使用额外的 O(1) 的空间。
  3. 时间复杂度小于 O(n2) 。
  4. 数组中只有一个重复的数字,但它可能不止重复出现一次。

因为不能能改原来的数组,并且只能使用额外O1的空间,所以我们不能使用map记录,也不能维护一个新的数组

因为时间复杂度要在n2以内,所以暴力查找也是不可取的

因为数据范围在1~n-1,所以可以保证不会数组越界,因此使用链表中快慢指针寻找环算法

https://www.cnblogs.com/fankongkong/p/7007869.html

1、当fast指针仅仅只比slow指针多走一个环,如图所示

2、第一次相遇的时候,如图

3、这个时候将fast 重新赋值为开头,如图

4、再走两次,则找到了环的入口结点

重新梳理一下步骤,解题思路

a、第一步,找环中相汇点。分别用fast,slow指向链表头部,slow每次走一步,fast每次走二步,直到fast==slow找到在环中的相汇点。

b、第二步,找环的入口。接上步,当fast==slow时,fast所经过节点数为2x,slow所经过节点数为x,设环中有n个节点,fast比slow多走一圈有2x=n+x; n=x;

可以看出slow实际走了一个环的步数,再让fast指向链表头部,slow位置不变。

假设链表开头到环接口的距离是y,如下图所示,那么x-y表示slow指针走过的除链表开头y在环中走过的距离,那么slow再走y步,此时fast结点与slow结点相遇,fast == slow ,x-y+y=x = n,即此时slow指向环的入口。

442. 数组中重复的数据

比较好的解法是鸽巢原理+亦或交换数据

不用到任何额外空间并在O(n)时间复杂度

把大小为i的数字,放进下标为i-1的位置上,排完之后还剩下的位置不对的,就是多余的。

对第i位置上的数字k,因为k!=i-1,所以要把k丢到k-1的位置上,然后再看看换过来的数字是不是i-1,不是就继续换(有点像排序算法),当这个位置正确后,就进入下一个位置。

当所有位置处理完以后,再扫一遍,发现位置和数字不匹配的数字就是多余的数字,因为不能采用额外空间,所以使用无额外内存的亦或交换(加减交换也可以)。

public List<Integer> findDuplicates(int[] nums) {
            Set<Integer> res=new HashSet<>();
            for(int i=0;i<nums.length;i++){
                while(nums[i]!=i+1){
                    if(nums[i]==nums[nums[i]-1]){
                        res.add(nums[i]);
                        break;
                    }
                    swap(nums,i,nums[i]-1);
                }
            }

            System.out.println(res.toString());
            return new ArrayList<>(res);
        }
        public void  swap(int[] nums,int a,int b){
            nums[a]^=nums[b];
            nums[b]^=nums[a];
            nums[a]^=nums[b];
        }

  

原文地址:https://www.cnblogs.com/wjune-0405/p/12690872.html

时间: 2024-08-10 10:34:37

数组问题(鸽巢原理、数字交换、链表寻环)的相关文章

找一个数组中差值绝对值的最小值(鸽巢原理)

给定一个数组a[n],要你求出数组中最小的|a[i]-a[j]|如果只有一个元素就返回0. 貌似是微软的面试题,估计大多数人首先想到的就是排序之后再比较吧,呵呵,是个人都会做.那面试官考你这个问题有毛线意义,这题我们可以用抽屉原理(也叫鸽巢原理)将n个元素放到n+1个桶中(只需要O(n)时间).按如下过程求解: 1首先找出数组中最大的和最小的元素,如果相等,直接返回0 2确定每个桶的大小bucket_size=(maxe-mine)/(n-1) 3将每个元素放到对应的桶id bucket_id=

POJ 2356. Find a multiple 抽屉/鸽巢原理

Find a multiple Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7192   Accepted: 3138   Special Judge Description The input contains N natural (i.e. positive integer) numbers ( N <= 10000 ). Each of that numbers is not greater than 15000

ZOJ 2955 Interesting Dart Game(完全背包+鸽巢原理)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1954 Recently, Dearboy buys a dart for his dormitory, but neither Dearboy nor his roommate knows how to play it. So they decide to make a new rule in the dormitory, which goes as follows

鸽巢原理(抽屉原理)的详解

抽屉原理 百科名片 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放两个苹果.这一现象就是我们所说的“抽屉原理”. 抽屉原理的一般含义为:“如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1或多于n+1个元素放到n个集合中去,其中必定至少有一个集合里有两个元素.” 抽屉原理有时也被称为鸽巢原理(“如果有五个鸽子笼,养鸽人养了6只鸽子,那么当鸽子飞回笼中后,至少有一个笼子中装有2只鸽子”).它是组合数学中一个重要的原理. 第一抽屉原理 原

程序设计中的组合数学——鸽巢原理

回想到高中的的组合学中,有这样的问题,12个班中有13个人参加IOI的名额(前提每班至少出一个人),那么这会有几种分法? 一个很简单的思路就是把这13个名额摊开,然后拿11个隔板插到这13个名额形成的12个空隙里,然后用组合数的公式即可计算.而鸽巢原理的简单形式就和这个模型有联系. 我们知道,如果把12只鸽子放到11个巢里面,显然有一个巢会出现两只鸽子,这显而易见,同时也是鸽巢原理的最简单的形式. 它的证明也和简单,如果我们假设11个巢穴里最多有1个鸽子,那么各自的总数最多有11个,这一12只鸽

鸽巢原理和容斥原理

鸽巢原理又名抽屉原理 一种简单的表述法为: 若有n个笼子和n+1只鸽子,所有的鸽子都被关在鸽笼里,那么至少有一个笼子有至少2只鸽子. 另一种为: 若有n个笼子和kn+1只鸽子,所有的鸽子都被关在鸽笼里,那么至少有一个笼子有至少k+1只鸽子. 例子: 盒子里有10只黑袜子.12只蓝袜子,你需要拿一对同色的出来.假设你总共只能拿一次,只要3只就可以拿到相同颜色的袜子,因为颜色只有两种(鸽巢只有两个),而三只袜子(三只鸽子),从而得到“拿3只袜子出来,就能保证有一双同色”的结论. 有n个人(至少2人)

抽屉原理(鸽巢原理)

转至:https://blog.csdn.net/sand8o8time/article/details/77009749 一.抽屉原理初介绍: 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面至少放两个苹果.这一现象就是我们所说的“抽屉原理”. 抽屉原理的一般含义为:“如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1个元素放到n个集合中去,其中必定有一个集合里至少有两个元素.” 抽屉原理有时也被称为鸽巢原理.它是组合数学中一个重要的原

Find a multiple POJ - 2356【鸽巢原理】

题意: 求在长度为 \(n\) 的数组中选择连续的元素,使其和是 \(n\) 的倍数,输出元素个数和每个元素的值. 分析: ??我们选取一段连续的元素.对原数组求前缀和,并且对 \(n\) 取模,那么结果就会分布在 \([0,n)\) 之间.如果有一个前缀和取模 \(n\) 的结果为 \(0\),那么这个前缀和一定满足条件.否则,我们考虑有 \(n\) 个结果,分布在长度为 \(n-1\) 的区间上,根据鸽巢原理,必然会有两个相同,那么这两个位置之间的元素和就满足条件. 代码: #include

鸽巢原理简单应用

http://poj.org/problem?id=2356 从n个数里面取出一些数,这些数的和是n的倍数.并输出这些数. 先预处理出前n个数的和用sum[i]表示前i个数的和.若某个sum[i]是n的倍数,直接输出前i个数即可. 否则说明n个数中对n取余的结果有n-1种,即余数为(1~n-1),根据鸽巢原理知必定至少存在两个sum[i]与sum[j]对n取余的结果相等.那么i+1 ~ j之间的数之和一定是n的倍数. #include <stdio.h> #include <iostre