【问题标题】:Pushing to a stack containing ONLY unique values in C推送到仅包含 C 中唯一值的堆栈
【发布时间】:2025-12-31 07:50:09
【问题描述】:

我已经实现了一个带有指针的堆栈,它的工作方式也像假设的那样。现在,我需要它推送到堆栈,而不是推送副本。例如,如果我将“2”压入堆栈,再压入另一个“2”仍然会导致堆栈中只有一个“2”,因为它已经存在。

以下是我尝试创建新推送功能的方法。我知道我想遍历堆栈并检查我要添加的元素,但我想我做错了吗?谁能帮帮我?

    typedef struct Node {
        void *content;
        struct Node *next;
    } Node;

    typedef struct Stack {
        Node *head;
        int count; 
    } Stack;

    void push(Stack *stack, void *newElem) {
        Node *newNode = (Node*) malloc(sizeof(Node));
        if (stack->count > 0) {
             int i;
             for (i = 0, newNode = stack->head; i < stack->count; i++, newNode =
                 newNode->next) {
                   if (newNode->content == newElem) return;
             }
        } else {
            newNode->next = stack->head;
            newNode->content = newElem;
            stack->head = newNode;
            stack->count++;
        }
    }

【问题讨论】:

  • 请注意,在您知道需要添加项目之前,您不应执行malloc()。如果您推送的项目已经存在,您将泄漏内存。您不知道如何比较两个节点的值(内容); content 指向的空间有多大,什么是合适的比较器函数。

标签: c linked-list stack duplicates push


【解决方案1】:
if (newNode->content == newElem)

您正在比较两个指针。我猜你想检查他们的 contents 是否相等:

#include <string.h>

if (memcmp(newNode->content, newElem, size) == 0)

size 可能由调用者指示。在你的情况下,它应该是sizeof(int)

此外,一旦您遍历了堆栈,您就不会将元素添加到您的数据结构中。

【讨论】:

  • for 循环怎么样?我是否正确地遍历堆栈?我仍然收到分段错误
  • @EjayTumacder:除了你不应该重复使用newNode 来遍历你的堆栈(否则,它会导致内存泄漏)之外,你所做的似乎是“合理的”。检查您是否正确插入节点。
【解决方案2】:

问题在于,如果您的堆栈不是空的,并且您没有找到堆栈中已经存在的元素,那么您什么也不做。您需要摆脱 else 关键字并使该代码无条件。然后,在您知道是否需要之前为新节点分配空间,更糟糕的是,用您在堆栈上的迭代覆盖新分配的指针,以查看是否需要推送它。所以在 } 结束 if 之后将 malloc 向下移动

【讨论】:

    【解决方案3】:

    你已经有工作了

    void push(Stack *stack, void *newElem);
    

    对吗?

    那么,为什么不写一个新函数

    int push_unique(Stack *stack, void *newElem) {
        if (find_value(stack, newElem) != NULL) {
            return 1; // indicate a collision
        }
        push(stack, newElem); // re-use old function
        return 0; // indicate success
    }
    

    现在您已将问题简化为写作

    Node *find_value(Stack *stack, void *value);
    

    ...你能做到吗?

    【讨论】:

    • 我是否可以使用我在问题中发布的 for 循环遍历堆栈,然后使用 memcmp 来查看它们是否相等?感谢您的帮助,非常有用的建议
    • 差不多,是的。不知道你是否需要memcmp或者你原来的指针比较是否OK。
    • @Useless,一个有用的观察。正如我在下面的回答中所建议的那样,我还赞成 find_value() 对哈希表进行操作以获得 O(1) 搜索时间
    【解决方案4】:

    我不确定您是否意识到这一点,但您建议的实现是对链表执行线性搜索。如果您将 2,000 个元素推送到堆栈上,每个元素值平均有 2 个重复项,那么就是对链表的 2,000 次搜索,平均在 500-750 个链接之间(这取决于何时,IE:什么顺序,重复项呈现给搜索功能。这需要 100 万次以上的比较。不漂亮。

    在上面的 find_value() 中更有效的重复检测可以使用搜索时间 O(1) 的哈希表或搜索时间 O(log N) 的树。前者如果您知道您可能将多少个值推入堆栈,而后者如果数字未知,例如从套接字实时接收数据时。 (如果前者你可以在一个数组中实现你的堆栈,而不是一个更慢、更冗长的链表)

    在任何一种情况下,为了正确维护哈希表,您的 pop() 函数都需要与哈希表 hashpop() 函数配对,该函数将从哈希表中删除匹配的值。

    使用哈希表,您的堆栈可以只指向位于其哈希位置的元素值 - 从 find_value() 返回。然而,对于自平衡树,节点的位置以及元素值会一直在变化,因此您需要将元素的值存储在堆栈和树中。除非您在内存非常紧张的环境中编写代码,否则第二个数据结构所能提供的性能将非常值得适度的内存成本。

    【讨论】: