给定一个大小为 ( n+1 ) 的只读数组,找到数组中多个重复元素之一,其中该数组只包含 1 到 n 之间的整数。
只读数组意味着数组的内容不能被修改。
例子:
Input : n = 5
arr[] = {1, 1, 2, 3, 5, 4}
Output : One of the numbers repeated in the array is: 1
Input : n = 10
arr[] = {10, 1, 2, 3, 5, 4, 9, 8, 5, 6, 4}
Output : One of the numbers repeated in the array is: 4 OR 5
由于数组的大小为 n+1 并且元素的范围从 1 到 n,因此可以确认至少会有一个重复元素。
一个简单的解决方案是创建一个计数数组并存储所有元素的计数。一旦我们遇到计数大于 1 的元素,我们就返回它。此解决方案在 O(n) 时间内有效,并且需要 O(n) 额外空间。
空间优化的解决方案是将给定的范围(从 1 到 n)分成大小等于 sqrt(n) 的块。我们为每个块维护属于每个块的元素计数。现在因为数组的大小是 (n+1) 并且块的大小是 sqrt(n),那么就会有一个这样的块,其大小将大于 sqrt(n)。对于计数大于 sqrt(n) 的块,我们可以对这个块的元素使用散列来查找哪个元素出现多次。
说明:
上述方法之所以有效,是因为以下两个原因:
- 由于一个额外的元素,总会有一个块的计数大于 sqrt(n)。即使添加了一个额外的元素,它也只会在其中一个块中占据一个位置,从而使该块被选中。
- 所选块肯定有重复元素。考虑第i个块被选中。块的大小大于 sqrt(n) (因此,它被选中)该块中的最大不同元素 = sqrt(n)。因此,仅当范围 ( i*sqrt(n), (i+1)*sqrt(n) ] 中存在重复元素时,size 才能大于 sqrt(n)。
注意:形成的最后一个块的范围可能等于也可能不等于 sqrt(n)。因此,检查此块是否具有重复元素将与其他块不同。但是,从实现的角度来看,可以通过使用最后一个块初始化所选块来克服这个困难。这是安全的,因为必须至少选择一个块。
以下是解决此问题的分步算法:
- 将数组分成大小为 sqrt(n) 的块。
- 制作一个计数数组,用于存储每个块的元素计数。
- 拿起计数大于sqrt(n)的块,设置最后一个块
作为默认值。 - 对于属于所选块的元素,使用散列的方法(在下一步中解释)找到该块中的重复元素。
- 我们可以创建一个键值对的哈希数组,其中键是块中的元素,值是给定键出现的次数。这可以使用 C++ STL 中的 unordered_map 轻松实现。
下面是上述想法的实现:
C++
// C++ program to find one of the repeating
// elements in a read only array
#include
using namespace std;
// Function to find one of the repeating
// elements
int findRepeatingNumber(const int arr[], int n)
{
// Size of blocks except the
// last block is sq
int sq = sqrt(n);
// Number of blocks to incorporate 1 to
// n values blocks are numbered from 0
// to range-1 (both included)
int range = (n / sq) + 1;
// Count array maintains the count for
// all blocks
int count[range] = {0};
// Traversing the read only array and
// updating count
for (int i = 0; i <= n; i++)
{
// arr[i] belongs to block number
// (arr[i]-1)/sq i is considered
// to start from 0
count[(arr[i] - 1) / sq]++;
}
// The selected_block is set to last
// block by default. Rest of the blocks
// are checked
int selected_block = range - 1;
for (int i = 0; i < range - 1; i++)
{
if (count[i] > sq)
{
selected_block = i;
break;
}
}
// after finding block with size > sq
// method of hashing is used to find
// the element repeating in this block
unordered_map m;
for (int i = 0; i <= n; i++)
{
// checks if the element belongs to the
// selected_block
if ( ((selected_block * sq) < arr[i]) &&
(arr[i] <= ((selected_block + 1) * sq)))
{
m[arr[i]]++;
// repeating element found
if (m[arr[i]] > 1)
return arr[i];
}
}
// return -1 if no repeating element exists
return -1;
}
// Driver Program
int main()
{
// read only array, not to be modified
const int arr[] = { 1, 1, 2, 3, 5, 4 };
// array of size 6(n + 1) having
// elements between 1 and 5
int n = 5;
cout << "One of the numbers repeated in"
" the array is: "
<< findRepeatingNumber(arr, n) << endl;
}
Java
// Java program to find one of the repeating
// elements in a read only array
import java.io.*;
import java.util.*;
class GFG
{
// Function to find one of the repeating
// elements
static int findRepeatingNumber(int[] arr, int n)
{
// Size of blocks except the
// last block is sq
int sq = (int) Math.sqrt(n);
// Number of blocks to incorporate 1 to
// n values blocks are numbered from 0
// to range-1 (both included)
int range = (n / sq) + 1;
// Count array maintains the count for
// all blocks
int[] count = new int[range];
// Traversing the read only array and
// updating count
for (int i = 0; i <= n; i++)
{
// arr[i] belongs to block number
// (arr[i]-1)/sq i is considered
// to start from 0
count[(arr[i] - 1) / sq]++;
}
// The selected_block is set to last
// block by default. Rest of the blocks
// are checked
int selected_block = range - 1;
for (int i = 0; i < range - 1; i++)
{
if (count[i] > sq)
{
selected_block = i;
break;
}
}
// after finding block with size > sq
// method of hashing is used to find
// the element repeating in this block
HashMap m = new HashMap<>();
for (int i = 0; i <= n; i++)
{
// checks if the element belongs to the
// selected_block
if ( ((selected_block * sq) < arr[i]) &&
(arr[i] <= ((selected_block + 1) * sq)))
{
m.put(arr[i], 1);
// repeating element found
if (m.get(arr[i]) == 1)
return arr[i];
}
}
// return -1 if no repeating element exists
return -1;
}
// Driver code
public static void main(String args[])
{
// read only array, not to be modified
int[] arr = { 1, 1, 2, 3, 5, 4 };
// array of size 6(n + 1) having
// elements between 1 and 5
int n = 5;
System.out.println("One of the numbers repeated in the array is: " +
findRepeatingNumber(arr, n));
}
}
// This code is contributed by rachana soma
Python3
# Python 3program to find one of the repeating
# elements in a read only array
from math import sqrt
# Function to find one of the repeating
# elements
def findRepeatingNumber(arr, n):
# Size of blocks except the
# last block is sq
sq = sqrt(n)
# Number of blocks to incorporate 1 to
# n values blocks are numbered from 0
# to range-1 (both included)
range__= int((n / sq) + 1)
# Count array maintains the count for
# all blocks
count = [0 for i in range(range__)]
# Traversing the read only array and
# updating count
for i in range(0, n + 1, 1):
# arr[i] belongs to block number
# (arr[i]-1)/sq i is considered
# to start from 0
count[int((arr[i] - 1) / sq)] += 1
# The selected_block is set to last
# block by default. Rest of the blocks
# are checked
selected_block = range__ - 1
for i in range(0, range__ - 1, 1):
if (count[i] > sq):
selected_block = i
break
# after finding block with size > sq
# method of hashing is used to find
# the element repeating in this block
m = {i:0 for i in range(n)}
for i in range(0, n + 1, 1):
# checks if the element belongs
# to the selected_block
if (((selected_block * sq) < arr[i]) and
(arr[i] <= ((selected_block + 1) * sq))):
m[arr[i]] += 1
# repeating element found
if (m[arr[i]] > 1):
return arr[i]
# return -1 if no repeating element exists
return -1
# Driver Code
if __name__ == '__main__':
# read only array, not to be modified
arr = [1, 1, 2, 3, 5, 4]
# array of size 6(n + 1) having
# elements between 1 and 5
n = 5
print("One of the numbers repeated in the array is:",
findRepeatingNumber(arr, n))
# This code is contributed by
# Sahil_shelangia
C#
// C# program to find one of the repeating
// elements in a read only array
using System;
using System.Collections.Generic;
class GFG
{
// Function to find one of the repeating
// elements
static int findRepeatingNumber(int[] arr, int n)
{
// Size of blocks except the
// last block is sq
int sq = (int) Math.Sqrt(n);
// Number of blocks to incorporate 1 to
// n values blocks are numbered from 0
// to range-1 (both included)
int range = (n / sq) + 1;
// Count array maintains the count for
// all blocks
int[] count = new int[range];
// Traversing the read only array and
// updating count
for (int i = 0; i <= n; i++)
{
// arr[i] belongs to block number
// (arr[i]-1)/sq i is considered
// to start from 0
count[(arr[i] - 1) / sq]++;
}
// The selected_block is set to last
// block by default. Rest of the blocks
// are checked
int selected_block = range - 1;
for (int i = 0; i < range - 1; i++)
{
if (count[i] > sq)
{
selected_block = i;
break;
}
}
// after finding block with size > sq
// method of hashing is used to find
// the element repeating in this block
Dictionary m = new Dictionary();
for (int i = 0; i <= n; i++)
{
// checks if the element belongs to the
// selected_block
if ( ((selected_block * sq) < arr[i]) &&
(arr[i] <= ((selected_block + 1) * sq)))
{
m.Add(arr[i], 1);
// repeating element found
if (m[arr[i]] == 1)
return arr[i];
}
}
// return -1 if no repeating element exists
return -1;
}
// Driver code
public static void Main(String []args)
{
// read only array, not to be modified
int[] arr = { 1, 1, 2, 3, 5, 4 };
// array of size 6(n + 1) having
// elements between 1 and 5
int n = 5;
Console.WriteLine("One of the numbers repeated in the array is: " +
findRepeatingNumber(arr, n));
}
}
// This code contributed by Rajput-Ji
Javascript
输出:
One of the numbers repeated in the array is: 1
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。