Cấu trúc dữ liệu: chèn, xóa, chứa, lấy phần tử ngẫu nhiên, tất cả tại O (1)


94

Tôi đã được đưa ra vấn đề này trong một cuộc phỏng vấn. Bạn sẽ trả lời như thế nào?

Thiết kế cấu trúc dữ liệu cung cấp các hoạt động sau trong thời gian O (1):

  • chèn
  • tẩy
  • chứa đựng
  • nhận phần tử ngẫu nhiên

Chúng tôi có thể chấp nhận các hạn chế bổ sung về loại dữ liệu không? như không có bản sao vv
Sanjeevakumar Hiremath

Chắc chắn, không có bản sao, bạn thậm chí có thể sử dụng cấu trúc dữ liệu được xây dựng trong một ngôn ngữ như java hoặc c #.
guildner

1
Tôi lưu ý rằng không có thông số kỹ thuật lại: đã đặt hàng / chưa có thứ tự
Charles Duffy

7
Tôi biết bài đăng này đã được trả lời tuy nhiên đối với tôi sẽ có ý nghĩa hơn nếu họ muốn bạn cung cấp o (1) quyền truy cập ngẫu nhiên thay vì lấy một phần tử ngẫu nhiên.
ramsinb

Bạn đã tìm thấy giải pháp chính xác cho điều này?
Balaji Boggaram Ramanarayan

Câu trả lời:


142

Hãy xem xét một cấu trúc dữ liệu bao gồm một bảng băm H và một mảng A. Các khóa bảng băm là các phần tử trong cấu trúc dữ liệu và các giá trị là vị trí của chúng trong mảng.

  1. insert (value): nối giá trị vào mảng và đặt i là chỉ số của nó trong A. Đặt H [value] = i.
  2. remove (value): Chúng ta sẽ thay thế ô chứa giá trị trong A bằng phần tử cuối cùng trong A. gọi d là phần tử cuối cùng trong mảng A tại chỉ số m. đặt tôi là H [giá trị], chỉ số trong mảng giá trị cần loại bỏ. Đặt A [i] = d, H [d] = i, giảm kích thước của mảng đi một và xóa giá trị khỏi H.
  3. chứa (giá trị): trả về H.contains (giá trị)
  4. getRandomElement (): cho r = ngẫu nhiên (kích thước hiện tại của A). trả về A [r].

vì mảng cần tự động tăng kích thước, nó sẽ được khấu hao O (1) để thêm một phần tử, nhưng tôi đoán điều đó không sao.


Điều này gần với những gì tôi có, nhưng tôi đã bỏ lỡ việc sử dụng chính các yếu tố làm chìa khóa .... Tôi biết tôi đã gần đạt được, nhưng điều này thực sự đóng đinh nó trên đầu!
guildner

Thật thú vị khi tôi nhận được câu hỏi này trên màn hình điện thoại Google và sau một thời gian đấu tranh vẫn gặp phải giải pháp tương tự. Tôi đã hoàn thành một triển khai một chút và gán cho màn hình điện thoại thứ hai.
Andrey Talnikov,

Kết hợp giá trị cho mảng: nó như thế nào là O (1)?
Balaji Boggaram Ramanarayan

4
@aamadmi - tốt, tôi đoán là trong Java. Trong mã giả, hàm chứa sẽ hoạt động tốt :)
r0u1i

4
Tại sao cần phải có mảng, tại sao chúng ta không thể sử dụng hashmap.
Ankit Zalani

22

Tra cứu O (1) ngụ ý một cấu trúc dữ liệu được băm .

Bằng cách so sánh:

  • O (1) chèn / xóa với tra cứu O (N) ngụ ý một danh sách được liên kết.
  • Tra cứu O (1) insert, O (N) delete và O (N) ngụ ý danh sách được hỗ trợ bởi mảng
  • O (logN) insert / delete / lookup ngụ ý một cây hoặc một đống.

Đó là một khởi đầu, nhưng những gì về yêu cầu cuối cùng? Bạn có thể lấy một phần tử ngẫu nhiên (với xác suất bằng nhau cho mỗi phần tử trong cấu trúc dữ liệu) từ cấu trúc dữ liệu được băm không?
guildner

1
@ lag1980, tôi đoán bạn có thể:hashtable.get((int)(Math.random()*hashtable.size()));
CMR

3
Hmmm, tôi không biết bất kỳ hashtables nào cho phép bạn nhận một phần tử như vậy, và nếu có, tôi không thể tưởng tượng rằng đây sẽ là một phép toán thời gian liên tục. Tôi muốn được chứng minh là sai về một trong hai số.
guildner

