C:队列和内存限制超过

时间:2018-03-09 08:10:47

标签: c data-structures queue

我是C初学者,并决定参加一个小型在线竞赛,以便练习。

在当前问题中,我被要求编写一个struct的队列,该队列响应命令PushBackPopFront

输入包含

  • 一个数字nn <= 1000000),表示输入的命令数。
  • n行。每行包含两个整数ab
    • a 2用于执行PopFront,在这种情况下,b是预期的弹出值。
    • a 3PushBack,在这种情况下,b是要入队的值。

如果我们尝试从空队列中弹出,则返回的值为-1

如果在程序执行期间任何YES返回的值与预期值重合或不同,则执行最后一个命令后,任务是打印NOPushBack

我实施了这个版本,但在提交答案后,在线评委给出Maximum-Limit-Excedeed(在27的最后一次测试中)。

我正在阅读它,这个问题可能与其中一些有关:

  1. 使用太大的数组或数据结构。
  2. 程序中存在无限(或太大)的递归。
  3. 指针的错误用法(诊断为MLE)。
  4. 我不确定是什么问题。在我看来,在一些测试中,节点的添加数量大于删除的数量(这意味着1.发生在我的代码中),这反过来导致while EmptyQueue中的循环太大(2.也会发生)。我无法发现是否有错误的指针用法。

    我的问题是:

    • 我在这里做错了什么?
    • 我该怎么做才能解决这个问题?

    代码:

    #include <stdio.h>
    #include <stdbool.h>
    #include <stdlib.h>
    
    //===================
    //Definitions:
    
    typedef int Item;
    
    typedef struct node
    {
        Item item;
        struct node * next;
    } Node;
    
    typedef struct queue
    {
        Node * front;
        Node * rear;
        long counter;
    } Queue;
    
    //===================
    //Function Prototypes:
    
    void InitializeQueue(Queue * pq);
    bool PushBack(Queue * pq, Item item);
    int PopFront(Queue * pq);
    void EmptyQueue(Queue * pq);
    
    
    int main(void)
    {
        Queue line;
        long n, i;
        int command, expected, received;
        bool check = true;
    
        scanf("%ld", &n);
    
        InitializeQueue(&line);
    
        i = 0;
        while (i < n)
        {
            scanf("%d %d", &command, &expected);
    
            switch (command)
            {
                case 2:
                    received = PopFront(&line);
                    if (received != expected)
                        check = false;
                    break;
                case 3:
                    PushBack(&line, expected);
                    break;
    
            }
            i++;
        }
    
        if (check == true)
            printf("YES\n");
        else
            printf("NO\n");
    
        // free memory used by all nodes
        EmptyQueue(&line);
    
        return 0;
    
    }
    
    
    
    
    void InitializeQueue(Queue * pq)
    {
        pq->front = NULL;
        pq->rear = NULL;
        pq->counter = 0;
    }
    
    bool PushBack(Queue * pq, Item item)
    {
        Node * pnode;
    
        //Create node
        pnode = (Node *)malloc(sizeof(Node));
    
        if (pnode == NULL)
        {
            fputs("Impossible to allocate memory", stderr);
            return false;
        }
        else
        {
            pnode->item = item;
            pnode->next = NULL;
        }
    
        //Connect to Queue
        if (pq->front == NULL)
        {
            pq->front = pnode;
            pq->rear = pnode;
        }
        else
        {
            pq->rear->next = pnode;
            pq->rear = pnode;
        }
    
        pq->counter++;
    
        return true;
    }
    
    
    int PopFront(Queue * pq)
    {
        int popped;
        Node * temp;
    
        temp = pq->front;
    
        if (pq->counter == 0)
            return -1;
        else
        {
            popped = pq->front->item;
            pq->front = pq->front->next;
            free(temp);
            pq->counter--;
            return popped;
        }
    }
    
    
    
    
    void EmptyQueue(Queue * pq)
    {
        int dummy;
    
        while (pq->counter != 0)
            dummy = PopFront(pq);
    }
    

    感谢。

2 个答案:

答案 0 :(得分:2)

我认为代码在功能上实际上没有任何问题,虽然它可以改进一些格式: - )

提及一件事:

  

任务是检查执行PopFront后返回的值是否与预期值一致。如果是,则打印YES。打印NO,否则。

我会将此视为每个 PopFront的要求。您似乎存储了故障情况,并且最后只打印了YESNO

我建议将其作为一个开始,然后看看在线评判的回归。

这一切都忽略了这样一个事实:除非你可以重现问题,否则调试代码实际上相当困难。如果您无法从在线竞赛中获取数据集,则可能值得生成您自己的(大)数据集,以确定您是否可以让代码失败。

一旦出现可重复的故障,调试变得非常容易。

尽管不太可能,但可能(如评论中指出的mch)正在与有限的内存发生冲突。我认为这不太可能,因为你自己的评论表明最后只使用了5meg的空间,这并不繁琐。但是,如果 这种情况,那可能是因为每个整数都有一个随之携带的指针的开销。

如果您想调查该途径,可以按如下方式稍微调整结构(除去不必要的counter):

#define ITEMS_PER_NODE 1000

typedef struct node {
    Item item[ITEMS_PER_NODE];  // array of items.
    int startIndex;             // start index (one to pop from).
    int nextIndex;              // next index (one to push at).
    struct node *next;          // next node.
} Node;

typedef struct queue {
    Node *front;                // first multi-item node.
    Node *rear;                 // last multi-item node.
} Queue;

