3-1 Deque(附测试点)

A "deque" is a data structure consisting of a list of items, on which the following operations are possible:

  • Push(X,D): Insert item X on the front end of deque D.
  • Pop(D): Remove the front item from deque D and return it.
  • Inject(X,D): Insert item X on the rear end of deque D.
  • Eject(D): Remove the rear item from deque D and return it.
    Write routines to support the deque that take O(1) time per operation.

“deque”是一种由项目列表组成的数据结构,可以对其进行以下操作:

推动(X,D):将项目X插入D板前端。
弹出(D):从deque D中取出前面的项目并将其退回。
注入(X,D):将项目X插入D的后端。
弹出(D):将后面的项目从deque D中取出,并将其退回。
编写例程以支持每次操作花费O(1)时间的deque。

Format of functions:

Deque CreateDeque();
int Push( ElementType X, Deque D );
ElementType Pop( Deque D );
int Inject( ElementType X, Deque D );
ElementType Eject( Deque D );

where Deque is defined as the following:

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};

Here the deque is implemented by a doubly linked list with a header. Front and Rear point to the two ends of the deque respectively. Front always points to the header. The deque is empty when Front and Rear both point to the same dummy header.
Note: Push and Inject are supposed to return 1 if the operations can be done successfully, or 0 if fail. If the deque is empty, Pop and Eject must return ERROR which is defined by the judge program.

在这里,deque由一个带有头结点的双向链表实现。Front指向链表的头,Rear指向链表的尾。Front始终指向头结点。当Front和Rear指向同一结点时,deque为空。
注意:如果操作可以成功完成,则Push和Inject应该返回1,如果操作失败,则返回0。如果deque为空,Pop 和 Eject必须返回ERROR。

Sample program of judge:

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

#define ElementType int
#define ERROR 1e5
typedef enum { push, pop, inject, eject, end } Operation;

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};
Deque CreateDeque();
int Push( ElementType X, Deque D );
ElementType Pop( Deque D );
int Inject( ElementType X, Deque D );
ElementType Eject( Deque D );

Operation GetOp();          /* details omitted */
void PrintDeque( Deque D ); /* details omitted */

int main()
{
    ElementType X;
    Deque D;
    int done = 0;

    D = CreateDeque();
    while (!done) {
        switch(GetOp()) {
        case push: 
            scanf("%d", &X);
            if (!Push(X, D)) printf("Memory is Full!\n");
            break;
        case pop:
            X = Pop(D);
            if ( X==ERROR ) printf("Deque is Empty!\n");
            break;
        case inject: 
            scanf("%d", &X);
            if (!Inject(X, D)) printf("Memory is Full!\n");
            break;
        case eject:
            X = Eject(D);
            if ( X==ERROR ) printf("Deque is Empty!\n");
            break;
        case end:
            PrintDeque(D);
            done = 1;
            break;
        }
    }
    return 0;
}

/* Your function will be put here */

Sample Input:

Pop
Inject 1
Pop
Eject
Push 1
Push 2
Eject
Inject 3
End

Sample Output:

Deque is Empty!
Deque is Empty!
Inside Deque: 2 3

代码:

Deque CreateDeque() {
    PtrToNode newNode = (PtrToNode)malloc(sizeof(struct Node));
    newNode->Last = NULL;
    newNode->Next = NULL;

    Deque newPoint = (Deque)malloc(sizeof(struct DequeRecord));
    newPoint->Front = newNode;
    newPoint->Rear = newNode;

    return newPoint;
}
int Push(ElementType X, Deque D) {//f
    PtrToNode newNode = (PtrToNode)malloc(sizeof(struct Node));
    newNode->Element = X;
    newNode->Last = NULL;
    newNode->Next = NULL;

    if (D->Front == D->Rear) {
        D->Front->Next = newNode;
        newNode->Last = D->Rear;

        D->Rear = D->Rear->Next;
    }
    else {
        newNode->Next = D->Front->Next;
        D->Front->Next->Last = newNode;

        newNode->Last = D->Front;
        D->Front->Next = newNode;
    }
    return 1;
}
ElementType Pop(Deque D) {//f
    if (D->Front == D->Rear) {
        return ERROR;
    }

    int temp = D->Front->Next->Element;
    PtrToNode p = D->Front->Next;

    D->Front->Next = p->Next;
    if (p->Next == NULL) {
        D->Rear = D->Front;
    }
    else {
        p->Next->Last = D->Front;
    }
    free(p);
    return temp;
}
int Inject(ElementType X, Deque D) {//r
    PtrToNode newNode = (PtrToNode)malloc(sizeof(struct Node));
    newNode->Element = X;
    newNode->Last = NULL;
    newNode->Next = NULL;

    D->Rear->Next = newNode;
    newNode->Last = D->Rear;

    D->Rear = D->Rear->Next;

    return 1;
}
ElementType Eject(Deque D) {//r
    if (D->Front == D->Rear) {
        return ERROR;
    }

    int temp = D->Rear->Element;
    PtrToNode p = D->Rear;


    D->Rear = D->Rear->Last;
    free(p);
    return temp;

}

测试点:

 

分析:

这是结构体的定义:

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};

第一部分:创建双向链表