@ lag1980 ... bạn có thể dễ dàng làm điều đó trong thời gian không đổi giống như cách vectơ của Clojure là "thời gian không đổi" - log32 (N) khi các giá trị có thể có của N bị phần cứng của bạn hạn chế sao cho giá trị log32 () lớn nhất có thể là ... một cái gì đó như 7, là thời gian hiệu quả không đổi.
Charles Duffy

Theo "danh sách được hỗ trợ bởi mảng", ý bạn là: mảng?
Hengameh

5

Bạn có thể không thích điều này, bởi vì họ có thể đang tìm kiếm một giải pháp thông minh, nhưng đôi khi bạn phải trả giá nếu dính vào súng của bạn ... Một bảng băm đã đáp ứng các yêu cầu - có lẽ tổng thể tốt hơn bất kỳ điều gì khác (mặc dù rõ ràng là không đổi phân bổ thời gian và với các thỏa hiệp khác nhau đối với các giải pháp khác).

Yêu cầu phức tạp là lựa chọn "phần tử ngẫu nhiên": trong một bảng băm, bạn sẽ cần phải quét hoặc thăm dò để tìm một phần tử như vậy.

Đối với địa chỉ băm / mở đóng, khả năng bất kỳ nhóm nào đã cho bị chiếm giữ là có size() / capacity(), nhưng điều quan trọng là điều này thường được giữ trong một phạm vi nhân không đổi bằng cách triển khai bảng băm (ví dụ: bảng có thể được giữ lớn hơn nội dung hiện tại của nó bằng 1,2x đến ~ 10x tùy thuộc vào hiệu suất / điều chỉnh bộ nhớ). Điều này có nghĩa là trung bình chúng ta có thể mong đợi tìm kiếm 1,2 đến 10 thùng - hoàn toàn độc lập với tổng kích thước của thùng chứa; khấu hao O (1).

Tôi có thể tưởng tượng ra hai cách tiếp cận đơn giản (và nhiều cách tiếp cận khác lạ lùng hơn):

  • tìm kiếm tuyến tính từ một nhóm ngẫu nhiên

    • coi các nhóm trống / nắm giữ giá trị ala "--AC ----- B - D": bạn có thể nói rằng lựa chọn "ngẫu nhiên" đầu tiên là công bằng mặc dù nó ủng hộ B, vì B không có xác suất được ưu tiên nữa hơn các phần tử khác, nhưng nếu bạn đang thực hiện lặp đi lặp lại các lựa chọn "ngẫu nhiên" sử dụng cùng các giá trị thì rõ ràng việc B được ưu tiên nhiều lần có thể là điều không mong muốn (mặc dù không có gì trong câu hỏi yêu cầu xác suất)
  • thử nhiều lần các nhóm ngẫu nhiên cho đến khi bạn tìm thấy một nhóm được điền

    • "only" dung lượng () / kích thước () nhóm trung bình được truy cập (như trên) - nhưng trong điều kiện thực tế đắt hơn vì tạo số ngẫu nhiên tương đối tốn kém và vô cùng tệ nếu hành vi trường hợp xấu nhất không thể xảy ra ...
      • một thỏa hiệp nhanh hơn sẽ là sử dụng danh sách các hiệu số ngẫu nhiên được tạo trước từ nhóm được chọn ngẫu nhiên ban đầu, tính% chúng vào số lượng nhóm

Không phải là một giải pháp tuyệt vời, nhưng vẫn có thể là một thỏa hiệp tổng thể tốt hơn so với chi phí bộ nhớ và hiệu suất của việc duy trì mảng chỉ mục thứ hai mọi lúc.


3

Giải pháp tốt nhất có lẽ là bảng băm + mảng, nó thực sự nhanh và xác định.

Nhưng câu trả lời được đánh giá thấp nhất (chỉ cần sử dụng bảng băm!) Cũng thực sự tuyệt vời!

  • bảng băm có băm lại hoặc lựa chọn nhóm mới (tức là một phần tử trên mỗi nhóm, không có danh sách được liên kết)
  • getRandom () liên tục cố gắng chọn một nhóm ngẫu nhiên cho đến khi nó trống.
  • dưới dạng không an toàn, có thể getRandom (), sau khi N (số phần tử) thử không thành công, hãy chọn một chỉ mục ngẫu nhiên thứ i trong [0, N-1] và sau đó đi qua bảng băm tuyến tính và chọn phần tử thứ i .

Mọi người có thể không thích điều này vì "có thể có vòng lặp vô hạn", và tôi đã thấy những người rất thông minh cũng có phản ứng này, nhưng sai rồi! Các sự kiện không thể xảy ra vô hạn chỉ không xảy ra.

Giả sử hoạt động tốt của nguồn giả ngẫu nhiên của bạn - không khó để thiết lập cho hành vi cụ thể này - và bảng băm luôn đầy ít nhất 20%, thật dễ dàng để thấy rằng:

