โครงสร้างข้อมูล: แทรกลบประกอบด้วยรับองค์ประกอบแบบสุ่มทั้งหมดที่ O (1)


96

ฉันได้รับปัญหานี้ในการสัมภาษณ์ คุณจะตอบว่าอย่างไร?

ออกแบบโครงสร้างข้อมูลที่มีการดำเนินการต่อไปนี้ใน O (1) เวลา:

  • แทรก
  • ลบ
  • ประกอบด้วย
  • รับองค์ประกอบแบบสุ่ม

เราสามารถสมมติข้อ จำกัด เพิ่มเติมเกี่ยวกับประเภทของข้อมูลได้หรือไม่? เช่นไม่มีรายการซ้ำ ฯลฯ
Sanjeevakumar Hiremath

แน่นอนว่าไม่มีรายการที่ซ้ำกันคุณยังสามารถใช้โครงสร้างข้อมูลในตัวในภาษาเช่น java หรือ c #
guildner

1
ฉันทราบว่าไม่มีข้อกำหนดเกี่ยวกับการสั่งซื้อ / ไม่เรียงลำดับ
Charles Duffy

7
ฉันรู้ว่าโพสต์นี้ได้รับคำตอบแล้ว แต่สำหรับฉันแล้วมันจะสมเหตุสมผลกว่าถ้าพวกเขาต้องการให้คุณให้ o (1) การเข้าถึงแบบสุ่มแทนที่จะได้รับองค์ประกอบแบบสุ่ม
ramsinb

คุณพบวิธีแก้ปัญหาที่ถูกต้องหรือไม่?
Balaji Boggaram Ramanarayan

คำตอบ:


145

พิจารณาโครงสร้างข้อมูลที่ประกอบด้วยแฮชแท็ก H และอาร์เรย์ A คีย์แฮชแท็กคือองค์ประกอบในโครงสร้างข้อมูลและค่าคือตำแหน่งในอาร์เรย์

  1. แทรก (ค่า): ต่อท้ายค่าในอาร์เรย์และให้ฉันเป็นดัชนีใน A. Set H [value] = i
  2. ลบ (ค่า): เราจะแทนที่เซลล์ที่มีค่าใน A ด้วยองค์ประกอบสุดท้ายใน A. ให้ d เป็นองค์ประกอบสุดท้ายในอาร์เรย์ A ที่ดัชนี m ให้ฉันเป็น H [ค่า] ดัชนีในอาร์เรย์ของค่าที่จะลบออก ตั้งค่า A [i] = d, H [d] = i ลดขนาดของอาร์เรย์ทีละรายการและลบค่าออกจาก H.
  3. ประกอบด้วย (ค่า): ส่งคืน H.contains (ค่า)
  4. getRandomElement (): ให้ r = สุ่ม (ขนาดปัจจุบันของ A) ส่งคืน A [r]

เนื่องจากอาร์เรย์จำเป็นต้องเพิ่มขนาดโดยอัตโนมัติจึงต้องตัดจำหน่าย O (1) เพื่อเพิ่มองค์ประกอบ แต่ฉันคิดว่าไม่เป็นไร


นี่ใกล้เคียงกับที่ฉันมี แต่ฉันพลาดการใช้องค์ประกอบที่ตัวเองเป็นกุญแจ .... ฉันรู้ว่าฉันอยู่ใกล้ แต่นี่มันตอกที่หัวจริงๆ!
guildner

เป็นเรื่องน่าสนใจที่ฉันได้รับคำถามนี้บนหน้าจอโทรศัพท์ Google และหลังจากที่บางคนดิ้นรนติดอยู่กับวิธีแก้ปัญหาเดียวกัน ฉันเมาการใช้งานเล็กน้อยและกำหนดให้กับหน้าจอโทรศัพท์ที่สอง
Andrey Talnikov

APpend value ให้กับ array: O (1) เป็นอย่างไร?
Balaji Boggaram Ramanarayan

