J'ai eu ce problème en entrevue. Comment auriez-vous répondu?
Concevez une structure de données qui offre les opérations suivantes dans O(1):
Considérons une structure de données composée d'une table de hachage H et d'un tableau A. Les clés de table de hachage sont les éléments de la structure de données et les valeurs sont leurs positions dans le tableau.
comme le tableau doit augmenter automatiquement en taille, il va falloir amortir O(1) pour ajouter un élément, mais je suppose que ça va.
La recherche O (1) implique une structure de données hachée .
Par comparaison:
Cela ne vous plaira peut-être pas, car ils recherchent probablement une solution intelligente, mais il est parfois avantageux de s'en tenir à leurs armes ... A La table de hachage répond déjà aux exigences - probablement globalement mieux que tout le reste évidemment en temps constant amorti et avec des compromis différents d’autres solutions).
L'exigence qui est délicate est la sélection "élément aléatoire": dans une table de hachage, vous devrez analyser ou rechercher un tel élément.
Pour un adressage en hachage fermé/ouvert, la possibilité d'occuper un compartiment donné est size() / capacity()
, mais elle est généralement conservée dans une plage multiplicative constante par une implémentation de table de hachage (par exemple, la table peut être plus grande que son contenu actuel, par exemple. 1.2 x à ~ 10x selon les performances/le réglage de la mémoire). Cela signifie en moyenne que nous pouvons nous attendre à rechercher entre 1,2 et 10 seaux - totalement indépendants de la taille totale du conteneur; amorti O (1).
Je peux imaginer deux approches simples (et beaucoup plus délicates):
rechercher linéairement à partir d'un seau aléatoire
essayez plusieurs fois des seaux aléatoires jusqu'à ce que vous en trouviez un rempli
Pas une bonne solution, mais peut-être un meilleur compromis que les frais de mémoire et de performance liés au maintien d’un second tableau d’index à tout moment.
La meilleure solution est probablement la table de hachage + tableau, c'est très rapide et déterministe.
Mais la réponse la moins bien notée (utilisez simplement une table de hachage!) Est également excellente!
Les gens pourraient ne pas aimer cela à cause de "boucles infinies possibles", et j'ai vu des gens très intelligents réagir aussi, mais c'est faux! Des événements infiniment improbables ne font que not ne se produisent pas.
En supposant le bon comportement de votre source pseudo-aléatoire - ce qui n'est pas difficile à établir pour ce comportement particulier - et que les tables de hachage sont toujours remplies à au moins 20%, il est facile de voir que:
Il arrivera jamais que getRandom () doive essayer plus de 1000 fois. Juste jamais. En effet, la probabilité d’un tel événement est de 0,8 ^ 1 000, ce qui correspond à 10 ^ -97 - nous devrions donc le répéter 10 fois sur 88 pour avoir une chance sur un milliard de se produire une fois. Même si ce programme fonctionnait à plein temps sur tous les ordinateurs de l’humanité jusqu’à la mort du Soleil, cela se produirait jamais.
Pour cette question, je vais utiliser deux structures de données
Pas :-
Code: -
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.HashMap;
import Java.util.HashSet;
import Java.util.LinkedList;
import Java.util.List;
import Java.util.Random;
import Java.util.Scanner;
public class JavaApplication1 {
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
ArrayList<Integer> al =new ArrayList<Integer>();
HashMap<Integer,Integer> mp = new HashMap<Integer,Integer>();
while(true){
System.out.println("**menu**");
System.out.println("1.insert");
System.out.println("2.remove");
System.out.println("3.search");
System.out.println("4.rendom");
int ch = sc.nextInt();
switch(ch){
case 1 : System.out.println("Enter the Element ");
int a = sc.nextInt();
if(mp.containsKey(a)){
System.out.println("Element is already present ");
}
else{
al.add(a);
mp.put(a, al.size()-1);
}
break;
case 2 : System.out.println("Enter the Element Which u want to remove");
a = sc.nextInt();
if(mp.containsKey(a)){
int size = al.size();
int index = mp.get(a);
int last = al.get(size-1);
Collections.swap(al, index, size-1);
al.remove(size-1);
mp.put(last, index);
System.out.println("Data Deleted");
}
else{
System.out.println("Data Not found");
}
break;
case 3 : System.out.println("Enter the Element to Search");
a = sc.nextInt();
if(mp.containsKey(a)){
System.out.println(mp.get(a));
}
else{
System.out.println("Data Not Found");
}
break;
case 4 : Random rm = new Random();
int index = rm.nextInt(al.size());
System.out.println(al.get(index));
break;
}
}
}
}
- Complexité temporelle O (1) .-- Complexité spatiale O (N).
Voici une solution C # à ce problème que j’ai trouvé il ya quelque temps quand on me posait la même question. Il implémente Add, Remove, Contains et Random avec d'autres interfaces .NET standard. Non pas que vous ayez besoin de la mettre en œuvre de manière aussi détaillée lors d'une interview, mais c'est bien d'avoir une solution concrète à regarder ...
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
/// <summary>
/// This class represents an unordered bag of items with the
/// the capability to get a random item. All operations are O(1).
/// </summary>
/// <typeparam name="T">The type of the item.</typeparam>
public class Bag<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable
{
private Dictionary<T, int> index;
private List<T> items;
private Random Rand;
private object syncRoot;
/// <summary>
/// Initializes a new instance of the <see cref="Bag<T>"/> class.
/// </summary>
public Bag()
: this(0)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Bag<T>"/> class.
/// </summary>
/// <param name="capacity">The capacity.</param>
public Bag(int capacity)
{
this.index = new Dictionary<T, int>(capacity);
this.items = new List<T>(capacity);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bag<T>"/> class.
/// </summary>
/// <param name="collection">The collection.</param>
public Bag(IEnumerable<T> collection)
{
this.items = new List<T>(collection);
this.index = this.items
.Select((value, index) => new { value, index })
.ToDictionary(pair => pair.value, pair => pair.index);
}
/// <summary>
/// Get random item from bag.
/// </summary>
/// <returns>Random item from bag.</returns>
/// <exception cref="System.InvalidOperationException">
/// The bag is empty.
/// </exception>
public T Random()
{
if (this.items.Count == 0)
{
throw new InvalidOperationException();
}
if (this.Rand == null)
{
this.Rand = new Random();
}
int randomIndex = this.Rand.Next(0, this.items.Count);
return this.items[randomIndex];
}
/// <summary>
/// Adds the specified item.
/// </summary>
/// <param name="item">The item.</param>
public void Add(T item)
{
this.index.Add(item, this.items.Count);
this.items.Add(item);
}
/// <summary>
/// Removes the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns></returns>
public bool Remove(T item)
{
// Replace index of value to remove with last item in values list
int keyIndex = this.index[item];
T lastItem = this.items[this.items.Count - 1];
this.items[keyIndex] = lastItem;
// Update index in dictionary for last item that was just moved
this.index[lastItem] = keyIndex;
// Remove old value
this.index.Remove(item);
this.items.RemoveAt(this.items.Count - 1);
return true;
}
/// <inheritdoc />
public bool Contains(T item)
{
return this.index.ContainsKey(item);
}
/// <inheritdoc />
public void Clear()
{
this.index.Clear();
this.items.Clear();
}
/// <inheritdoc />
public int Count
{
get { return this.items.Count; }
}
/// <inheritdoc />
public void CopyTo(T[] array, int arrayIndex)
{
this.items.CopyTo(array, arrayIndex);
}
/// <inheritdoc />
public bool IsReadOnly
{
get { return false; }
}
/// <inheritdoc />
public IEnumerator<T> GetEnumerator()
{
foreach (var value in this.items)
{
yield return value;
}
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
/// <inheritdoc />
public void CopyTo(Array array, int index)
{
this.CopyTo(array as T[], index);
}
/// <inheritdoc />
public bool IsSynchronized
{
get { return false; }
}
/// <inheritdoc />
public object SyncRoot
{
get
{
if (this.syncRoot == null)
{
Interlocked.CompareExchange<object>(
ref this.syncRoot,
new object(),
null);
}
return this.syncRoot;
}
}
}
Bien que ce soit bien vieux, mais comme il n'y a pas de réponse en C++, voici mes deux sous.
#include <vector>
#include <unordered_map>
#include <stdlib.h>
template <typename T> class bucket{
int size;
std::vector<T> v;
std::unordered_map<T, int> m;
public:
bucket(){
size = 0;
std::vector<T>* v = new std::vector<T>();
std::unordered_map<T, int>* m = new std::unordered_map<T, int>();
}
void insert(const T& item){
//prevent insertion of duplicates
if(m.find(item) != m.end()){
exit(-1);
}
v.Push_back(item);
m.emplace(item, size);
size++;
}
void remove(const T& item){
//exits if the item is not present in the list
if(m[item] == -1){
exit(-1);
}else if(m.find(item) == m.end()){
exit(-1);
}
int idx = m[item];
m[v.back()] = idx;
T itm = v[idx];
v.insert(v.begin()+idx, v.back());
v.erase(v.begin()+idx+1);
v.insert(v.begin()+size, itm);
v.erase(v.begin()+size);
m[item] = -1;
v.pop_back();
size--;
}
T& getRandom(){
int idx = Rand()%size;
return v[idx];
}
bool lookup(const T& item){
if(m.find(item) == m.end()) return false;
return true;
}
//method to check that remove has worked
void print(){
for(auto it = v.begin(); it != v.end(); it++){
std::cout<<*it<<" ";
}
}
};
Voici un morceau de code client pour tester la solution.
int main() {
bucket<char>* b = new bucket<char>();
b->insert('d');
b->insert('k');
b->insert('l');
b->insert('h');
b->insert('j');
b->insert('z');
b->insert('p');
std::cout<<b->random()<<std::endl;
b->print();
std::cout<<std::endl;
b->remove('h');
b->print();
return 0;
}
Ne pouvons-nous pas faire cela en utilisant HashSet of Java? Il fournit insert, del, recherche tout dans O(1) par défaut . Pour getRandom, nous pouvons utiliser l'itérateur de Set qui donne de toute façon un comportement aléatoire. On peut simplement itérer le premier élément d'un ensemble sans se soucier du reste des éléments
public void getRandom(){
Iterator<integer> sitr = s.iterator();
Integer x = sitr.next();
return x;
}
Dans C # 3.0 + .NET Framework 4, un Dictionary<TKey,TValue>
générique est encore meilleur qu'un Hashtable car vous pouvez utiliser la méthode d'extension System.Linq
ElementAt()
pour indexer dans le tableau dynamique sous-jacent où les éléments KeyValuePair<TKey,TValue>
sont stockés:
using System.Linq;
Random _generator = new Random((int)DateTime.Now.Ticks);
Dictionary<string,object> _elements = new Dictionary<string,object>();
....
Public object GetRandom()
{
return _elements.ElementAt(_generator.Next(_elements.Count)).Value;
}
Cependant, autant que je sache, une table de hachage (ou sa descendance de dictionnaire) n’est pas une solution réelle à ce problème car Put () ne peut être amorti que O(1), pas vrai O(1), car il s'agit de O(N) à la limite de redimensionnement dynamique.
Y at-il une vraie solution à ce problème? Tout ce que je peux penser, c’est que si vous spécifiez une capacité initiale de Dictionary/Hashtable d’un ordre de grandeur supérieur à ce que vous prévoyez d’avoir besoin, vous obtiendrez O(1) opération car vous n’auriez jamais besoin de la redimensionner.
Je suis d'accord avec Anon. À l'exception de la dernière exigence, où l'obtention d'un élément aléatoire avec une équité égale est requise, toutes les autres exigences peuvent être traitées uniquement à l'aide d'un seul DS basé sur Hash. Je vais choisir HashSet pour cela en Java. Le modulo de code de hachage d'un élément me donnera le numéro d'index du tableau sous-jacent dans O(1) temps. Je peux l'utiliser pour ajouter, supprimer et contient des opérations.
Nous pouvons utiliser le hachage pour prendre en charge les opérations dans (1) temps.
insert (x) 1) Vérifiez si x est déjà présent en effectuant une recherche de carte de hachage . 2) S'il n'est pas présent, insérez-le à la fin du tableau . 3) Ajoutez également dans la table de hachage, x est ajouté en tant que clé et le dernier index du tableau en tant qu'index.
remove (x) 1) Vérifiez si x est présent en effectuant une recherche de carte de hachage . 2) Si présent, recherchez son index et supprimez-le de la carte de hachage . 3) Permutez le dernier élément avec cet élément dans le tableau et supprime le dernier élément . L'échange est effectué car le dernier élément peut être supprimé dans O(1) time . 4) Met à jour l'index du dernier élément dans le hachage carte.
getRandom () 1) Générez un nombre aléatoire compris entre 0 et le dernier index . 2) Renvoyez l'élément de tableau à l'index généré de manière aléatoire.
search (x) Fais une recherche de x dans la carte de hachage.
/* Java program to design a data structure that support folloiwng operations
in Theta(n) time
a) Insert
b) Delete
c) Search
d) getRandom */
import Java.util.*;
// class to represent the required data structure
class MyDS
{
ArrayList<Integer> arr; // A resizable array
// A hash where keys are array elements and vlaues are
// indexes in arr[]
HashMap<Integer, Integer> hash;
// Constructor (creates arr[] and hash)
public MyDS()
{
arr = new ArrayList<Integer>();
hash = new HashMap<Integer, Integer>();
}
// A Theta(1) function to add an element to MyDS
// data structure
void add(int x)
{
// If ekement is already present, then noting to do
if (hash.get(x) != null)
return;
// Else put element at the end of arr[]
int s = arr.size();
arr.add(x);
// And put in hash also
hash.put(x, s);
}
// A Theta(1) function to remove an element from MyDS
// data structure
void remove(int x)
{
// Check if element is present
Integer index = hash.get(x);
if (index == null)
return;
// If present, then remove element from hash
hash.remove(x);
// Swap element with last element so that remove from
// arr[] can be done in O(1) time
int size = arr.size();
Integer last = arr.get(size-1);
Collections.swap(arr, index, size-1);
// Remove last element (This is O(1))
arr.remove(size-1);
// Update hash table for new index of last element
hash.put(last, index);
}
// Returns a random element from MyDS
int getRandom()
{
// Find a random index from 0 to size - 1
Random Rand = new Random(); // Choose a different seed
int index = Rand.nextInt(arr.size());
// Return element at randomly picked index
return arr.get(index);
}
// Returns index of element if element is present, otherwise null
Integer search(int x)
{
return hash.get(x);
}
}
// Driver class
class Main
{
public static void main (String[] args)
{
MyDS ds = new MyDS();
ds.add(10);
ds.add(20);
ds.add(30);
ds.add(40);
System.out.println(ds.search(30));
ds.remove(20);
ds.add(50);
System.out.println(ds.search(50));
System.out.println(ds.getRandom());`enter code here`
}
}