📌  相关文章
📜  在只读数组中查找多个重复元素中的任何一个

📅  最后修改于: 2021-10-27 07:11:47             🧑  作者: Mango

给定一个大小为 ( 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) 的块,我们可以对这个块的元素使用散列来查找哪个元素出现多次。
说明
上述方法之所以有效,是因为以下两个原因:

  1. 由于一个额外的元素,总会有一个块的计数大于 sqrt(n)。即使添加了一个额外的元素,它也只会在其中一个块中占据一个位置,从而使该块被选中。
  2. 所选块肯定有重复元素。考虑i块被选中。块的大小大于 sqrt(n) (因此,它被选中)该块中的最大不同元素 = sqrt(n)。因此,仅当范围 ( i*sqrt(n), (i+1)*sqrt(n) ] 中存在重复元素时,size 才能大于 sqrt(n)。

注意:形成的最后一个块的范围可能等于也可能不等于 sqrt(n)。因此,检查此块是否具有重复元素将与其他块不同。但是,从实现的角度来看,可以通过使用最后一个块初始化所选块来克服这个困难。这是安全的,因为必须至少选择一个块。
以下是解决此问题的分步算法

  1. 将数组分成大小为 sqrt(n) 的块。
  2. 制作一个计数数组,用于存储每个块的元素计数。
  3. 拿起计数大于sqrt(n)的块,设置最后一个块
    作为默认值。
  4. 对于属于所选块的元素,使用散列的方法(在下一步中解释)找到该块中的重复元素。
  5. 我们可以创建一个键值对的哈希数组,其中键是块中的元素,值是给定键出现的次数。这可以使用 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 现场工作专业课程学生竞争性编程现场课程