4
@aamadmi - ดีใน Java ฉันคิดว่าควร ในรหัสหลอกมีควรใช้งานได้ดี :)
r0u1i

4
ทำไมต้องใช้อาร์เรย์ทำไมเราใช้แฮชแมปไม่ได้
Ankit Zalani

22

O (1) การค้นหาหมายถึงโครงสร้างข้อมูลแฮช

โดยเปรียบเทียบ:

  • O (1) แทรก / ลบด้วยการค้นหา O (N) หมายถึงรายการที่เชื่อมโยง
  • แทรก O (1), O (N) ลบและการค้นหา O (N) แสดงถึงรายการที่สำรองด้วยอาร์เรย์
  • O (logN) แทรก / ลบ / ค้นหาหมายถึงต้นไม้หรือฮีป

นั่นคือการเริ่มต้น แต่สิ่งที่เกี่ยวกับความต้องการสุดท้าย? คุณจะได้รับองค์ประกอบแบบสุ่ม (ที่มีความน่าจะเป็นเท่ากันสำหรับแต่ละองค์ประกอบในโครงสร้างข้อมูล) จากโครงสร้างข้อมูลที่แฮช
guildner

1
@ lag1980 ฉันเดาว่าคุณทำได้:hashtable.get((int)(Math.random()*hashtable.size()));
CMR

3
อืมฉันไม่รู้ว่ามีแฮชแท็กอะไรที่ให้คุณได้องค์ประกอบแบบนั้นและถ้ามีฉันนึกไม่ถึงว่านี่จะเป็นการทำงานที่คงที่ ฉันสนใจที่จะได้รับการพิสูจน์ว่าผิดในการนับอย่างใดอย่างหนึ่ง
guildner

@ lag1980 ... คุณสามารถทำได้อย่างง่ายดายในเวลาคงที่เช่นเดียวกับเวกเตอร์ของ Clojure คือ "เวลาคงที่" - log32 (N) เมื่อค่าที่เป็นไปได้ของ N ถูก จำกัด โดยฮาร์ดแวร์ของคุณดังนั้นค่า log32 () ที่ใหญ่ที่สุดที่เป็นไปได้คือ ... บางอย่างเช่น 7 ซึ่งเป็นเวลาคงที่อย่างมีประสิทธิภาพ
Charles Duffy

โดย "array-backed list" คุณหมายถึงอาร์เรย์?
Hengameh

5

คุณอาจไม่ชอบสิ่งนี้เพราะพวกเขาอาจกำลังมองหาวิธีแก้ปัญหาที่ชาญฉลาด แต่บางครั้งมันก็จ่ายเพื่อยึดติดกับปืนของคุณ ... ตารางแฮชตอบสนองความต้องการอยู่แล้ว - อาจจะดีกว่าสิ่งอื่นใด (แม้ว่าจะเห็นได้ชัดว่าเป็นค่าคงที่ค่าตัดจำหน่าย เวลาและการประนีประนอมกับโซลูชันอื่น ๆ ที่แตกต่างกัน)

ข้อกำหนดที่ยุ่งยากคือการเลือก "องค์ประกอบแบบสุ่ม": ในตารางแฮชคุณจะต้องสแกนหรือตรวจสอบองค์ประกอบดังกล่าว

สำหรับการแฮชแบบปิด / ที่อยู่แบบเปิดโอกาสที่ที่เก็บข้อมูลใด ๆ จะถูกครอบครองsize() / capacity()แต่โดยทั่วไปแล้วสิ่งสำคัญคือสิ่งนี้จะถูกเก็บไว้ในช่วงการคูณคงที่โดยการใช้งานตารางแฮช (เช่นตารางอาจมีขนาดใหญ่กว่าเนื้อหาปัจจุบันโดยพูดว่า 1.2x ถึง ~ 10x ขึ้นอยู่กับการปรับแต่งประสิทธิภาพ / หน่วยความจำ) ซึ่งหมายความว่าโดยเฉลี่ยแล้วเราสามารถคาดหวังว่าจะค้นหา 1.2 ถึง 10 ที่เก็บข้อมูลโดยไม่ขึ้นกับขนาดทั้งหมดของคอนเทนเนอร์ ตัดจำหน่าย O (1)

