【问题标题】:Interview question: data structure to set all values in O(1)面试题:在O(1)中设置所有值的数据结构
【发布时间】:2012-04-17 19:58:02
【问题描述】:

我在网上遇到了以下面试问题。

描述一个数据结构,其中 getValue(int index)、setValue(int index, int value) 和 setAllValues(int value) 都是 O(1)。

虽然数组对于在 O(1) 中执行的第一个和第二个操作已经足够了,但是对于第三个(setAllValues)可以提出什么建议?

【问题讨论】:

  • 用哈希表实现可行吗?

标签: data-structures


【解决方案1】:

元组{timestamp, value}array 怎么样,另外一个{timestamp, value} 称为all。由于您只关心插入的相对时间,您可以使用单调递增的id 作为时间戳的值:

type Entry {
  int timestamp, 
  int value
}

type structure {
    int     id
    Entry   all
    Entry[] array
}

将所有字段初始化为 0。那么以下内容应该适合您:

  • setValue(索引 i, 值 v):

    array[i] = {id++, value}
    
  • 值 getValue(索引 i)

    if(all.timestamp > array[i].timestamp) return all.value
    else return array[i].value
    
  • setAll(值 v)

    all = {id++, value}
    

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

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

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

如果其中任何一个是问题,最简单的解决方案是在文档中添加“非线程安全”(作弊)。或者,如果您不能以您选择的语言原子地实现这些方法,则需要在它们周围放置某种同步锁。

【讨论】:

  • 但我们不能确定它在内部是如何工作的。这条语句可以是 O(n)。
  • 这依赖于每次调用 current_time() 返回不同的值(否则,两个时间戳可能相等)
  • @Damien_The_Unbeliever 这是真的。如果该程序存在一段时间,您可能还必须处理环绕案例。
  • @Damien_The_Unbeliever 所以,当我意识到根本不需要涉及系统时间戳时,我正在编写一个编辑建议使用 {system_timestamp,id} 的元组代替时间戳来解决这个问题。见编辑:)
  • 您的getValue() 签名需要修复。
【解决方案2】:

我在一次技术面试中遇到了同样的问题。
这是我完整的即用型 Java 实现,包括测试用例。

关键思想是将setAll()的值保存在特殊变量中(例如joker),然后以适当的方式跟踪该值的变化。

为了保持代码干净,一些访问修饰符已被废除。

节点类:

import java.time.LocalDate;

class Node {

    int value;
    LocalDate jokerSetTime;

    Node(int val, LocalDate jokSetTime) {
        value = val;
        jokerSetTime = jokSetTime;
    }
}

DS 类:

class DS {

    Node[] arr;

    DS(int len) {
        arr = new Node[len];
    }
}

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

类:

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

更新:
代码已更新。前面的实现没有考虑setAll()连续调用两次相同值后跟set(x),get(y)的情况,例如:setAll(100),set(3, 1),setAll(100) , set(5, 3), get(3).

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

附:此实现不是线程安全的。

【讨论】:

  • 我有一种强烈的预感,这是意料之中的。所有基于时间戳的解决方案都增加了完全不必要的复杂性。
  • @MikeB。伟大的实施!非常干净和清晰。起初,我无法理解所有时间戳的先前解决方案,但这真是太棒了,正是我正在寻找的。再次感谢!
  • 这个解决方案是错误的。在 main 函数中考虑以下示例:ds.setAll(6); ds.set(3,10); ds.setAll(6); ds.set(4,1); res = ds.get(); 它会返回不正确的值 10,即使在它之后调用了 setAll(6)。需要第二个 ds.set() 调用来清除 isJokerChanged 变量,否则它会从表面上防御此错误。
  • @AnatolyVorobey,代码已修复。感谢您的测试用例。
【解决方案3】:

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

【讨论】:

  • 这一直有效,直到您想将其中一个元素更改为指向公共值以外的其他内容。
  • 这不是问题中指定的要求。
  • 我认为是 - 如果您调用 setAll(),然后调用 setValue(),则必须更改其中一个元素。
  • 我会说规范清楚地暗示应该可以单独设置值而不影响其他值。否则 setValue 方法的 index 参数是多余的。如果您不想将方法签名解释为规范,那么可以简单地将所有方法实现为 NoOps...
【解决方案4】:

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

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

这是我在 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;
        }
    }
}

