Algorithm 用于推送、弹出和查找最小值的自定义数据结构

Algorithm 用于推送、弹出和查找最小值的自定义数据结构,algorithm,data-structures,Algorithm,Data Structures,我刚刚被问到一个关于A公司的面试问题如下: 问题:设计一个数据结构,其中有3个操作:推送、弹出和查找最小值。您应该在固定时间内完成所有3个操作 我的答案:我会使用一个链表,在这个链表中我可以在固定的时间内进行插入和删除,我会使用一个额外的内存来存储最小值 他提出了第二个问题说,如果你达到最小值,你如何找到第二个最小值?同样,在恒定时间内。 你会告诉他什么?如果你像你说的那样做了一个链表,但也存储了当前的最小值,该怎么办。添加新数字时,它会查看上一个最小值,如果当前值较低,则会将当前最小值更改为当

我刚刚被问到一个关于A公司的面试问题如下:

问题:设计一个数据结构,其中有3个操作:推送、弹出和查找最小值。您应该在固定时间内完成所有3个操作

我的答案:我会使用一个链表,在这个链表中我可以在固定的时间内进行插入和删除,我会使用一个额外的内存来存储最小值

他提出了第二个问题说,如果你达到最小值,你如何找到第二个最小值?同样,在恒定时间内。


你会告诉他什么?

如果你像你说的那样做了一个链表,但也存储了当前的最小值,该怎么办。添加新数字时,它会查看上一个最小值,如果当前值较低,则会将当前最小值更改为当前值

例如,假设你有数据:3,6,4,2,7,1。那么这就是列表的样子

值| min

3 | 3->6 | 3->4 | 3->2 | 2->7 | 2->1 | 1

这将在添加/删除项目时跟踪分钟数

编辑: 我提到了倒退,是这样的: 1|1 -> 7|2 -> 2|2 -> 4|3 -> 6|3 -> 3|3
这样,您就不需要“页脚”了。

让每个节点保留另一个对先前最小项的引用。因此,当弹出最小的项目时,可以恢复以前最小的项目。因为您只能推送和弹出,所以它将是正确的节点。

最小堆栈问题-

从PDF:

问题:最小堆栈

描述一种支持“推送”、“弹出”和“查找最小值”的堆栈数据结构 操作。“Find minimum”返回堆栈中最小的元素

好答案:存储两个堆栈,其中一个包含堆栈中的所有项 其中之一是一堆极小值。要推动图元,请将其推到第一个图元上 堆栈检查是否小于第二层的顶部项目;如果是,推 它被放在第二层。要弹出项目,请从第一个堆栈中弹出它。如果是最上面的 元素,从第二个堆栈中弹出它。求最小值 元素,只需返回第二个堆栈顶部的元素。每次操作
需要O(1)个时间。

这会让你发疯-

以下是实现Bryce Siedschlaw给出的上述算法的C代码:

#include<stdio.h>
#include<stdlib.h>

#define minimumOf(a,b) (a<b) ? a : b;

struct node{
    int data;
    struct node* next;
    int min;
};

void push(struct node **head_ref, int new_data){
    struct node* new_node = (struct node *)malloc(sizeof(struct node));
    new_node->data = new_data;

    if(*head_ref == NULL)
        new_node->min = new_data;
    else
        new_node->min = minimumOf(new_data,(*head_ref)->min);

    new_node->next = *head_ref;
    *head_ref = new_node;
}

int minimum(struct node *head){
    return head->min;
}

int pop(struct node **head_ref){
    int pop_data = (*head_ref)->data;
    (*head_ref) = (*head_ref)->next;
    return pop_data;
}

void printList(node *head){
    while(head != NULL){
        printf("%d->", head->data);
        head = head->next;
    }
    printf("\b\n");
}

int main(){
    struct node* a = NULL;

    push(&a, 100);
    push(&a, 24);
    push(&a, 16);
    push(&a, 19);
    push(&a, 50);
    printList(a);
    printf("Minimum is:%d\n", minimum(a));
    printf("Popped:%d\n",pop(&a));
    printf("Minimum is:%d\n", minimum(a));
    printf("Popped:%d\n",pop(&a));
    printf("Minimum is:%d\n", minimum(a));
    printf("Popped:%d\n",pop(&a));
    printf("Minimum is:%d\n", minimum(a));
    printf("Popped:%d\n",pop(&a));
    printf("Minimum is:%d\n", minimum(a));
}
#包括
#包括
#定义(a,b)的最小值(adata=新数据;
如果(*head_ref==NULL)
新建_节点->最小值=新建_数据;
其他的
新建节点->最小值=最小值(新建数据,(*head\u ref)->最小值);
新建节点->下一步=*头部\u参考;
*head\u ref=新节点;
}
最小整数(结构节点*头){
返回头->最小值;
}
int pop(结构节点**head\U ref){
int pop_data=(*head_ref)->数据;
(*head\u ref)=(*head\u ref)->下一步;
返回pop_数据;
}
无效打印列表(节点*头){
while(head!=NULL){
printf(“%d->”,头部->数据);
头部=头部->下一步;
}
printf(“\b\n”);
}
int main(){
结构节点*a=NULL;
推送(&a,100);
推(a,24);
推(a,16);
推(a,19);
推(a,50);
印刷品清单(a);
printf(“最小值为:%d\n”,最小值为(a));
printf(“弹出:%d\n”,弹出(&a));
printf(“最小值为:%d\n”,最小值为(a));
printf(“弹出:%d\n”,弹出(&a));
printf(“最小值为:%d\n”,最小值为(a));
printf(“弹出:%d\n”,弹出(&a));
printf(“最小值为:%d\n”,最小值为(a));
printf(“弹出:%d\n”,弹出(&a));
printf(“最小值为:%d\n”,最小值为(a));
}

您需要有一个根节点和一个指定为“页脚”的节点,这样才能访问O(1)中的结尾或者你可以倒过来,在前面添加一些东西,每次插入时都更改根节点……这很有效,但是如果删除1,你必须更新列表中的所有元素,因此你的
pop
操作不是恒定的,但你不能只是“更新”2…你只能插入和弹出。除非我理解wrong@BryceSiedschlaw你是对的,但是在这种情况下,你存储的数量不再是最小的了,对吗?我想你和我谈论的是同一件事,是吗?这假设没有重复的,但调整它以允许它们意味着用“更小或相等”@Jim Mischel:如果是这样的话,你就不必担心了,因为你必须先删除4和3,然后才能删除1。这意味着最小值堆栈将包含列表中的最小值。你读过pdf中的问题了吗?它不是说常数时间,你可以使用线性时间使用列表或使用最小值堆来完成它n do in lognYes我做了。问题要求你用push、pop和find min操作构建一个数据结构。他们的解决方案在固定时间内完成所有三个操作。好了。3个像样的答案说了同样的事情,以不同的方式实现:可能的重复:对不起,我投票中的链接是错误的,但这仍然是一个重复685050硅酸盐