Algorithm 将find min/find max堆栈推广到任意顺序统计?
在中,OP要求提供一种类似于堆栈的数据结构,每个堆栈支持O(1)次以下操作: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返回堆栈
- Push,它在堆栈顶部添加一个新元素
- Pop,它从堆栈中删除顶部元素
- Find Max返回(但不删除)堆栈中最大的元素,以及
- Find 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)
是排序链表中元素的索引,用于保留引用。它是可变的,当设置时,结构会立即对此进行更正李>K
是该索引处的值,除非该结构还没有k个元素,在这种情况下,它返回一个默认值李>KthValue
的存在可以很容易地将这些默认值与碰巧是其类型的默认值的元素区分开来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;
}
}
}