【问题标题】:Explanation of how stacks work in C解释堆栈如何在 C 中工作
【发布时间】:2011-01-16 00:52:23
【问题描述】:

我只是想简单解释一下将数据推送到堆栈时的链接过程。我知道如何使用我书中的代码进行构建,但我不确定我是否理解当您将堆栈头链接从一个移动到下一个时该过程是如何工作的。

对于像这样的堆栈:

typedef struct node
{
    void dataptr;
    struct node* link;
}STRUCT_NODE;

typedef struct
{
    int count;
    STACK_NODE* top;
}STACK;

如何更改链接以指向推送到堆栈上的新数据。我也不知道

【问题讨论】:

  • 对于像这样的堆栈:typedef struct node{ void dataptr; struct node* link; }STRUCT_NODE typedef struct{ int count; STACK_NODE* top; }STACK; 如何更改链接以指向堆栈上推送的新数据。我也不知道
  • 我也不知道怎么把东西放到代码里?有人可以告诉我该怎么做。抱歉,我是这个网站的新手

标签: c data-structures stack


【解决方案1】:

堆栈可以通过多种方式实现,但考虑到您提出问题的方式,我假设您的堆栈只是一个链表,类似于

head
↓
A → B → C → D → 0

“当您将堆栈头链接从一个移动到下一个时”图片会变为:

    head
    ↓
A → B → C → D → 0

当然 A 在此图中不再可访问,因此您最好在某个地方有另一个指向它的指针(只是为了处理它),但这是如何弹出堆栈的要点(通过使head = head->next 如果堆栈中的每个节点都是 struct nodenext 字段,则 struct node*,当然 head 也是 struct node*

这是为了从堆栈中弹出一些东西(在这种情况下,您应该释放 A 使用的内存)。详细步骤如下:

1/ 保存旧头。

      head
      ↓
old → A → B → C → D → 0

2/ 调整头部。

          head
          ↓
old → A → B → C → D → 0

3/ 回老头(old指向)。

如果您是在谈论将某些东西推到堆栈上,那么这是一个涉及以下操作的操作:

1/ 创建一个新元素。

    head
    ↓
Z   A → B → C → D → 0

2/ 指向当前头部

    head
    ↓
Z → A → B → C → D → 0

3/ 调整头部指向它。

head
↓
Z → A → B → C → D → 0

【讨论】:

  • @Alex,图表 +1,我一直很喜欢。但我认为你的操作有误(pop,而不是 push),所以我希望你不介意我把它扩大了一点。如果您愿意,请随时清理我的烂摊子。
  • @Alex,图表太好了。顺便说一句,你是如何创建它们的?
  • @Jay,我刚刚输入了它们——箭头可用(它们是 Unicode 字符),例如在 Mac 上的“字符调色板”对话框中。
  • @pax, tx -- 最初的 Q 没有提到 push 与 pop,所以我只是猜测!-)
  • 我更喜欢带有第一个/最后一个指针的双链表 :) 喜欢这些图片,好主意,因为代码是等宽的
【解决方案2】:

鉴于您的数据结构表示堆栈上的一个节点,以及实际的堆栈本身:

typedef struct node {
    void        *dataptr;
    struct node *link;
} NODE;

typedef struct {
    int  count;
    NODE *top;
} STACK;

您将按如下方式初始化堆栈:

STACK myStack;
myStack.count = 0;
myStack.top = NULL;

这基本上给你一个空堆栈。我将使用top 来确定堆栈是否为空 - 您可以使用counttop(分别为0NULL)来完成这项工作,但最好选择一个并坚持下去,以防您将来编写一些错误的代码,其中缓存计数和实际计数失步:-)

要将一个节点压入堆栈,这是一个简单的操作:

  • 分配新节点并设置有效负载 (1)。
  • 将新节点的链接指向当前头。
  • 将头部指向该新节点 (3)。
  • 增加计数 (4)。

下面的代码展示了如何做到这一点:

/* 1 */ NODE *newNode = malloc (sizeof (NODE)); // should check for failure.
        newNode->dataptr = NULL;