ฉันนึกภาพออกสองวิธีง่ายๆ (และอีกหลายวิธีที่ดีมาก):

  • ค้นหาเชิงเส้นจากที่เก็บข้อมูลแบบสุ่ม

    • พิจารณาที่เก็บข้อมูลว่าง / ค่าที่เก็บไว้ ala "--AC ----- B - D": คุณสามารถพูดได้ว่าการเลือก "สุ่ม" ครั้งแรกนั้นยุติธรรมแม้ว่าจะชอบ B ก็ตามเพราะ B ไม่มีความเป็นไปได้ที่จะเป็นที่ชื่นชอบอีกต่อไป มากกว่าองค์ประกอบอื่น ๆ แต่ถ้าคุณทำการเลือกแบบ "สุ่ม" ซ้ำ ๆ โดยใช้ค่าเดียวกันการให้ B ซ้ำ ๆ กันอาจเป็นสิ่งที่ไม่พึงปรารถนา (ไม่มีอะไรในคำถามที่เรียกร้องความน่าจะเป็น)
  • ลองสุ่มที่เก็บข้อมูลซ้ำ ๆ จนกว่าคุณจะพบถังบรรจุ

    • "เท่านั้น" ความจุ () / ขนาด () ที่เก็บข้อมูลโดยเฉลี่ยที่เข้าชม (ตามด้านบน) - แต่ในทางปฏิบัติมีราคาแพงกว่าเนื่องจากการสร้างตัวเลขแบบสุ่มมีราคาค่อนข้างแพงและไม่ดีอย่างไม่มีที่สิ้นสุดหากพฤติกรรมที่เลวร้ายที่สุดที่ไม่น่าจะเป็นไปได้ ...
      • การประนีประนอมที่เร็วขึ้นคือการใช้รายการออฟเซ็ตสุ่มที่สร้างไว้ล่วงหน้าจากที่เก็บข้อมูลที่สุ่มเลือกครั้งแรกโดย% -ing ให้เป็นจำนวนที่เก็บข้อมูล

ไม่ใช่วิธีแก้ปัญหาที่ยอดเยี่ยม แต่อาจยังเป็นการประนีประนอมโดยรวมที่ดีกว่าค่าใช้จ่ายด้านหน่วยความจำและประสิทธิภาพในการดูแลอาร์เรย์ดัชนีที่สองตลอดเวลา


3

ทางออกที่ดีที่สุดน่าจะเป็นตารางแฮช + อาร์เรย์มันเร็วและกำหนดได้จริง

แต่คำตอบที่ได้คะแนนต่ำที่สุด (เพียงแค่ใช้ตารางแฮช!) ก็ยอดเยี่ยมเช่นกัน!

  • ตารางแฮชที่มีการแฮชซ้ำหรือการเลือกที่เก็บข้อมูลใหม่ (เช่นหนึ่งองค์ประกอบต่อที่เก็บข้อมูลไม่มีรายการที่เชื่อมโยง)
  • getRandom () พยายามเลือกที่เก็บข้อมูลแบบสุ่มซ้ำ ๆ จนกว่าจะว่างเปล่า
  • ในฐานะที่เป็นความล้มเหลวที่ปลอดภัยอาจจะ getRandom () หลังจากที่ N (จำนวนองค์ประกอบ) พยายามไม่สำเร็จให้เลือกดัชนีแบบสุ่ม i ใน [0, N-1] จากนั้นไปที่ตารางแฮชแบบเชิงเส้นและเลือกองค์ประกอบ # i-th .

ผู้คนอาจไม่ชอบสิ่งนี้เนื่องจาก "เป็นไปได้ไม่สิ้นสุดลูป" และฉันเคยเห็นคนฉลาด ๆ ก็มีปฏิกิริยาเช่นนี้เหมือนกัน แต่มันผิด! เหตุการณ์ที่ไม่น่าเกิดขึ้นอย่างไม่มีที่สิ้นสุดก็ไม่เกิดขึ้น