这个想法是为每个节点存储许多项目,以便大大减少next指针的开销(每个项目的一个指针而不是一个每个项目。)

然后,队列操作的代码会变得稍微复杂一些,但仍然可以理解。首先,用于创建新节点的辅助函数,准备将数据添加到:

// Helper to allocate a new node and prep it for appending.
// Returns node or NULL (and prints error) if out of memory.

Node *GetNewNode(void) {
    Node *pnode = malloc (sizeof(Node));
    if (pnode == NULL)
        fputs ("Impossible to allocate memory", stderr);
    else
        pnode->startIndex = pnode->nextIndex = 0;
    return pnode;
}

接下来,大部分未更改的队列初始化:

void InitializeQueue (Queue *pq) {
    pq->front = pq->rear = NULL;
}

回退稍微复杂一点,因为如果队列为空或当前最后一个节点已到达结尾,它首先会添加一个新的多项目节点。无论是否发生,项目都会添加到最终节点:

bool PushBack (Queue *pq, Item item) {
    // Default to adding to rear node (assuming space for now).

    Node *pnode = pq->rear;

    // Make sure queue has space at end for new item.

    if (pq->front == NULL) {
        // Handle empty queue first, add single node.

        if ((pnode = GetNewNode()) == NULL)
            return false;
        pq->front = pq->rear = pnode;

    } else if (pq->rear->nextItem == ITEMS_PER_NODE) {
        // Handle new node needed in non-empty queue, add to rear of queue.

        if ((pnode = GetNewNode()) == NULL)
            return false;
        pq->rear->next = pnode;
        pq->rear = pnode;
    }

    // Guaranteed space in (possibly new) rear node now, just add item.

    pq->rear->item[pq->rear->nextIndex++] = item;
}

Popping也有点复杂 - 它获取返回的值然后删除第一个节点,如果它现在已经用完了。如果删除的节点是唯一的节点,那么这也可能需要清除队列:

int PopFront (Queue * pq) {
    // Capture empty queue.

    if (pq->first == NULL)
        return -1;

    // Get value to pop.

    Node *currFront = pq->front;
    int valuePopped = currFront->item[currFront->startIndex++];

    // Detect current node now empty, delete it.

    if (currFront->startItem == currFront->endIndex) {
        // Detect last node in queue, just free and empty entire queue.

        if (currFront == pq->rear) {
            free (currFront);
            pq->front = pq->rear = NULL;
        } else {
            // Otherwise remove front node, leaving others.

            pq->front = currFront->next;
            free (currFront);
        }
    }

    // Regardless of queue manipulation, return popped value.

    return valuePopped;
}

除了我们清除节点而不是项目的事实之外,清空队列基本没有变化:

void EmptyQueue (Queue * pq) {
    // Can empty node at a time rather than item at a time.

    while (pq->front != NULL) {
        Node *currentFront = pq->front;
        pq->front = pq->front->next;
        free (currentFront);
    }
}

答案 1 :(得分:-1)

我认为最好在我发布的代码中使用更简单的方法。

以下行中的代码与比赛所需的输入/输出不匹配,但包含一个功能简单的方法来解决问题:一个简单的堆栈管理器! (如果我正确理解的话)。

#include <stdio.h>
#include <malloc.h>

int * stack;
int * base;
int cnt;

/* To emulate input file */
struct stFile {
    int n;
    struct stCmd {
        int a;
        int b;
    } cmd[200]; // 200 is an arbitrary value.
} fdata = {
    20,
    {
        {2,0},
        {2,0},
        {2,0},
        {3,35},
        {2,0},
        {3,4},
        {2,0},
        {2,0},
        {2,0},
        {3,12},
        {3,15},
        {3,8},{3,18},
        {2,0},
        {2,0},
        {3,111},
        {2,0},
        {2,0},
        {2,0},
        {2,0},
        {3,8},{3,18},{3,8},{3,18},{3,8},{3,18},{3,8},{3,18},{3,8},{3,18},
        {3,11},{3,13},{3,11},{3,11},{3,11},{3,11},{3,11},{3,11},
        {3,11},{3,13},{3,11},{3,11},{3,11},{3,11},{3,11},{3,11},
        {2,0},
        {2,0},
        {2,0},
        {2,0},
        {2,0},
        {2,0},
        {2,0},
        {2,0},
        {0,0}
    }
};

int push(int item)
{
    if (cnt) {
        *stack = item;
        stack++;
        cnt--;
        return 0;
    } else {
        return 1;
    }
}

int pop(int *empty)
{
    if (stack!=base) {
        stack--;
        cnt++;
        if (empty)
            *empty = 0;
    } else {
        if (empty)
            *empty = 1;
    }

    return *stack;
}

int main(void)
{
    int i=0,e=0;

    cnt = fdata.n;
    base = stack = malloc(cnt*sizeof(int));

    if (!base) {
        puts("Not enough memory!");
        return 1;
    }

    while(fdata.cmd[i].a!=0) {
        switch(fdata.cmd[i].a) {
        case 2:
            printf("popping ...: %d ",pop(&e));
            printf("empty: %d\n",e);
            break;

        case 3:
            e = push(fdata.cmd[i].b);
            printf("pushing ...: %d %s\n",fdata.cmd[i].b,(e)?"not pushed":"pushed");
            break;

        default:
            break;

        };

        i++;
    }

    if (base)
        free(base);

    return 0;
}