Sẽ không bao giờ xảy ra trường hợp getRandom () phải thử hơn 1000 lần. Chỉ là không bao giờ . Thật vậy, xác suất của một sự kiện như vậy là 0,8 ^ 1000, là 10 ^ -97 - vì vậy chúng ta sẽ phải lặp lại nó 10 ^ 88 lần để có một cơ hội trong một tỷ sự kiện xảy ra một lần. Ngay cả khi chương trình này được chạy toàn thời gian trên tất cả các máy tính của loài người cho đến khi Mặt trời chết, điều này sẽ không bao giờ xảy ra.


1
Nếu bạn liên tục chọn để chọn một xô ngẫu nhiên có giá trị, làm thế nào trên trái đất là tồi tệ nhất trường hợp dẫn đến O (1) khi bạn chọn một yếu tố ngẫu nhiên
Balaji Boggaram Ramanarayan

@ user1147505 - bạn lấy con số này ở đâu: "0.8 ^ 1000"?
Hengameh

Làm thế nào bạn đạt được điều này: "bảng băm luôn đầy ít nhất 20%"
Hengameh

Bạn có thể vui lòng viết phương thức mà bạn có thể chọn một nhóm ngẫu nhiên không?
Hengameh

3

Đối với câu hỏi này, tôi sẽ sử dụng hai Cấu trúc dữ liệu

  • Bản đồ băm
  • ArrayList / Array / Double LinkedList.

Các bước: -

  1. Chèn: - Kiểm tra xem X đã có trong HashMap chưa - Độ phức tạp thời gian O (1). nếu không có thì Thêm vào cuối ArrayList - Độ phức tạp thời gian O (1). thêm nó trong HashMap cũng x làm khóa và Chỉ mục cuối cùng làm giá trị - Độ phức tạp thời gian O (1).
  2. Loại bỏ: - Kiểm tra xem X có trong HashMap - Độ phức tạp thời gian O (1). Nếu có thì hãy tìm chỉ mục của nó và xóa nó khỏi HashMap - Độ phức tạp thời gian O (1). hoán đổi phần tử này với phần tử cuối cùng trong ArrayList và xóa phần tử cuối cùng - Độ phức tạp thời gian O (1). Cập nhật chỉ mục của Phần tử cuối cùng trong HashMap - Độ phức tạp thời gian O (1).
  3. GetRandom: - Tạo số ngẫu nhiên từ 0 đến chỉ mục cuối cùng của ArrayList. trả về phần tử ArrayList tại chỉ số ngẫu nhiên được tạo - Độ phức tạp thời gian O (1).
  4. Tìm kiếm: - Xem trong HashMap cho x làm khóa. - Độ phức tạp thời gian O (1).

Mã: -

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;

            }
        }
    }

}

- Độ phức tạp thời gian O (1). - Độ phức tạp không gian O (N).


1

Đây là một giải pháp C # cho vấn đề đó mà tôi đã đưa ra cách đây ít lâu khi được hỏi cùng một câu hỏi. Nó thực hiện Thêm, Xóa, Chứa và Ngẫu nhiên cùng với các giao diện .NET tiêu chuẩn khác. Không phải là bạn sẽ cần phải thực hiện nó một cách chi tiết như vậy trong một cuộc phỏng vấn nhưng thật tuyệt khi có một giải pháp cụ thể để xem xét ...

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&lt;T&gt;"/> class.
    /// </summary>
    public Bag()
        : this(0)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Bag&lt;T&gt;"/> 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&lt;T&gt;"/> 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;

        }
    }
}

Tôi không chắc rằng điều này sẽ hoạt động nếu bạn có các số trùng lặp.
AlexIIP

Nó không xử lý các bản sao vì @guildner cho biết rằng không có bản sao nào trong các nhận xét của câu hỏi. Nếu một bản sao được thêm vào một ArgumentExceptionvới thông báo "Một mục có cùng khóa đã được thêm." sẽ được ném (từ Từ điển chỉ mục cơ bản).
Scott Lerch

1

Chúng tôi có thể sử dụng băm để hỗ trợ các hoạt động trong thời gian Θ (1).

insert (x) 1) Kiểm tra xem x đã có mặt hay chưa bằng cách thực hiện tra cứu bản đồ băm. 2) Nếu không có, hãy chèn nó vào cuối mảng. 3) Thêm vào bảng băm cũng được, x được thêm làm chỉ mục chính và chỉ số mảng cuối cùng làm chỉ mục.

remove (x) 1) Kiểm tra xem x có hiện diện hay không bằng cách thực hiện tra cứu bản đồ băm. 2) Nếu có, hãy tìm chỉ mục của nó và xóa nó khỏi bản đồ băm. 3) Hoán đổi phần tử cuối cùng với phần tử này trong mảng và xóa phần tử cuối cùng. Việc hoán đổi được thực hiện vì phần tử cuối cùng có thể được loại bỏ trong thời gian O (1). 4) Cập nhật chỉ mục của phần tử cuối cùng trong bản đồ băm.