Deque CreateDeque() {
    PtrToNode newNode = (PtrToNode)malloc(sizeof(struct Node));
    newNode->Last = NULL;
    newNode->Next = NULL;

    Deque newPoint = (Deque)malloc(sizeof(struct DequeRecord));
    newPoint->Front = newNode;
    newPoint->Rear = newNode;

    return newPoint;
}

首先创建有头结点队列,对应的是代码的2--4行,因为是创建,所以先将里面的指针指向NULL肯定没错。

然后创建包含头指针和尾指针的结构体,对应的是代码的6--8行,这两个指针不能指向NULL,要指向新建的队列的头结点。

第二部分:头插函数

int Push(ElementType X, Deque D) {//f
    PtrToNode newNode = (PtrToNode)malloc(sizeof(struct Node));
    newNode->Element = X;
    newNode->Last = NULL;
    newNode->Next = NULL;

    if (D->Front == D->Rear) {
        D->Front->Next = newNode;
        newNode->Last = D->Rear;

        D->Rear = D->Rear->Next;
    }
    else {
        newNode->Next = D->Front->Next;
        D->Front->Next->Last = newNode;

        newNode->Last = D->Front;
        D->Front->Next = newNode;
    }
    return 1;
}

首先创建新节点,给Element赋值,给两个指针指向NULL,这是基本操作,之后就不说了,基本所有的创建结点都可以先这样。

然后,在头插中,我们会遇到两种情况:

一种是插入的结点是队列的第一个结点,那么我们需要连接头结点与新节点,需要操作两个指针进行连接。

另一种是插入的结点不是队列的第一个结点,那么我们不仅需要连接新节点与头结点之间的两个指针,我们还需要连接新节点与它下一个结点的两个指针。共需要操作四个指针。

所以,我们要用判断语句来区分两种情况。

当头结点为空的时候,也就是第一种情况,插入的结点是队列的第一个结点,那么:

if (D->Front == D->Rear) {
//D->Front保存的位置是头结点
    D->Front->Next = newNode; //头结点的下一个结点指向新节点
    newNode->Last = D->Rear;  //新节点的上一个结点指向头结点

    D->Rear = D->Rear->Next;  //让尾指针指向新节点
}

当头结点不为空的时候,也就是第二种情况,插入的结点不是队列的第一个结点,那么:

注意!代码的顺序不能改变!

else {//头插法
    newNode->Next = D->Front->Next;//让新节点的下一个结点指向原来头结点的下一个结点
    D->Front->Next->Last = newNode;//让原来头结点的下一个结点的上一个结点指向新节点

    newNode->Last = D->Front;//让新节点的上一个结点指向头结点
    D->Front->Next = newNode;//让头结点的下一个结点指向新节点
}

第三部分:头删

ElementType Pop(Deque D) {//f
    if (D->Front == D->Rear) {
        return ERROR;
    }

    int temp = D->Front->Next->Element;
    PtrToNode p = D->Front->Next;

    D->Front->Next = p->Next;
    if (p->Next == NULL) {
        D->Rear = D->Front;
    }
    else {
        p->Next->Last = D->Front;
    }
    free(p);
    return temp;
}

首先在删除时要判断队列是否为空。

然后,因为我们要删除一个结点并且返回删除的值,所以我们用要保存他们的值:

int temp = D->Front->Next->Element;
PtrToNode p = D->Front->Next;

 删除结点时也有两种情况,

一种是队列中只有一个结点,我们删除这个结点之后,队列为空。

另一种是队列中不止一个结点,也就是一般的情况。

当队列中只有一个结点时:

D->Front->Next = p->Next;
if (p->Next == NULL) {
    D->Rear = D->Front;
}

当队列中不止一个结点时:

else {
    p->Next->Last = D->Front;
}

最后都要free和返回值:

D->Front->Next = p->Next;
    if (p->Next == NULL) {
        D->Rear = D->Front;
    }
    else {
        p->Next->Last = D->Front;
    }
    free(p);
    return temp;

最难的部分已经过去了,下面的比较简单

第四部分:尾加

int Inject(ElementType X, Deque D) {//r
    PtrToNode newNode = (PtrToNode)malloc(sizeof(struct Node));
    newNode->Element = X;
    newNode->Last = NULL;
    newNode->Next = NULL;

    D->Rear->Next = newNode; //尾指针指向的结点的下一结点指向新节点
    newNode->Last = D->Rear;//新节点指向尾结点

    D->Rear = D->Rear->Next;//不要忘了移动尾指针

    return 1;
}

首先创建结点。

因为尾指针rear指向的是队列尾,所以我们只需用尾指针连接队列尾和新节点,然后让尾指针移动到下一结点即可。

第五部分:尾删

ElementType Eject(Deque D) {//r
    if (D->Front == D->Rear) {
        return ERROR;
    }

    int temp = D->Rear->Element;
    PtrToNode p = D->Rear;


    D->Rear = D->Rear->Last;//尾指针移动到他的上一个结点
    free(p);
    return temp;

}

首先在删除时要判断队列是否为空是必不可少的。

然后,因为我们要删除一个结点并且返回删除的值,所以我们用要保存他们的值:

int temp = D->Rear->Element;
PtrToNode p = D->Rear;

最后进行删除操作,移动尾指针。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值