สมมติว่าพฤติกรรมที่ดีของแหล่งที่มาแบบสุ่มหลอกของคุณซึ่งไม่ยากที่จะสร้างขึ้นสำหรับพฤติกรรมนี้โดยเฉพาะและตารางแฮชนั้นเต็มอย่างน้อย 20% เสมอดูง่ายๆว่า:

จะไม่มีทางเกิดขึ้นได้ที่ getRandom () ต้องพยายามมากกว่า 1,000 ครั้ง เพียงแค่ไม่เคย อันที่จริงความน่าจะเป็นของเหตุการณ์ดังกล่าวคือ 0.8 ^ 1000 ซึ่งก็คือ 10 ^ -97 - ดังนั้นเราจึงต้องทำซ้ำ 10 ^ 88 ครั้งเพื่อให้มีโอกาสหนึ่งครั้งในพันล้านของเหตุการณ์นั้นเคยเกิดขึ้นครั้งเดียว แม้ว่าโปรแกรมนี้จะทำงานเต็มเวลาบนคอมพิวเตอร์ทุกเครื่องของมนุษยชาติจนกว่าดวงอาทิตย์จะสิ้นชีวิตสิ่งนี้ก็จะไม่เกิดขึ้น


1
หากคุณเลือกที่จะเลือกถังแบบสุ่มที่มีค่าอย่างต่อเนื่องกรณีที่เลวร้ายที่สุดในโลกจะนำไปสู่ ​​O (1) ได้อย่างไรในขณะที่คุณเลือกองค์ประกอบแบบสุ่ม
Balaji Boggaram Ramanarayan

@ user1147505 - คุณได้หมายเลขนี้มาจากไหน: "0.8 ^ 1000"
Hengameh

คุณมาถึงสิ่งนี้ได้อย่างไร: "ตารางแฮชมักจะเต็มอย่างน้อย 20%"
Hengameh

คุณช่วยเขียนวิธีเลือกถังแบบสุ่มด้วยได้ไหม
Hengameh

3

สำหรับคำถามนี้ฉันจะใช้โครงสร้างข้อมูลสองแบบ

  • HashMap
  • ArrayList / Array / Double LinkedList

ขั้นตอน: -

  1. การแทรก: - ตรวจสอบว่ามี X อยู่แล้วใน HashMap หรือไม่ - ความซับซ้อนของเวลา O (1) ถ้าไม่มีให้เพิ่มในตอนท้ายของ ArrayList - ความซับซ้อนของเวลา O (1) เพิ่มใน HashMap ด้วย x เป็นคีย์และดัชนีสุดท้ายเป็นค่า - ความซับซ้อนของเวลา O (1)
  2. ลบ: - ตรวจสอบว่ามี X อยู่ใน HashMap หรือไม่ - ความซับซ้อนของเวลา O (1) หากมีอยู่ให้ค้นหาดัชนีและลบออกจาก HashMap - ความซับซ้อนของเวลา O (1) สลับองค์ประกอบนี้กับองค์ประกอบสุดท้ายใน ArrayList และลบองค์ประกอบสุดท้าย - ความซับซ้อนของเวลา O (1) อัปเดตดัชนีขององค์ประกอบสุดท้ายใน HashMap - ความซับซ้อนของเวลา O (1)
  3. GetRandom: - สร้างหมายเลขสุ่มจาก 0 ถึงดัชนีสุดท้ายของ ArrayList ส่งคืนองค์ประกอบ ArrayList ที่ดัชนีสุ่มที่สร้างขึ้น - ความซับซ้อนของเวลา O (1)
  4. ค้นหา: - ดูใน HashMap สำหรับ x เป็นคีย์ - ความซับซ้อนของเวลา O (1)

รหัส: -

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;

            }
        }
    }

}

- ความซับซ้อนของเวลา O (1) - ความซับซ้อนของพื้นที่ O (N)


1