getRandom () 1) Tạo một số ngẫu nhiên từ 0 đến chỉ mục cuối cùng. 2) Trả về phần tử mảng tại chỉ mục được tạo ngẫu nhiên.

tìm kiếm (x) Thực hiện tìm kiếm x trong bản đồ băm.


1

Mặc dù đây là cách cũ, nhưng vì không có câu trả lời trong C ++, đây là hai xu của tôi.

#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<<" ";
        }
    }
};

Đây là một đoạn mã khách hàng để kiểm tra giải pháp.

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;
}

0

Trong C # 3.0 + .NET Framework 4, giá trị chung Dictionary<TKey,TValue>thậm chí còn tốt hơn Hashtable vì bạn có thể sử dụng System.Linqphương thức mở rộng ElementAt()để lập chỉ mục vào mảng động cơ bản nơi các KeyValuePair<TKey,TValue>phần tử được lưu trữ:

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;
}

Tuy nhiên, theo như tôi biết, một Hashtable (hoặc con cháu của Từ điển) không phải là giải pháp thực sự cho vấn đề này vì Put () chỉ có thể được khấu hao O (1), không đúng O (1), vì nó là O (N ) ở ranh giới thay đổi kích thước động.

Có một giải pháp thực sự cho vấn đề này? Tất cả những gì tôi có thể nghĩ là nếu bạn chỉ định dung lượng ban đầu của Dictionary / Hashtable theo thứ tự độ lớn vượt quá những gì bạn dự đoán từng cần, thì bạn sẽ nhận được các phép toán O (1) vì bạn không bao giờ cần thay đổi kích thước.


Nếu bạn rất nghiêm khắc về bảng băm là gì, thì việc thay đổi kích thước O (N) là không thể tránh khỏi. Tuy nhiên, một số triển khai thỏa hiệp để giảm chi phí thay đổi kích thước - ví dụ: bằng cách giữ lại bảng hiện có trong khi thêm một giây gấp đôi kích thước hoặc cố gắng thay đổi kích thước bảng hiện có tại chỗ (sau khi sắp xếp cẩn thận không gian địa chỉ ảo và kích thước bảng trên ranh giới trang nên không yêu cầu sao chép, có thể yêu cầu bản đồ bộ nhớ chứ không phải bản đồ mới / malloc), sau đó tìm kiếm trong vùng mới lớn hơn trước khi quay trở lại vùng nhỏ hơn (trong mô hình tại chỗ bằng cách sửa đổi chặt chẽ hơn), với logic di chuyển phần tử.
Tony Delroy

0

Tôi đồng ý với Anon. Ngoại trừ yêu cầu cuối cùng trong đó yêu cầu nhận được một phần tử ngẫu nhiên với sự công bằng như nhau, tất cả các yêu cầu khác chỉ có thể được giải quyết bằng cách sử dụng một DS dựa trên băm duy nhất. Tôi sẽ chọn HashSet cho điều này trong Java. Mô-đun mã băm của một phần tử sẽ cung cấp cho tôi chỉ số không của mảng bên dưới trong thời gian O (1). Tôi có thể sử dụng nó cho các hoạt động thêm, bớt và chứa.


0

Chúng tôi không thể làm điều này bằng cách sử dụng HashSet của Java? Nó cung cấp chèn, xóa, tìm kiếm tất cả trong O (1) theo mặc định. Đối với getRandom, chúng ta có thể sử dụng trình lặp của Set mà luôn cho hành vi ngẫu nhiên. Chúng tôi chỉ cần lặp lại phần tử đầu tiên từ tập hợp mà không cần lo lắng về phần còn lại

public void getRandom(){
    Iterator<integer> sitr = s.iterator();
    Integer x = sitr.next();    
    return x;
}

0
/* 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`
    }
}

-2

Tại sao chúng ta không sử dụng epoch% mảng để tìm phần tử ngẫu nhiên. Tìm kích thước mảng là O (n) nhưng độ phức tạp khấu hao sẽ là O (1).


-3

Tôi nghĩ chúng ta có thể sử dụng danh sách liên kết kép với bảng băm. khóa sẽ là phần tử và giá trị liên kết của nó sẽ là nút trong danh sách liên kết kép.

  1. insert (H, E): chèn nút trong danh sách liên kết kép và tạo mục nhập là H [E] = node; O (1)
  2. xóa (H, E): lấy địa chỉ nút theo H (E), đi đến trước của nút này và xóa và làm cho H (E) là NULL, do đó O (1)
  3. chứa (H, E) và getRandom (H) là obviuosly O (1)

Điều này không có ý nghĩa.
innosam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.