Algorithm 将find min/find max堆栈推广到任意顺序统计?

Algorithm 将find min/find max堆栈推广到任意顺序统计?,algorithm,data-structures,stack,Algorithm,Data Structures,Stack,在中,OP要求提供一种类似于堆栈的数据结构,每个堆栈支持O(1)次以下操作: Push,它在堆栈顶部添加一个新元素 Pop,它从堆栈中删除顶部元素 Find Max返回(但不删除)堆栈中最大的元素,以及 Find Min,它返回(但不删除)堆栈的最小元素 几分钟前,我发现要求澄清一个类似的数据结构,它不允许查询max和min,而是允许查询堆栈的中间元素。这两种数据结构似乎是支持以下操作的更通用数据结构的特例: Push,将元素推到堆栈顶部 弹出,弹出堆栈顶部,然后 Find Kth返回堆栈

中,OP要求提供一种类似于堆栈的数据结构,每个堆栈支持O(1)次以下操作:

  • Push,它在堆栈顶部添加一个新元素
  • Pop,它从堆栈中删除顶部元素
  • Find Max返回(但不删除)堆栈中最大的元素,以及
  • Find Min,它返回(但不删除)堆栈的最小元素
几分钟前,我发现要求澄清一个类似的数据结构,它不允许查询max和min,而是允许查询堆栈的中间元素。这两种数据结构似乎是支持以下操作的更通用数据结构的特例:

  • Push,将元素推到堆栈顶部
  • 弹出,弹出堆栈顶部,然后
  • Find Kth返回堆栈中第k个最大的元素,该元素用于在创建结构时确定的固定k

通过存储一个堆栈和一个包含前k个元素的平衡二叉搜索树,可以支持所有这些操作,这将使所有这些操作能够在O(logk)时间内运行。我的问题是:是否有可能比这更快地实现上述数据结构?也就是说,我们能为所有三个操作得到O(1)吗?或者O(1)用于push和pop,O(log k)用于order statistic查找?

这是否实际比log k实现快,取决于最常用的操作,我建议使用O(1)Find kth和pop以及O(n)push实现,其中n是堆栈大小。我还想与SO分享这一点,因为乍一看,这只是一个有趣的数据结构,但可能是合理的

它最好用双链接堆栈来描述,或者更容易用链接堆栈和双链接排序列表的混合体来描述。基本上,每个节点维护对其他节点的4个引用,在元素大小上按堆栈顺序的下一个和上一个,以及按排序顺序的下一个和上一个。这两个链表可以使用相同的节点实现,但它们完全独立工作,即排序链表不必知道堆栈顺序,反之亦然

与普通链接堆栈一样,集合本身需要维护对顶部节点(和底部节点?)的引用。为了适应Find kth方法的O(1)性质,集合还将保留对第k个最大元素的引用

pop方法的工作原理如下: 弹出的节点将从排序的双链接列表中删除,就像从普通排序的链接列表中删除一样。它采用O(1)作为集合对顶部的引用。根据弹出的元素是大于还是小于第k个元素,对第k个最大元素的引用设置为上一个还是下一个。因此,该方法仍然具有O(1)复杂度

push方法的工作原理与排序链表的普通加法类似,这是一个O(n)操作。它从最小的元素开始,并在遇到较大的元素时插入新节点。为了保持对第k个最大元素的正确引用,再次选择当前第k个最大元素的前一个或下一个元素,这取决于推送节点是否大于或小于第k个最大元素

当然,除此之外,必须在这两种方法中设置对堆栈“顶部”的引用。还有k>n的问题,您还没有指定数据结构应该做什么。我希望它是如何工作的,否则我可以添加一个例子

但好吧,不完全是你所希望的那样复杂,但我发现这是一个有趣的“解决方案”


编辑:所述结构的实现 对这个问题悬赏,这表明我最初的回答不够好:p也许OP希望看到实现

我在C#中实现了中值问题和fixed-k问题。中值跟踪器的实现只是围绕第k个元素跟踪器的包装,其中k可以变异

要回顾复杂性,请执行以下操作:

  • 推送O(n)
  • 流行音乐(1)
  • FindKth需要O(1)
  • 改变k取O(δk)
我已经在我的原始帖子中详细描述了算法。这样的实现就相当简单了(但要正确实现就不那么简单了,因为有很多不平等的迹象和if语句需要考虑)。我的评论只是为了说明做了什么,而不是如何做的细节,否则会变得太大。对于SO帖子来说,代码已经相当长了