【讨论】:

  • 能否请您提供带有哈希表的代码?
  • 这几乎是正确的,除了不存在的 hey 的 GetValue 将始终返回默认值,即使该键未插入到数据结构中也是如此。
  • 这看起来不错,但是“SetAllValues”方法在最坏的情况下是 O(N) 时间复杂度,因为工作被转移到垃圾收集器,除非你不考虑垃圾收集器,这完全没问题.
  • 我认为这里的问题是,对于查找或设置,哈希表不一定是 O(1) - 在最好的情况下,它会是,但如果存在哈希冲突,它不会是。我怀疑面试官在想“所以,这将接近 O(1),但你能保证 O(1)”
【解决方案5】:

我们可以有一个变量 V,它存储一个 int 和一个包含元组的数组作为 {Value, id}..

还有一个全局 int 变量 G(其作用类似于 SQL 中的标识,并且每当执行任何 set 或 setAll 操作时,其值都会增加 1)

初始所有 Ids 和 V 值将默认为 null..

so V = null All Tuple = {null, null}
set(index i, val v) -> G = G+1, arr[i].Val = v, arr[i].Id = G

get(index i) -> if V.Id > arr[i].Id return V.Val else return arr[i]



set-all(val v) -> G= G+1, V.Id= G, V.Val = v

