面试问题:数据结构设置O(1)中的所有值

Ale*_*lec 49 data-structures

我在互联网上遇到了以下面试问题.

描述getValue(int index),setValue(int index,int value)和setAllValues(int value)都是O(1)的数据结构.

虽然数组足以让第一次和第二次操作在O(1)中执行,但第三次(setAllValues)可以提出什么?

Tim*_*nes 46

一个array元组如何{timestamp, value},另外一个{timestamp, value}all.由于您只关心插入的相对时间,因此可以使用单调递增id的时间戳值:

type Entry {
  int timestamp, 
  int value
}

type structure {
    int     id
    Entry   all
    Entry[] array
}
Run Code Online (Sandbox Code Playgroud)

将所有字段初始化为0.然后以下内容适合您:

这种方法的一个问题是,最终你会用掉时间戳的id,并且可能会回滚.如果您选择64位值来存储时间戳,那么在此之前,这将为您提供18,446,744,073,709,551,616次插入或setAlls.根据数据结构的预期用途,O(n)清理阶段可能是合适的,或者您可以抛出异常.

可能需要考虑的另一个问题是多线程.三个明显的问题:

  • 如果id++不是原子,两个线程同时获得一个新的id,那么你可以得到两个具有相同id的条目.
  • 如果id的增量和创建的元组的赋值不是原子的(它们可能不是)并且有两个同时插入,那么你可以得到一个竞争条件,其中旧的id获胜.
  • 如果元组的赋值不是原子的,并且insert()与a同时存在,get()那么你最终可能会{new_id, old_value}在数组中说出你的位置,导致返回错误的值.

如果这些问题中的任何一个都是问题,那么绝对最简单的解决办法是在文档中使用"not thread safe"(作弊).或者,如果您无法以您选择的语言原子方式实现这些方法,则需要在它们周围放置某种同步锁.


Mik*_* B. 6

我在其中一次技术访谈中得到了同样的问题.
这是我完整的即用型Java实现,包括测试用例.

关键的想法是保持setAll()特殊变量(例如joker)的值,然后以适当的方式跟踪该值的变化.

为了保持代码清洁,一些访问修饰符已被废除.

节点类:

import java.time.LocalDate;

class Node {

    int value;
    LocalDate jokerSetTime;

    Node(int val, LocalDate jokSetTime) {
        value = val;
        jokerSetTime = jokSetTime;
    }
}
Run Code Online (Sandbox Code Playgroud)

DS类:

class DS {

    Node[] arr;

    DS(int len) {
        arr = new Node[len];
    }
}
Run Code Online (Sandbox Code Playgroud)

DataStructure类:

import java.time.LocalDate;

class DataStructure {

    private boolean isJokerChanged;
    private Integer joker;
    private LocalDate jokerSetTime;
    private DS myDS;

    DataStructure(int len) {
        myDS = new DS(len);
    }

    Integer get(int i) {

        Integer result;

        if (myDS.arr.length < i) {
            return null;
        }

        // setAll() has been just called
        if (isJokerChanged) {
            return joker;
        }

        if (myDS.arr[i] == null) {

            // setAll() has been previously called
            if (joker != null) {
                result = joker;
            } else {
                result = null;

            }

        } else if (myDS.arr[i].jokerSetTime == jokerSetTime) {
            // cell value has been overridden after setAll() call
            result = myDS.arr[i].value;
        } else {
            result = joker;
        }

        return result;
    }

    void setAll(int val) {
        isJokerChanged = true;
        joker = val;
        jokerSetTime = LocalDate.now();
    }

    void set(int i, int val) {
        isJokerChanged = false;
        myDS.arr[i] = new Node(val, jokerSetTime);
    }
}
Run Code Online (Sandbox Code Playgroud)

主要课程:

class Main {

    public static void main(String[] args) {

        DataStructure ds = new DataStructure(100);

        Integer res;

        res = ds.get(3);

        ds.set(3, 10);

        res = ds.get(3);

        ds.setAll(6);

        res = ds.get(3);

        res = ds.get(15);

        ds.set(4, 7);

        res = ds.get(4);

        res = ds.get(3);

        ds.setAll(6);

        ds.set(8, 2);

        res = ds.get(3);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:
代码已更新.以前的实现并没有考虑到时的情况下setAll()具有相同值的行被称为两次,之后set(x),get(y)如:setAll(100),set(3, 1),setAll(100),set(5, 3),get(3).

添加了时间戳方法的使用,以允许区分setAll()具有相同值的不同调用.

PS此实现不是线程安全的.

  • 我有一种强烈的预感,这是预期的.所有基于时间戳的解决方案都增加了不必要的复杂性. (2认同)
  • @MikeB.很棒的实施!非常干净清晰.起初我无法理解以前所有时间戳的解决方案,但这真是太棒了,正是我所寻找的.再次感谢! (2认同)

WOP*_*OPR 5

一个指向一个公共值的指针数组怎么样?设置值,所有引用都将指向O(1)中的单个更改值.

  • 我会说这些规范明确暗示应该可以单独设置值而不影响其他值.否则,setValue方法的index参数是多余的.如果您不想将方法签名解释为规范,那么可以简单地将所有方法实现为NoOps ...... (3认同)

小智 5

我最近在一次采访中被问到这个问题。我想出了一个哈希表实现。它将解决时间戳值耗尽的问题,但需要实现线程安全功能(可能使用延迟初始化技术)

假设在我们的类中,我们有一个私有变量_defaultValue来保存默认值,并且我们还有一个私有哈希表或字典_hashtableSetAllValues可以仅将_defaultValue设置为等于传递的值,并将_hashtable初始化/设置为新的哈希表,并丢弃对旧哈希表的任何引用。SetValue应该只是向_hashtable添加一个新值,或者如果键(或索引)已存在于_hashtable中则更新该值。GetValue应检查_hashtable中是否存在键(或索引) ,然后返回它,否则返回存储在_defaultValue中的值。

这是我在 StackOverflow 上的第一个回答。我有点懒得写代码了。可能很快就会编辑答案。

面试官对这个解决方案点头同意,但坚持不使用哈希表来实现。我想,他是在要求我给出与蒂莫西的回答类似的方法。那一刻我无法得到它:(。无论如何,干杯!

编辑:发布代码(在 C# 中)

class MyDataStruct
{
    private int _defaultValue;
    private Dictionary<int,int> _hashtable;

    public MyDataStruct()
    {
        _defaultValue = 0; // initialize default with some value
        _hashtable = new Dictionary<int, int>();
    }

    public void SetAllValues(int val)
    {
        _defaultValue = val;
        _hashtable = new Dictionary<int, int>();
    }

    public void SetValue(int index, int val)
    {
        if (_hashtable.ContainsKey(index))
        {
            _hashtable.Add(index, val);
        }
        else
        {
            _hashtable[index] = val;
        }
    }

    public int GetValue(int index)
    {
        if (_hashtable.ContainsKey(index))
        {
            return _hashtable[index];
        }
        else
        {
            return _defaultValue;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这几乎是正确的,只是即使该键没有插入到数据结构中,不存在的 hey 的 GetValue 也总是返回默认值。 (3认同)