我想提供所有非普通公众成员的合同:

  • K
    是排序链表中元素的索引,用于保留引用。它是可变的,当设置时,结构会立即对此进行更正
  • KthValue
    是该索引处的值,除非该结构还没有k个元素,在这种情况下,它返回一个默认值
  • HasKthValue
    的存在可以很容易地将这些默认值与碰巧是其类型的默认值的元素区分开来
  • 构造函数
    :空可枚举项被解释为空可枚举项,空比较器被解释为默认值。此比较器定义确定第k个值时使用的顺序
这就是代码:

public sealed class KthTrackingStack<T>
{
    private readonly Stack<Node> stack;
    private readonly IComparer<T> comparer;
    private int k;
    private Node smallestNode;
    private Node kthNode;

    public int K
    {
        get { return this.k; }
        set
        {
            if (value < 0) throw new ArgumentOutOfRangeException();
            for (; k < value; k++)
            {
                if (kthNode.NextInOrder == null)
                    return;
                kthNode = kthNode.NextInOrder;
            }
            for (; k >= value; k--)
            {
                if (kthNode.PreviousInOrder == null)
                    return;
                kthNode = kthNode.PreviousInOrder;
            }
        }
    }
    public T KthValue
    {
        get { return HasKthValue ? kthNode.Value : default(T); }
    }
    public bool HasKthValue
    {
        get { return k < Count; }
    }
    public int Count
    {
        get { return this.stack.Count; }
    }

    public KthTrackingStack(int k, IEnumerable<T> initialElements = null, IComparer<T> comparer = null)
    {
        if (k < 0) throw new ArgumentOutOfRangeException("k");
        this.k = k;
        this.comparer = comparer ?? Comparer<T>.Default;
        this.stack = new Stack<Node>();
        if (initialElements != null)
            foreach (T initialElement in initialElements)
                this.Push(initialElement);
    }
    public void Push(T value)
    {
        //just a like a normal sorted linked list should the node before the inserted node be found.
        Node nodeBeforeNewNode;
        if (smallestNode == null || comparer.Compare(value, smallestNode.Value) < 0)
            nodeBeforeNewNode = null;
        else
        {
            nodeBeforeNewNode = smallestNode;//untested optimization: nodeBeforeNewNode = comparer.Compare(value, kthNode.Value) < 0 ? smallestNode : kthNode;
            while (nodeBeforeNewNode.NextInOrder != null && comparerCompare(value, nodeBeforeNewNode.NextInOrder.Value) > 0)
                nodeBeforeNewNode = nodeBeforeNewNode.NextInOrder;
        }
        //the following code includes the new node in the ordered linked list
        Node newNode = new Node
                        {
                            Value = value,
                            PreviousInOrder = nodeBeforeNewNode,
                            NextInOrder = nodeBeforeNewNode == null ? smallestNode : nodeBeforeNewNode.NextInOrder
                        };
        if (newNode.NextInOrder != null)
            newNode.NextInOrder.PreviousInOrder = newNode;
        if (newNode.PreviousInOrder != null)
            newNode.PreviousInOrder.NextInOrder = newNode;
        else
            smallestNode = newNode;
        //the following code deals with changes to the kth node due the adding the new node
        if (kthNode != null && comparer.Compare(value, kthNode.Value) < 0)
        {
            if (HasKthValue)
                kthNode = kthNode.PreviousInOrder;
        }
        else if (!HasKthValue)
        {
            kthNode = newNode;
        }
        stack.Push(newNode);
    }

    public T Pop()
    {
        Node result = stack.Pop();
        //the following code deals with changes to the kth node
        if (HasKthValue)
        {
            if (comparer.Compare(result.Value, kthNode.Value) <= 0)
                kthNode = kthNode.NextInOrder;
        }
    else if(kthNode.PreviousInOrder != null || Count == 0)
        {
            kthNode = kthNode.PreviousInOrder;
        }
        //the following code maintains the order in the linked list
        if (result.NextInOrder != null)
            result.NextInOrder.PreviousInOrder = result.PreviousInOrder;
        if (result.PreviousInOrder != null)
            result.PreviousInOrder.NextInOrder = result.NextInOrder;
        else
            smallestNode = result.NextInOrder;
        return result.Value;
    }
    public T Peek()
    {
        return this.stack.Peek().Value;
    }
    private sealed class Node
    {
        public T Value { get; set; }
        public Node NextInOrder { get; internal set; }
        public Node PreviousInOrder { get; internal set; }
    }
}
public class MedianTrackingStack<T>
{
    private readonly KthTrackingStack<T> stack;
    public void Push(T value)
    {
        stack.Push(value);
        stack.K = stack.Count / 2;
    }
    public T Pop()
    {
        T result = stack.Pop();
        stack.K = stack.Count / 2;
        return result;
    }