【讨论】:

    【解决方案6】:

    所有现有答案都使用在每次setVal 操作时递增的时间戳。这不是必需的。事实上,只需要增加setAll 上的时间戳即可。有人提出的另一个问题是算术溢出。这可以通过更新每个 setAll 上的单个单元格并仔细执行时间比较来处理,而不会打破恒定的时间界限。

    工作原理

    基本概念与其他答案基本相似,但有所不同。

    它们的作用:分别存储用于最后一次setAll 操作的值,并跟踪执行该操作的时间。每次执行setVal 时,它们都会将当前时间与给定值一起存储在数组中。每次他们执行getVal 时,他们会将给定位置的时间与最后一次setAll 发生的时间进行比较,然后选择该位置的值或setAll 值,这取决于哪个更大。

    为什么这可能是一个问题:假设当前时间溢出并且不久之后发生setAll 操作。看起来好像大多数存储的数组值都比 setAll 值新,但实际上它们更旧。

    解决方案:不要再想象我们正在跟踪自数据结构初始化以来经过的总时间。想象一个带有“秒针”的巨型时钟,它不是绕圈 60 圈,而是绕圈圈 2^n 圈。秒针的位置代表最近一次setAll 操作的时间。每个setVal 操作将这个时间与一个值一起存储。因此,如果我们在“时钟”为 45 时执行setAll,然后对不同元素执行六次setVal 操作,则setAll 时间和所有六个位置的时间将相同。我们希望保持以下不变量:

    给定元素位置的时间等于setAll 时间当且仅当该元素使用setVal 设置的时间比上一次setAll 操作更近。

    显然,上述过程自动确保如果元素是最近设置的,则其时间将等于setAll 时间。挑战在于使相反的含义也成立。

    待续......

    代码

    我用 Haskell 写了这篇文章,因为这是我最了解的语言,但它并不是最适合这项工作的语言。

    {-# LANGUAGE BangPatterns #-}
    module RepArr where
    
    import Control.Monad.Primitive
    import Data.Primitive.MutVar
    import qualified Data.Vector.Mutable as V
    import Data.Vector.Mutable (MVector)
    import Control.Applicative
    import Prelude hiding (replicate)
    import Control.Monad.ST
    import Data.Word
    
    -- The Int in the MutVar is the refresh pointer
    data RepArr s a = RepArr (MutVar s (Word, a, Int)) (MVector s (Word,a))
    
    -- Create a fancy array of a given length, initially filled with a given value
    replicate :: (PrimMonad m, Applicative m) => Int -> a -> m (RepArr (PrimState m) a)
    replicate n a = RepArr <$> newMutVar (0,a,0) <*> V.replicate n (0,a)
    
    getVal :: PrimMonad m => RepArr (PrimState m) a -> Int -> m a
    getVal (RepArr allv vec) n = do
      (vectime, vecval) <- V.read vec n
      (alltime, allval, _) <- readMutVar allv
      if (fromIntegral (alltime - vectime) :: Int) > 0
        then return allval
        else return vecval
    
    setVal :: PrimMonad m => RepArr (PrimState m) a -> Int -> a -> m ()
    setVal (RepArr allv vec) i v = do
      (!alltime, _, _) <- readMutVar allv
      V.write vec i (alltime, v)
    
    setAll :: PrimMonad m => RepArr (PrimState m) a -> a -> m ()
    setAll r@(RepArr allv vec) v = do
      (oldt, _, oldc) <- readMutVar allv
      getVal r oldc >>= setVal r oldc
      let !newc = case oldc+1 of
            op1 | op1 == V.length vec -> 0
                | otherwise -> op1
      let !newt = oldt+1
      writeMutVar allv (newt, v, newc)
    

    为了避免潜在的(罕见的)垃圾收集暂停,实际上有必要取消装箱 IntWord 值,以及使用未装箱的向量而不是多态向量,但我并没有心情和这个是一项相当机械的任务。

    这是一个 C 版本(完全未经测试):

    #include <malloc.h>
    
    struct Pair {
      unsigned long time;
      void* val;
    };
    
    struct RepArr {
      unsigned long allT;
      void* allV;
      long count;
      long length;
      struct Pair vec[];
    };
    
    struct RepArr *replicate (long n, void* val) {
      struct RepArr *q = malloc (sizeof (struct RepArr)+n*sizeof (struct Pair));
      q->allT = 1;
      q->allV = val;
      q->count = 0;
      q->length = n;
    
      int i;
      for (i=0; i<n; i++) {
        struct Pair foo = {0,val};
        q->vec[i] = foo;
      }
      return q;
    }
    
    
    void* getVal (struct RepArr *q, long i) {
      if ((long)(q->vec[i].time - q->allT) < 0)
        return q->allV;
      else
        return q->vec[i].val;
    }
    
    void setVal (struct RepArr *q, long i, void* val) {
      q->vec[i].time = q->allT;
      q->vec[i].val = val;
    }
    
    void setAll (struct RepArr *q, void* val) {
      setVal (q, q->count, getVal (q, q->count));
      q->allV = val;
      q->allT++;
      q->count++;
      if (q->count == q->length)
        q->count = 0;
    }
    

    【讨论】:

      【解决方案7】:
      /*
      

      在我写这篇文章的时候,这个页面上的所有解决方案都会加倍(或者 更多)存储数组所需的空间量。以下解决方案 将浪费的空间量从 Ω(n) 减少到 θ(n/w),其中 w 是 计算机“单词”中的位。在我的机器上是 64。

      这个答案中的这篇散文是在 C cmets 中的,所以你可以复制粘贴这个 逐字回答并使用您的 C 编译器进行编译。

      */
      
      #include <stdbool.h>
      #include <stddef.h>
      #include <stdint.h>
      #include <stdlib.h>
      
      /*
      

      问题是支持在 O(1) 时间内读取和写入数组中的值 以及批量写入,其中数组中的所有值一次写入 O(1) 时间。这可以使用 Aho、Hopcroft 和 厄尔曼,据我所知。由于Gonzalo Navarro, "Constant-Time Array Initialization in Little Space",我将提供一个版本。

      这个想法是将三个元数据数组与数据数组一起保留。我们也 保留两个整数:unset,这是批量写入中使用的最后一个值 操作,和size,一个近似值的数量已经 自上次批量写入以来设置。在任何时候,不同值的数量 自上次批量写入后写入的时间介于 size 和 w * size 之间。

      三个元数据数组描述了关于 w 值块的信息 数据数组。它们是:

      • nth: nth[i] 是自上次批量写入以来要写入的第 i 个唯一块 写

      • inverse_nth: inverse_nth[i] 是第 i 个块的顺序 数组已写入,在最后一次批量写入时从 0 开始计数。

      • bitset:数组单元编号时bitset[i]的第j位为1 自上次批量写入以来已写入 64*i + j。

      bitset[i]inverse_nth[i] 如果i 不是 集合 {nth[0], nth[1], ... , nth[size-1]} 的成员。换一种说法, inverse_nth[i]bitset[i] 有效当且仅当 inverse_nth[i] &lt; sizenth[inverse_nth[i]] == i

      我没有存储三个相同长度的单独数组,而是选择存储一个 数组,is_set,包含三个字段。

      */
      
      typedef struct {
        int nth_;
        int inverse_nth_;
        uint64_t bitset_;
      } IsSetCell;
      
      typedef struct {
        int unset_;
        int size_;
        IsSetCell is_set_[];
      } IsSetArray;
      
      typedef struct {
        IsSetArray * metadata_;
        int payload_[];
      } ResettableArray;
      
      /*
      

      要构造一个数组,我们需要一个默认值来在读取a时返回 从未被写入的值。

      */
      
      ResettableArray * ConstructResettableArray(int n, int unset) {
        ResettableArray* result =
            malloc(offsetof(ResettableArray, payload_) + n * sizeof(int));
        if (!result) return NULL;
        n = (n + 63) / 64;
        result->metadata_ =
            malloc(offsetof(IsSetArray, is_set_) + n * sizeof(IsSetCell));
        if (!result->metadata_) {
          free(result);
          return NULL;
        }
        result->metadata_->size_ = 0;
        result->metadata_->unset_ = unset;
        return result;
      }
      
      void DestructResettableArray(ResettableArray * a) {
        if (a) free(a->metadata_);
        free(a);
      }
      
      /*
      

      算法的大部分内容是写入和读取元数据。后 IsSet()Set() 已定义(如下),读取和写入数组是 直截了当。

      */
      
      bool IsSet(const IsSetArray * a, int i);
      void Set(IsSetArray * a, int i);
      
      int GetValue(const ResettableArray * a, int i) {
        if (!IsSet(a->metadata_, i)) return a->metadata_->unset_;
        return a->payload_[i];
      }
      
      void SetValue(ResettableArray * a, int i, int v) {
        a->payload_[i] = v;
        Set(a->metadata_, i);
      }
      
      void SetAllValues(ResettableArray * a, int v) {
        a->metadata_->unset_ = v;
      }
      
      /*
      

      读写的复杂部分是双向关系 在inverse_nthnth 之间。如果它们在位置 i 相互指向 (is_set[is_set[i].inverse_nth].nth == i) 然后位置 i 包含有效数据 在最后一次批量写入之后写入,只要is_set[i].inverse_nth < size

      */
      
      uint64_t OneBit(int i) {
        return UINT64_C(1) << i;
      }
      
      bool IsSet(const IsSetArray * a, int i) {
        const int cell = i/64, offset = i & 63;
        const int inverse_nth = a->is_set_[cell].inverse_nth_;
        return inverse_nth < a->size_ && a->is_set_[inverse_nth].nth_ == cell &&
               a->is_set_[cell].bitset_ & OneBit(offset);
      }
      
      void Set(IsSetArray * a, int i) {
        const int cell = i/64, offset = i & 63;
        const int inverse_nth = a->is_set_[cell].inverse_nth_;
        if (inverse_nth >= a->size_ || a->is_set_[inverse_nth].nth_ != cell) {
          a->is_set_[cell].inverse_nth_ = a->size_;
          a->is_set_[cell].bitset_ = 0;
          a->is_set_[a->size_].nth_ = cell;
          ++a->size_;
        }
        a->is_set_[cell].bitset_ |= OneBit(offset);
      }
      

      【讨论】:

      • 代码永远不会减小size_,我认为这一定是错误的。也许它应该在 SetAllValues() 中归零?
      【解决方案8】:

      C#中的正确解法:

      public sealed class SpecialDictionary<T, V>
      {
          private Dictionary<T, Tuple<DateTime, V>> innerData;
          private Tuple<DateTime, V> setAllValue;
          private DateTime prevTime;
      
          public SpecialDictionary()
          {
              innerData = new Dictionary<T, Tuple<DateTime, V>>();
          }
          public void Set(T key, V value) => innerData[key] = new Tuple<DateTime, V>(GetTime(), value);
          public void SetAll(V value) => setAllValue = new Tuple<DateTime, V>(GetTime(), value);
          public V Get(T key)
          {
              Tuple<DateTime, V> tmpValue = innerData[key];
              if (setAllValue?.Item1 > tmpValue.Item1)
              {
                  return setAllValue.Item2;
              }
              else
              {
                  return tmpValue.Item2;
              }
          }
          private DateTime GetTime()
          {
              if (prevTime == null)
              {
                  prevTime = DateTime.Now;
      
              }
              else
              {
                  if (prevTime == DateTime.Now)
                  {
                      Thread.Sleep(1);
                  }
                  prevTime = DateTime.Now;
              }
              return prevTime;
          }
      }
      

      还有测试:

      static void Main(string[] args)
      {
          SpecialDictionary<string, int> dict = new SpecialDictionary<string, int>();
          dict.Set("testA", 1);
          dict.Set("testB", 2);
          dict.Set("testC", 3);
          Console.WriteLine(dict.Get("testC"));
          dict.SetAll(4);
          dict.Set("testE", 5);
          Console.WriteLine(dict.Get("testC"));
          Console.WriteLine(dict.Get("testE"));
          Console.ReadKey();
      }
      

      【讨论】:

        【解决方案9】:

        Python 示例

        class d:
            def __init__(self, l):
                self.len = l
                self.g_p = [i for i in range(self.len)]
                self.g_v = [0 for i in range(self.len)]
                self.g_s = self.len - 1
                self.g_c = 0  
        
            def getVal(self, i):
                if (i < 0 or i >= self.len):
                    return
        
                if (self.g_p[i] <= self.g_s):
                    return self.g_v[self.g_p[i]]
        
                return self.g_c
        
            def setVal(self, i, v):
                if (i < 0 or i >= self.len):
                    return
        
                if (self.g_p[i] > self.g_s):
                    self.g_s += 1
        
                    self.g_p[self.g_s], self.g_p[i] = self.g_p[i], self.g_p[self.g_s]
        
                self.g_v[self.g_p[i]] = v
        
            def setAll(self, v):
                self.g_c = v
                self.g_s = -1
        

        【讨论】:

          【解决方案10】:

          关于蒂莫西·琼斯的回答:

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

          这正是使这个解决方案也是 O(n) 而不是 O(1) 的最坏情况。这种结构虽然节省了很多潜在的 O(n) 插入操作,但仍然处于 O(n) 效率。

          【讨论】:

          • 查看我的答案,了解在溢出时保持恒定时间更新的方法。
          • @dfeuer 能否请您用文字指定如何将 Int 和 Word 值拆箱?这个问题在世界各地的采访中被问到,而采访者的预期答案(正如蒂莫西所描述的那样)肯定是不正确的,或者至少是不完整的。无论如何,很明显上面的答案应该被细化。提前致谢。
          • 这听起来像是完全删除我的答案的一个很好的理由。
          • 这并没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方发表评论 - 您可以随时评论自己的帖子,一旦您有足够的reputation,您就可以comment on any post
          • @Bluehound 因为我通常不在这里写信,所以我不能在蒂莫西的回答下发表评论。我知道我已经发表了重要评论,所以请不要删除它。如果最好将此答案移动到蒂莫西的答案下,以便任何人都可以查看和评论。谢谢
          【解决方案11】:

          这是我在 Java 中的答案(我不完全确定语法)。 为了避免 changeT 和 defChangeT 相等的情况,我做了 set-functions 同步。

          Struct ValueSt{ 
            int value;
            Timestamp changeT;
          }
          
          Class MyDS{
            int default;
            Timestamp defChangeT;
            HashMap map;
          
            public MyDS(){
              map = new HashMap<int, ValueSt>();
            }
          
            public synchronized void mySet(Int key, int value){
              ValueSt vs = new ValueSt(value, Timestamp(System.current));
              map.put(key, vs);
            }
          
            public synchronized void setAll(int value){
              default = value;
              defChangeT = Timestamp(System.current));
            }
          
            public int myGet(int key){
              ValueSt vs = map.get(key);
              if(vs != null){
                if(vs.changeT > defChangeT)
                  return vs.value;
                else
                  return default;
              }
              return null;
            }
          }
          

          【讨论】:

            【解决方案12】:

            我最近遇到了类似的问题。我使用的数据结构是hashmap和hashset的组合。

            1) setValue(字符串键,字符串值) 在这种方法中,我直接在哈希图中插入对。我们还在哈希集中插入键

            2) setAllValue(字符串值) 在这种方法中,我重新初始化哈希图。这会删除所有对,然后添加一个键,例如 和值hashset 具有跨所有版本的 hashmap 维护的键集

            3) getValue(字符串键) 在这个方法中,我维护了多个 if/else 条件来返回值。

            1. 我检查条件 如果键存在于哈希图中,则返回该键的值。如果尚不存在“上帝”键并且每个键都保留其原始值,或者存在“上帝”键但该键的值已被覆盖,则可能会发生这种情况。
            2. 一个 else/if 条件来检查 如果该键不存在于哈希集中,则返回 null,因为该键从未出现在哈希映射或映射的任何先前实例中。
            3. 最后是一个 else 条件,其中我返回键 的值,因为这意味着该键存在于某个版本的 hashmap 中并且尚未被覆盖。李>

            类优化地图 {

            Map<String, String> mymap = new HashMap<String>();
            Set<String> myset = new HashSet<>();
            public void setValue (String key, String value) {
                mymap.put(key, value);
                myset.add(key);
            }
            
            public void setAllValue (String value) {
                mymap = new HashMap<>();
                mymap.put("god", value);
            }
            
            public String getValue (String key) {
                if (mymap.containsKey(key)) {
                    return mymap.get(key);
                } else if (!myset.contains(key)) {
                    return null;
                } else {
                    return mymap.get(key);
                }
            }
            
            public static void main (String args[]) {
            
                OptimisedMap opmap = new OptimisedMap();
                String value = opmap.getValue("hey");
                if (value == null) {
                    System.out.println("Key not present");
                }
                opmap.setValue("hey", "there");
                opmap.setValue("ho", "there");
                System.out.println(opmap.getValue("hey")); // will print there
                opmap.setAllValue("whatsup");
                System.out.println(opmap.getValue("hey")); // will print whatsup
                System.out.println(opmap.getValue("ho")); // will print whatsup
                opmap.setValue("hey", "there");
                System.out.println(opmap.getValue("hey")); // will print there
                System.out.println(opmap.getValue("ho")); // will print whatsup
            }
            

            }

            【讨论】:

              【解决方案13】:

              另一个 Python 示例:

                  class SetAll:
                  def __init__(self):
                      self.mapping = {}
                      self.is_all_same = True
                      self.all_value = None
                      self.set_all_ran = False
              
                  def set(self, index, value):
                      if self.is_all_same:
                          if value == self.all_value:
                              return
                          else:
                              self.is_all_same = False
                              self.mapping[index] = value
                      else:
                          self.mapping[index] = value
              
                  def get(self, index):
                      if self.mapping.get(index, None):
                          return self.mapping[index]
                      else:
                          if self.is_all_same:
                              return self.all_value
                          else:
                              if self.set_all_ran:
                                  return self.all_value
                      return
              
                  def set_all(self, value):
                      self.mapping = {}
                      self.is_all_same = True
                      self.set_all_ran = True
                      self.all_value = value
              

              【讨论】:

                【解决方案14】:

                我在 c# 中针对这个问题的实现:

                   public class MyStruct
                    {
                    public MyStruct(int val)
                    {
                        this.value = val;
                        this.currentTime = 0;
                    }
                    public int value { get; set; }
                    public int currentTime { get; set; }
                }
                
                public class Struct
                {
                    public List<MyStruct> array = new List<MyStruct>();
                    public MyStruct all = new MyStruct(0);
                
                    public int GetValue(int index)
                    {
                        if(all.currentTime >= array[index].currentTime)
                        {
                            return all.value;
                        }
                        else
                        {
                            return array[index].value;
                        }
                    }
                
                    public void Set(int index, int value)
                    {
                        if(array.Count <= index)
                        {
                            array.Add(new MyStruct(value));
                        }
                        else
                        {
                            array[index].value = value;
                        }
                        array[index].currentTime = all.currentTime +1;
                    }
                
                    public void SetAll(int value)
                    {
                        all.value = value;
                        all.currentTime++;
                    }
                
                    public void Delete(int index)
                    {
                        array[index].currentTime = 0;
                        array[index].value = -1;
                    }
                }
                

                陈卢加斯

                【讨论】:

                  【解决方案15】:

                  嗯,我是根据事件来做的。看看吧。

                  internal class Program
                  {
                      public static void Main(string[] args)
                      {
                          SomeUsefullDataStructure ds = new SomeUsefullDataStructure();
                          
                          ds.SetValue(1, "11");
                          ds.SetValue(2, "22");
                          var result = ds.GetValue(2);
                          ds.SetAllValues("haha");
                  
                          Console.ReadKey();
                      }
                  }
                  
                  public class SomeUsefullDataStructure
                  {
                      private delegate void SetValueDelegate(string newValue);
                      private event SetValueDelegate SetValueEventFired;
                      private Dictionary<int, StringDataEntry> dict = new Dictionary<int, StringDataEntry>();
                  
                      public string GetValue(int key)
                      {
                          if (dict.ContainsKey(key))
                          {
                              return dict[key].StringValue;
                          }
                          
                          throw new ArgumentException("no such key");
                      }
                  
                      public void SetValue(int key, string value)
                      {
                          if (dict.ContainsKey(key))
                          {
                              dict[key].UpdateValue(value);
                          }
                          else
                          {
                              StringDataEntry stringDataEntry = new StringDataEntry(value);
                              SetValueEventFired += stringDataEntry.UpdateValue;
                              dict.Add(key, stringDataEntry);
                          }
                      }
                  
                      public void SetAllValues(string value)
                      {
                          SetValueEventFired(value);
                      }
                  }
                  
                  public class StringDataEntry
                  {
                      public string StringValue { get; private set; }
                  
                      public StringDataEntry(string value)
                      {
                          StringValue = value;
                      }
                      
                      public void UpdateValue(string newValue)
                      {
                          StringValue = newValue;
                      }
                  }
                  

                  【讨论】:

                    【解决方案16】:

                    许多解决方案都很棒,但是没有一个提到state-of-the-art一个

                    fill(v), read(i), write(i, v) 操作的 O(1) 最坏时间复杂度
                    (调用 fill(v) 会将数组中的所有值设置为 v,read/write 是不言自明的),
                    除了数组之外,只占用 1 位额外空间。是的。

                    因此,大小为 1,000,000 的 int32_t 数组将需要 O(1) 最坏时间来初始化(和填充),
                    并且只占用 32,000,001 位内存。

                    In-Place Initializable Arrays 论文中提到了它,
                    并在我写过的有关该主题的 Article 中进行了解释。

                    我编写了一个名为 Farray 的仅 C++ 标头库,其中包含 Fillable Array
                    这是上述论文的模板实现。

                    【讨论】:

                      【解决方案17】:

                      我的 Python 解决方案。经测试。不是线程安全的。 如果您发现任何问题,请告诉我:)

                      class TimestampedNode:
                          def __init__(self, timestamp, val):
                              self.timestamp = timestamp
                              self.val = val
                      
                      class SetAllDS:
                          def __init__(self):
                              self.items = []
                              self.timestamp = 0
                              self.all_joker= TimestampedNode(self.timestamp, 0)
                      
                          def getValue(self, index):
                              try:
                                  item=self.items[index]
                              except IndexError:
                                  return None
                              if item.timestamp<self.all_joker.timestamp:
                                  return self.all_joker.val
                              return item.val
                      
                          def setValue(self, index, val):
                              # if index<0 or index>=len(self.items): #
                              #     raise IndexError("Invalid index\n")
                              self.timestamp += 1
                              self.items[index]=TimestampedNode(self.timestamp, val)
                      
                          def setAllValues(self, val):
                              self.timestamp+=1
                              self.all_joker=TimestampedNode(self.timestamp, val)
                      

                      【讨论】:

                        【解决方案18】:

                        这是另一个 python 解决方案: 这里是a link

                        from datetime import datetime
                        from typing import Any, Dict
                        
                        
                        class ValueNode:
                            def __init__(self, value):
                                self.value = value
                                self.date_updated = datetime.now()
                        
                            def set_value(self, value):
                                self.value = value
                                self.date_updated = datetime.now()
                        
                            def get_value(self, value):
                                return self.value
                        
                        
                        class Structure:
                            def __init__(self):
                                self._structure: Dict[Any, ValueNode] = {}
                                self._set_all_node: ValueNode = None
                        
                            def get_val(self, index):
                                if self._set_all_node and self._structure.get(index) and self._structure.get(index).date_updated < self._set_all_node.date_updated:
                                    return self._set_all_node.value
                                else:
                                    if index in self._structure:
                                        return self._structure[index].value
                                    else:
                                        return None
                        
                            def set_val(self, index, value):
                                self._structure[index] = ValueNode(value)
                        
                            def set_all(self, value):
                                self._set_all_node = ValueNode(value)
                        
                        
                        s1 = Structure()
                        s1.set_val(1, 'green')
                        s1.set_val(5, 'blue')
                        s1.set_val(9, 'yellow')
                        print(s1.get_val(1))
                        print(s1.get_val(5))
                        print(s1.get_val('NotExistedValue'))
                        s1.set_all('black')
                        print(s1.get_val(1))
                        print(s1.get_val(5))
                        print(s1.get_val('NotExistedValue'))
                        

                        【讨论】:

                          猜你喜欢
                          • 2018-03-27
                          • 1970-01-01
                          • 2012-05-13
                          • 1970-01-01
                          • 1970-01-01
                          • 2017-03-19
                          • 2019-01-14
                          • 2020-02-21
                          • 1970-01-01
                          相关资源
                          最近更新 更多