/* 2 */ newNode->link = myStack.top;
/* 3 */ myStack.top = newNode;
/* 4 */ myStack.count++;

这将推送到空堆栈或填充堆栈。空堆栈的边缘情况将看到newNode.link 设置为NULL,然后myStack.top 设置为newNode,这是正确的行为。

从堆栈中弹出一个节点:

  • 保存当前磁头 (1)。
  • 如果当前 head 不为 NULL,则将 head 前进到其链接(并减少计数)。
  • 返回保存的当前磁头 (3)。

翻译成代码的是:

/* 1 */ NODE *popNode = myStack.top;
/* 2 */ if (myStack.top != NULL) {
            myStack.top = myStack.top->link;
            myStack.count--;
        }
/* 3 */ return popNode;

这将返回弹出节点的地址,如果堆栈为空,则返回 NULL

整套操作可以封装如下。希望 cmets 能够结合上面的 cmets 使其不言自明,但请随时提出任何疑虑,我将通过编辑解决它们。


// Error codes (probably should be enums).

#define STACK_ERR_OKAY       0
#define STACK_ERR_NOTEMPTY   1
#define STACK_ERR_NOPAYLOAD  2
#define STACK_ERR_NOMEMORY   3

// Structures.

typedef struct sNode {
    void         *data;   // Payload pointer.
    struct sNode *next;   // Link to next node.
} tNode;

typedef struct {
    int          count;   // Count for fast sizing.
    NODE         *top;    // First node.
} tStack;

// Make a new stack returning its pointer or NULL on failure.

tStack *stackNew (void) {
    tStack stack = malloc (sizeof (tStack));
    if (stack != NULL) {
        stack->count = 0;
        stack->top = NULL;
    }
    return stack;
}

// Delete a current stack, must be empty first.

int stackDel (tStack *stack) {
    if (stack->top != NULL)
        return STACK_ERR_NOT_EMPTY;
    free (stack);
    return STACK_ERR_OK;
}

// Push a pointer payload (no NULLs allowed) onto the stack.

int stackPush (tStack *stack, void *payload) {
    if (payload == NULL)
        return STACK_ERR_NOPAYLOAD;
    tNode *node = malloc (sizeof (tNode));
    if (node == NULL)
        return STACK_ERR_NOMEMORY;
    node->data = payload;
    node->next = stack->top;
    stack->top = node;
    stack->count++;
    return STACK_ERR_OK;
}

// Pop a pointer payload from the stack. Returns NULL if stack was empty.

int stackPop (tStack *stack) {
    tNode *node = stack->top;
    if (node == NULL) {
        return NULL;
    stack->top = node->next;
    stack->count--;
    return node->data;
}

// Get stack size.

int stackSize (tStack *stack) {
    return stack->count;
}

我坚持在删除之前堆栈必须为空的原因是因为代码不确定有效负载是什么。它可能是一个指向内存块(浅)的简单指针,在这种情况下您可以使用:

void stackDel (tStack *stack) {
    tNode *node = stackPop (stack);
    while (node != NULL) {
        free (node);
        node = stackPop (stack);
    }
    free (stack);
}

但是,如果它是一个指向内存的指针,它持有其他指向内存的指针,那么自动释放它所引用的内存就会有更大的问题(它可能最好作为对 API 调用者的回调来完成,因为它将是唯一的野兽知道如何正确释放内存)。将其作为预先调用者的先决条件要简单得多。

【讨论】:

    【解决方案3】:

    假设您有一些名为 my_stack 的 STACK 和一个名为 my_node 的 STACK_NODE。要将 my_node 添加到 my_stack,请执行以下操作:

    my_node.link = my_stack.top;
    my_stack.top = &my_node;
    my_stack.count = my_stack.count + 1;
    

    【讨论】:

      猜你喜欢
      • 2013-02-16
      • 2013-09-10
      • 2011-07-20
      • 2015-10-21
      • 1970-01-01
      • 2018-09-22
      • 2012-05-15
      • 2023-03-03
      • 2015-08-17
      相关资源
      最近更新 更多