    public T Median
    {
        get { return stack.KthValue; }
    }
    public MedianTrackingStack(IEnumerable<T> initialElements = null, IComparer<T> comparer = null)
    {
        stack = new KthTrackingStack<T>(initialElements == null ? 0 : initialElements.Count()/2, initialElements, comparer);
    }
}
KthTrackingStack公共密封类
{
私有只读堆栈;
专用只读IComparer比较器;
私人INTK;
私有节点smallestNode;
私有节点kthNode;
公共int K
{
获取{返回此.k;}
设置
With this data structure, inserting/deleting would take O(ln(k))

and finding the maximum O(1)
[number in the stack] [ skip list  linked with that number]
1 [7,2,1] 
7 [7,2,null]
2 [2,null,null]
8 [7,2,1] since 8 > kth element  therefore skip list doesn't change
3 [3,2,1] since 3 < kth element, the kth element has changed. I first delete 7 who was the previous kth element (O(ln(k))) then insert 3 O(ln(k)) => total O(ln(k))
9 [3,2,1] since 9 > kth element
9 [3,2,1]
3 [3,2,1]
8 [7,2,1]
1 [7,2,1] 
7 [7,2,null]
2 [2,null,null]
I get 3 in O(1)
8 [7,2,1]
1 [7,2,1] 
7 [7,2,null]
2 [2,null,null]
I get 7 in O(1)
0 [2,1,0]
8 [7,2,1]
1 [7,2,1] 
7 [7,2,null]
2 [2,null,null]
BTrie* BTrieInsert(BTrie* t, Item key, int data);
BTrie* BTrieFind(BTrie* t, Item key);
BTrie* BTrieDelete(BTrie* t, Item key);
BTrie* BTrieNextKey(BTrie* t, Item key);
BTrie* BTriePrevKey(BTrie* t, Item key);
void KSStackPush(KStack* ks, Item val)
{
   BTrie* node;
   //resize if needed
   if (ks->ct == ks->sz) ks->stack = realloc(ks->stack,sizeof(Item)*(ks->sz*=2)); 

   //push val
   ks->stack[ks->ct++]=val;

   //record count of value instances in trie
   node = BTrieFind(ks->trie, val);
   if (node) node->data++;
   else ks->trie = BTrieInsert(ks->trie, val, 1);

   //adjust kth if needed
   ksCheckDecreaseKth(ks,val);
}
//check if inserted val is in set of K
void ksCheckDecreaseKth(KStack* ks, Item val)
{
   //if less than K items, track the max.
   if (ks->ct <= ks->K) {
      if (ks->ct==1) { ks->kthValue = val; ks->iCount = 1;} //1st item
      else if (val == ks->kthValue) { ks->iCount++; }
      else if (val > ks->kthValue) { ks->kthValue = val; ks->iCount = 1;}
   }

   //else if value is one of the K, decrement instance count
   else if (val < ks->kthValue &&  (--ks->iCount<=0))  {
      //if that was only instance in set,
      //find the previous value, include all its instances
      BTrie* node = BTriePrev(ks->trie, ks->kthValue);
      ks->kthValue = node->key;
      ks->iCount = node->data;
   }
}
Item KSStackPop(KStack* ks)
{
   //pop val
   Item val = ks->stack[--ks->ct];
   //find in trie
   BTrie* node = BTrieFind(ks->trie, val);
   //decrement count, remove if no more instances
   if (--node->data == 0)
      ks->trie = BTrieDelete(ks->trie, val);
   //adjust kth if needed
   ksCheckIncreaseKth(ks,val);
   return val;
}
//check if removing val causes Kth to increase
void ksCheckIncreaseKth(KStack* ks, Item val)
{
   //if less than K items, track max
   if (ks->ct < ks->K)
   {  //if removing the max,
      if (val==ks->kthValue) {
         //find the previous node, and set the instance count.
         BTrie* node = BTriePrev(ks->trie, ks->kthValue);
         ks->kthValue = node->key;
         ks->iCount = node->data;
      }
   }
   //if removed val was among the set of K,add a new item
   else if (val <= ks->kthValue)
   {
      BTrie* node = BTrieFind(ks->trie, ks->kthValue);
      //if more instances of kthValue exist, add 1 to set. 
      if (node && ks->iCount < node->data) ks->iCount++;
      //else include 1 instance of next value
      else {
         BTrie* node = BTrieNext(ks->trie, ks->kthValue);
         ks->kthValue = node->key;
         ks->iCount = 1;
      }
   }
}