นี่คือวิธีแก้ปัญหา C # สำหรับปัญหานั้นที่ฉันคิดขึ้นเล็กน้อยเมื่อย้อนกลับไปเมื่อถามคำถามเดียวกัน ใช้เพิ่มลบประกอบด้วยและสุ่มพร้อมกับอินเทอร์เฟซ. NET มาตรฐานอื่น ๆ ไม่ใช่ว่าคุณจะต้องนำไปใช้ในรายละเอียดในระหว่างการสัมภาษณ์ แต่ก็เป็นเรื่องดีที่จะมีแนวทางแก้ไขที่เป็นรูปธรรมให้ดู ...

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;

        }
    }
}

ฉันไม่แน่ใจว่าจะได้ผลหากคุณมีตัวเลขที่ซ้ำกัน
AlexIIP

ไม่จัดการรายการที่ซ้ำกันเนื่องจาก @guildner กล่าวว่าจะถือว่าไม่มีรายการที่ซ้ำกันในความคิดเห็นของคำถาม หากมีการเพิ่มรายการที่ซ้ำกันจะArgumentExceptionมีข้อความ "มีการเพิ่มรายการที่มีคีย์เดียวกันแล้ว" จะถูกโยนทิ้ง (จากพจนานุกรมดัชนีพื้นฐาน)
Scott Lerch

1

เราสามารถใช้แฮชเพื่อสนับสนุนการดำเนินการในΘ (1) ครั้ง

แทรก (x) 1) ตรวจสอบว่ามี x อยู่แล้วหรือไม่โดยทำการค้นหาแผนที่แฮช 2) ถ้าไม่มีให้ใส่ที่ท้ายอาร์เรย์ 3) เพิ่มในตารางแฮชด้วย x จะถูกเพิ่มเป็นคีย์และดัชนีอาร์เรย์สุดท้ายเป็นดัชนี

ลบ (x) 1) ตรวจสอบว่ามี x อยู่หรือไม่โดยทำการค้นหาแผนที่แฮช 2) หากมีอยู่ให้ค้นหาดัชนีและลบออกจากแผนที่แฮช 3) สลับองค์ประกอบสุดท้ายกับองค์ประกอบนี้ในอาร์เรย์และลบองค์ประกอบสุดท้าย การสลับเสร็จสิ้นเนื่องจากสามารถลบองค์ประกอบสุดท้ายได้ในเวลา O (1) 4) อัปเดตดัชนีขององค์ประกอบสุดท้ายในแผนที่แฮช

getRandom () 1) สร้างตัวเลขสุ่มจาก 0 ถึงดัชนีสุดท้าย 2) ส่งคืนองค์ประกอบอาร์เรย์ที่ดัชนีที่สร้างขึ้นแบบสุ่ม

ค้นหา (x) ทำการค้นหา x ในแผนที่แฮช


1

แม้ว่านี่จะเก่า แต่เนื่องจากไม่มีคำตอบใน C ++ นี่คือสองเซ็นต์ของฉัน

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

นี่คือส่วนหนึ่งของรหัสไคลเอ็นต์เพื่อทดสอบโซลูชัน

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

ใน C # 3.0 + .NET Framework 4 แบบทั่วไปDictionary<TKey,TValue>นั้นดีกว่า Hashtable เนื่องจากคุณสามารถใช้System.LinqวิธีการขยายElementAt()เพื่อจัดทำดัชนีในอาร์เรย์แบบไดนามิกที่อยู่ภายใต้ที่KeyValuePair<TKey,TValue>จัดเก็บองค์ประกอบ:

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

อย่างไรก็ตามเท่าที่ฉันรู้ Hashtable (หรือลูกหลานในพจนานุกรม) ไม่ใช่วิธีแก้ปัญหาที่แท้จริงสำหรับปัญหานี้เนื่องจาก Put () สามารถตัดจำหน่ายได้เฉพาะ O (1) ไม่ใช่ O (1) จริงเนื่องจากเป็น O (N ) ที่ขอบเขตการปรับขนาดแบบไดนามิก

