61. 从1到n,共有n个数字,每个数字只出现一次。从中随机拿走一个数字x,请给出最快的方法,找到这个数字。如果随机拿走k(k>=2)个数字呢?[find k missing numbers from 1 to n]

【本文链接】

http://www.cnblogs.com/hellogiser/p/find-k-missing-numbers-from-1-to-n.html

 【题目】

从1到n,共有n个数字(无序排列),每个数字只出现一次。现在随机拿走一个数字x,请给出最快的方法,找到这个数字。要求时间复杂度为O(n),空间复杂度为O(1)。如果随机拿走k(k>=2)个数字呢?

【分析】

题目给出的条件很强,数字是从1~n的数字,限制了数字的范围;每个数字只出现一次,限制了数字出现的次数;随即拿走了一个数字,说明只有一处是与其他不同、不符合规律的。我们可以利用这些特点来选择合适的解法。

(1)Hash法。利用Hash法统计数字出现的次数,次数为0的即为所求。时间复杂度O(n),空间复杂度O(n)。通常这不是面试、笔试时想要的答案,但是Hash的优势在于其通用性。

(2)排序法。利用快排,得到排序后的数组,然后顺序遍历,统计次数为0的数字。时间复杂度O(nlgn),空间复杂度O(1)。其时间复杂度略高,通常也不是面试官期待的解法,但排序法也算是一种通用做法。

(3)元素相乘/相加法。时间复杂度O(n),空间复杂度O(1)。

元素相乘法:由于只有一个元素被拿走,因此我们只需要先算出n的阶乘n!,再除以现存所有数字的乘积M,即可得到拿走的数字x
(x=n!/M)。但是且缺陷是n不能太大,否则会溢出。

元素相加法:先算出从1到n的所有数字的和Sn,然后减去现有所有数字的和sum,即可得到拿走的数字x(x=Sn-sum)。元素相加法比元素相乘要更好一些。

(4)位运算。时间复杂度O(n),空间复杂度O(1)。

位运算法如果可以使用的话,应该是计算最快的方法。但是位运算对条件要求也较苛刻,一般需要元素有特殊规律,才有可能使用这种方法。在本题目中,对1~n所有元素进行xor运算得到A=1^2^3^…^(x-1)^x^(x+1)^…^n,在对取走一个元素后剩下的元素进行xor运算得到B=1^2^3^…^(x-1)^(x+1)^…^n,二者xor即可得拿走的数字x
= A^B。因为在A^B的过程中相同的数字都被抵消掉了,剩余的结果即为x。

【代码】

C++
Code






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

 

// 61_FindMissingNumberFrom1toN.cpp : Defines the entry point for the console application.

//
/*
     version: 1.0

     author: hellogiser

     blog: http://www.cnblogs.com/hellogiser

     date: 2014/5/28
*/


#include "stdafx.h"

/*

A=1^2^3^...^(x-1)^x^(x+1)^...^n
B=1^2^3^...^(x-1)^(x+1)^...^n

x = A^B

n=9
1,2,3,4,6,7,8,9
x = 5

*/
int FindMissingNumberFrom1ToN(int data[], int n)

{
    int length = n - 1;
    if(NULL == data || length <= 0)

        return -1;
    // xor all
    int xor_all = 0;
    for(int i = 1; i <= n; i++)

         xor_all ^= i;


    // xor of current array

    int xor_current = 0;
    for(int i = 0; i < length; i++)

         xor_current ^= data[i];


    //get result
    int result = xor_all ^ xor_current;

    return result;

}

void test_base(int data[], int n)
{

    int result = FindMissingNumberFrom1ToN(data, n);

     printf("%d \n", result);
}

void test_case1()

{
    int data[] = {1, 2, 3};
    int length = sizeof(data) / sizeof(int);

     test_base(data, length + 1);
}

void test_case2()
{

    int data[] = {2, 3, 4};
    int length = sizeof(data) / sizeof(int);

     test_base(data, length + 1);
}

void test_case3()
{

    int data[] = {1, 2, 3, 4, 6, 7, 8, 9};
    int length = sizeof(data) / sizeof(int);

     test_base(data, length + 1);
}

void test_main()
{

     test_case1();

     test_case2();

     test_case3();
}

int _tmain(int argc, _TCHAR *argv[])

{
     test_main();

    return 0;
}
/*

4
1
5
*/

【扩展】

如果随机拿走两个数字呢?如果随机拿走k(k>2)个数字呢?

(1)(2)是通用做法,仍适合。

(3)扩展:

K=1时,构造2个等式。

Sa = 1+2+…(x-1)+x+(x+1)…+n

Sb = 1+2+…(x-1)+(x+1)…+n

X = Sa-Sb

K=2时,构造4个等式。

S2a = 12+22+…+x2+…+y2+…+n

S2b =
12+22+…(x-1)2+(x+1)2…+(y-1)2+(y+1)2…+n

S1a = 1+2+…+x+…+y+…+n

