Deque(双端队列的双链表双头结点代码实现)C语言

Deque

双端队列顾名思义:两端都可以进行插入删除元素的队列。
在双端队列两端如果进行限制插入删除操作,就会退化成队列或者栈,所以如果你掌握了双端队列,那么队列和栈自然就更加的easy了。
以下是双端队列这个抽象数据类型的英文描述:
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.
PUSH和POP为双端队列头部插入和删除操作
inject和Eject为双端队列尾部插入和删除操作。
首先我们先定义双端队列的物理结构

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

图解形式

在这里插入图片描述
这里的小伙伴很容易理解错,再刷pat时我也遇到这种情况,以前学数据结构的时候,双端队列说是左右都能插入,图解的形式是
在这里插入图片描述
但是实际存储的时候一开始并不是这样,这明显是插入删除多次之后才会变成这样,不管是数组还是链表,一开始的front和rear都是在第一幅图的样子,有些小伙伴会出错的地方在,front插入第一个元素时用尾插法会把front指针往左移添加,其实front一开始的位置是不能动的,而且最好用头插写比较好。

首先我们创建双端队列链表形式

Deque CreateDeque()
{
    Deque p;
    p = (Deque)malloc(sizeof(struct DequeRecord));
    //队列开空间
    p->Front = (PtrToNode)malloc(sizeof(struct Node));
    //队列头部指向一个新地址
    p->Rear = (PtrToNode)malloc(sizeof(struct Node));
    //队列尾部也指向一个新地址
    p->Rear->Last = p->Front;//头尾相等
    p->Front->Next = p->Rear;
    p->Front->Last = NULL;
    p->Rear->Next = NULL;
    return p;
}

因为网上只有单头指针的形式,导致push,pop,inject,eject的代码操作上不够简洁,看上去也难以理解,我最新创了一种双头结点的写法,pat上ac过了,是可行的,虽然多用了一块的空间,但是影响不大。因为这样4个操作统一了,看上去代码也更好看了。

第二步我们写出4种操作的具体原理代码

int Push(ElementType X, Deque D){
    struct Node* tmp;
    tmp = (struct Node*)malloc(sizeof(struct Node));
    //开新的节点
    if (!tmp)return 0;
    tmp->Element = X;//给新节点赋值
    tmp->Last = D->Front;
    tmp->Next = D->Front->Next;
    D->Front->Next->Last = tmp;
    D->Front->Next = tmp;
    return 1;//对其进行新节点头插入双链表的写法
}
ElementType Pop(Deque D)
{
    //队列为空
    if (D->Front->Next == D->Rear)return ERROR;
    //删除头部结点的操作
    int num = D->Front->Next->Element;
    PtrToNode tmp = D->Front->Next;
    D->Front->Next = tmp->Next;
    tmp->Next->Last = D->Front;
    free(tmp);
    return num;
}
int Inject(ElementType X, Deque D)
{
    struct Node* tmp;
    tmp = (struct Node*)malloc(sizeof(struct Node));
    //开新的节点
    if (!tmp)return 0;
    tmp->Element = X;//给新结点赋值
    tmp->Next = D->Rear;
    tmp->Last = D->Rear->Last;
    D->Rear->Last->Next = tmp;
    D->Rear->Last = tmp;
    return 1;//对其进行新节点头插入双链表的写法
}
ElementType Eject(Deque D)
{	
	//队列为空
    if (D->Front->Next == D->Rear)return ERROR;
    //删除尾部的结点
    int num = D->Rear->Last->Element;
    PtrToNode tmp = D->Rear->Last;
    D->Rear->Last = tmp->Last;
    tmp->Last->Next = D->Rear;
    free(tmp);
    return num;
}

4个操作比其他网上的写法要简洁很多,而且操作统一,尾部和头部操作相同,只是front和rear,last和next改变,最最重要的是相比不插尾部头结点的方法要好在,不用判断没有结点插入和一个结点插入的情况了。

最后完整代码贴上,可以自己尝试一下实现

#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;
};



//主函数入口
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;
}




//push,pop,inject,enject4种操作的具体形式
int Push(ElementType X, Deque D){
    struct Node* tmp;
    tmp = (struct Node*)malloc(sizeof(struct Node));//开新的节点
    if (!tmp)return 0;
    tmp->Element = X;//赋值
    tmp->Last = D->Front;
    tmp->Next = D->Front->Next;
    D->Front->Next->Last = tmp;
    D->Front->Next = tmp;
    return 1;
}
ElementType Pop(Deque D)
{
    //队列为空
    if (D->Front->Next == D->Rear)return ERROR;
    int num = D->Front->Next->Element;
    PtrToNode tmp = D->Front->Next;
    D->Front->Next = tmp->Next;
    tmp->Next->Last = D->Front;
    free(tmp);
    return num;
}
int Inject(ElementType X, Deque D)
{
    struct Node* tmp;
    tmp = (struct Node*)malloc(sizeof(struct Node));//开新的节点
    if (!tmp)return 0;
    tmp->Element = X;//赋值
    tmp->Next = D->Rear;
    tmp->Last = D->Rear->Last;
    D->Rear->Last->Next = tmp;
    D->Rear->Last = tmp;
    return 1;
}
ElementType Eject(Deque D)
{
    if (D->Front->Next == D->Rear)return ERROR;
    int num = D->Rear->Last->Element;
    PtrToNode tmp = D->Rear->Last;
    D->Rear->Last = tmp->Last;
    tmp->Last->Next = D->Rear;
    free(tmp);
    return num;
}



//输入判断入口
Operation GetOp()
{
    char a[111];
    scanf("%s", a);
    //push, pop, inject, eject, end
    if (!strcmp("Push", a))
        return push;
    if (!strcmp("Pop", a))
        return pop;
    if (!strcmp("Inject", a))
        return inject;
    if (!strcmp("Eject", a))
        return eject;
    if (!strcmp("End", a))
        return end;
}

//打印函数
void PrintDeque(Deque D)
{
    while (D->Front != D->Rear)
    {

        printf("%d ", Pop(D));
    }
    puts("");
}
//声明:内部代码有部分使用pat

发布了19 篇原创文章 · 获赞 4 · 访问量 500

猜你喜欢

转载自blog.csdn.net/qq_35050438/article/details/103263223