มีวิธีแก้ปัญหานี้จริงหรือไม่? สิ่งที่ฉันคิดได้ก็คือหากคุณระบุความจุเริ่มต้นของ Dictionary / Hashtable ตามลำดับขนาดที่เกินกว่าที่คุณคาดไว้ว่าจะต้องการคุณจะได้รับการดำเนินการ O (1) เนื่องจากคุณไม่จำเป็นต้องปรับขนาด


หากคุณเข้มงวดมากเกี่ยวกับสิ่งที่เป็นตารางแฮชการปรับขนาด O (N) เป็นสิ่งที่หลีกเลี่ยงไม่ได้ การใช้งานบางอย่างประนีประนอมเพื่อลดต้นทุนในการปรับขนาด - เช่นโดยการรักษาตารางที่มีอยู่ในขณะที่เพิ่มขนาดสองเท่าหรือพยายามปรับขนาดตารางที่มีอยู่ให้เข้าที่ (หลังจากจัดพื้นที่ที่อยู่เสมือนและขนาดตารางอย่างระมัดระวังบนขอบเขตของหน้าดังนั้นจึงไม่มี จำเป็นต้องมีการคัดลอกซึ่งอาจต้องใช้แผนที่หน่วยความจำมากกว่าใหม่ / malloc mem) จากนั้นค้นหาในพื้นที่ใหม่ที่ใหญ่กว่าก่อนที่จะถอยกลับไปที่ขนาดเล็ก (ในรูปแบบแทนที่โดยการปรับให้แน่นขึ้น) ด้วยตรรกะการย้ายองค์ประกอบ
Tony Delroy

0

ฉันเห็นด้วยกับอานนท์ ยกเว้นข้อกำหนดสุดท้ายที่ต้องการองค์ประกอบแบบสุ่มที่มีความเป็นธรรมเท่าเทียมกันข้อกำหนดอื่น ๆ ทั้งหมดสามารถแก้ไขได้โดยใช้ DS ที่ใช้ Hash เดียว ฉันจะเลือก HashSet สำหรับสิ่งนี้ใน Java โมดูโลของรหัสแฮชขององค์ประกอบจะทำให้ฉันไม่มีดัชนีของอาร์เรย์พื้นฐานในเวลา O (1) ฉันสามารถใช้เพื่อเพิ่มลบและมีการดำเนินการได้


0

เราทำสิ่งนี้โดยใช้ HashSet ของ Java ไม่ได้หรือ มีการแทรกเดลค้นหาทั้งหมดใน O (1) ตามค่าเริ่มต้น สำหรับ getRandom เราสามารถใช้ iterator ของ Set ที่ให้พฤติกรรมแบบสุ่มได้ เราสามารถทำซ้ำองค์ประกอบแรกจากชุดได้โดยไม่ต้องกังวลเกี่ยวกับองค์ประกอบที่เหลือ

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

ทำไมเราไม่ใช้ epoch% arraysize เพื่อค้นหาองค์ประกอบแบบสุ่ม การค้นหาขนาดอาร์เรย์คือ O (n) แต่ความซับซ้อนที่ตัดจำหน่ายจะเป็น O (1)


-3

ฉันคิดว่าเราสามารถใช้ลิงค์ลิสต์แบบทวีคูณกับตารางแฮชได้ คีย์จะเป็นองค์ประกอบและค่าที่เกี่ยวข้องจะเป็นโหนดในลิงค์ลิสต์แบบทวีคูณ

  1. แทรก (H, E): แทรกโหนดในลิงค์ลิสต์ทวีคูณและสร้างรายการเป็น H [E] = โหนด; O (1)
  2. ลบ (H, E): รับที่อยู่โหนดโดย H (E) ไปที่ก่อนหน้าของโหนดนี้และลบและทำให้ H (E) เป็น NULL ดังนั้น O (1)
  3. ประกอบด้วย (H, E) และ getRandom (H) เป็น obviuosly O (1)

นี่ไม่สมเหตุสมผลเลย
innosam
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.