S1b = 1+2+…(x-1)+(x+1)…+(y-1)+(y+1)…+n

则有x2+y2=S2a-S2b,x+y =S1a-S1b。可以求解得到x和y。

同理(k>2),构造2*k个等式,可以得到关于k个数的k个方程,求解即可得到k个数字。

(4)扩展:

思考一下,如何扩展?

【参考】

http://ouscn.diandian.com/post/2013-10-06/40052170552

【本文链接】

http://www.cnblogs.com/hellogiser/p/find-k-missing-numbers-from-1-to-n.html

61.
从1到n,共有n个数字,每个数字只出现一次。从中随机拿走一个数字x,请给出最快的方法,找到这个数字。如果随机拿走k(k>=2)个数字呢?[find k
missing numbers from 1 to n],布布扣,bubuko.com

61.
从1到n,共有n个数字,每个数字只出现一次。从中随机拿走一个数字x,请给出最快的方法,找到这个数字。如果随机拿走k(k>=2)个数字呢?[find k
missing numbers from 1 to n]

时间: 2024-11-08 07:12:05

61. 从1到n,共有n个数字,每个数字只出现一次。从中随机拿走一个数字x,请给出最快的方法,找到这个数字。如果随机拿走k(k>=2)个数字呢?[find k missing numbers from 1 to n]的相关文章

猜数游戏,随机产生一个数字(0-9),自己看几次能出来,并且给出提示。

1 /* 2 * 猜数游戏,随机产生一个数字(0-9),自己看几次能出来,并且给出提示. 3 * */ 4 import java.util.Random; 5 import java.util.Scanner; 6 7 public class WhileTest{ 8 public static void main(String args[]){ 9 10 Scanner sc = new Scanner(System.in); 11 Random ran = new Random(); 1

算法导论学习之线性时间求第k小元素+堆思想求前k大元素

对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思路例如以下: 1.分解:将A[p,r]分解成A[p,q-1]和A[q+1,r]两部分.使得A[p,q-1]都小于A[q],A[q+1,r]都不小于A[q]; 2.求解:假设A[q]恰好是第k小元素直接返回,假设第k小元素落在前半区间就到A[p,q-1]递归查找.否则到A[q+1,r]中递归查找. 3

此题要求前k小答案,我们首先把k个inf加入小根堆

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#define mp make_pair#define pa pair<int,int>#define M 500005#include <queue>#define inf 0x3f3f3f3f#

C语言K&R习题系列——句子中一个空格代替多个空格的四种方法

原题: Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank. 第一种: 这种最常用,设置一个inspace作为布尔变量,标志当前输入是否在字符中,或在字符外 #include <stdio.h>   int main(void) {   int c;   int inspace=0;     while((c = getcha

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格, 但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37), 因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

package edu.bjtu.day8_27; import java.util.Scanner; /** * @author Allen * @version 创建时间:2017年8月27日 下午7:55:46 * 类说明:链接:https://www.nowcoder.com/questionTerminal/6e5207314b5241fb83f2329e89fdecc8 地上有一个m行和n列的方格.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格, 但

随机生成一个5位以内的数,然后输出该数共有多少位,每位分别是什么

//方法一: <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> /* * 函数名:randomFiveNumber * 参数:无 * 作业:随机生成一个5位以内的数,然后输出该数共有多少位,每位分别是什么 */ function ran

剑指Offer(Java版)第十二题:地上有一个m行n列的方格。一个机器人从坐标(0, 0)的格子开始移动, 它每一次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。 如,当k为18时,机器人能够进入方格(35, 37),因为3+5+3+7=18。但它不能进入方格(35, 38), 因为3+5+3+8=19。请问该机器人能够到达多少个格子?

/*地上有一个m行n列的方格.一个机器人从坐标(0, 0)的格子开始移动, 它每一次可以向左.右.上.下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子. 如,当k为18时,机器人能够进入方格(35, 37),因为3+5+3+7=18.但它不能进入方格(35, 38), 因为3+5+3+8=19.请问该机器人能够到达多少个格子?*/public class Class12 { public int moveCount(int rows, int cols, int threshold){

剑指Offer(Java版)第六十五题:给定一棵二叉搜索树,请找出其中的第k小的结点。 例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

/*给定一棵二叉搜索树,请找出其中的第k小的结点.例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4.*//*二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值*///思路:从最左边的叶子节点开始找起. import java.util.*; public clas

输入一个正整数n,计算出[0,n]这些整数中的二进制数没有连续3个1的数字有多少

输入一个正整数n,计算出[0,n]这些整数中的二进制数没有连续3个1的数字有多少? 例子:输入数字9,则输出结果位9.因为[0-9]中,只有数字7有连续的三个'1'出现,别的都没有,所以一共有9个数字满足要求. 分析:这个题目与求解一个正整数中'1'的个数有点类似,就是进行一些循环处理. 代码如下: 1 #include <iostream> 2 using namespace std; 3 int main() 4 { 5 int n,num_1=0,k=0; 6 cin>>n;