day17

一链式栈

1 > 链式存储的栈,称为链式栈

2 > 对于单链表而言,我们可以使用,使用头插头删完成一个栈,或者尾插尾删完成链式栈

3 > 头插头删:链表的头部就是栈顶,链表的尾部就是栈底(常用)

4 > 尾插尾删:链表的尾部就是栈顶,链表的头部就是栈底

                                                                                                        二、队列                                      

2.1 队列介绍

1 > 队列也是操作受限的线性表:所有操作只能在端点处进行,其删除和插入必须在不同端进行

2 > 允许插入操作的一端称为队尾,允许删除操作的一端称为队头

3 > 特点:先进先出(FIFO)

4 > 分类:顺序存储的队列称为顺序队列 ;链式存储的队列,称为链式队列                                                                                                                                                                                                                                                                                                                                                     

                                                                                                                                                                                                                                2.2 顺序队列                                                                                   

1 > 使用一片连续存储的空间存储队列,并且给定两个变量,分别记录队头和队尾下标

2 > 普通顺序队列使用中,存在“假溢满”现象   

假溢满:队列中明明还有存储空间,但是由于队尾已经达到了数组的最大下标,不能在继续入队元素了

3 > 为了解决“假溢满”现象,我们引入了循环顺序队列

                                                                                                                2.3 循环顺序队列                                

1 > 循环顺序队列:通过相关操作,当对头或队尾达到数组最大下标时,可以返回到下标为0的位置

2 > 结构体类型:一个数组存储队列,两个变量分别存储队头和队尾的下标

注意:需要人为浪费一个存储空间,用于判满

                                                                                                                   

#define MAX 8             // 队列最大长度

   typedef int datatype; // 数据元素类型

// 定义队列类型

typedef struct

{

    datatype data[MAX]; // 存储队列的容器

    int front;          // 记录队头下标

    int tail;           // 记录队尾下标

} SeqQueue, *SeqQueuePtr;

3 > 创建队列

// 创建队列

SeqQueuePtr queue_create()

{

    // 在堆区申请一个队列的大小

    SeqQueuePtr Q = (SeqQueuePtr)malloc(sizeof(SeqQueue));

    if (NULL == Q)

    {

        printf("创建失败\n");

        return NULL;

    }

    // 初始化

    bzero(Q->data, sizeof(Q->data)); // 初始化数组

    Q->front = Q->tail = 0;          // 队列为空

    printf("队列创建成功\n");

    return Q;

}

4 > 判空判满

     

    // 队列判空

    int queue_empty(SeqQueuePtr Q)

{

    return Q->front == Q->tail;

}

// 队列判满

int queue_full(SeqQueuePtr Q)

{

    return (Q->tail + 1) % MAX == Q->front;

}

5 > 入队:将数据放入队尾所在位置

   

    // 入队

    void queue_push(SeqQueuePtr Q, datatype e)

{

    // 判断逻辑

    if (NULL == Q || queue_full(Q))

    {

        printf("入队失败\n");

        return;

    }

    // 入队逻辑

    Q->data[Q->tail] = e;

    // 队列变化:队尾后移

    Q->tail = (Q->tail + 1) % MAX;

    printf("入队成功\n");

}

6 > 遍历队

     

    // 遍历队列

    void  queue_show(SeqQueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("遍历失败\n");

        return;

    }

    // 遍历逻辑

    printf("从队头到队尾元素分别是:");

    for (int i = Q->front; i != Q->tail; i = (i + 1) % MAX)

    {

        printf("%d\t", Q->data[i]);

    }

    printf("\n");

}

7 > 出队

   

    // 出队

    void queue_pop(SeqQueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("出队失败\n");

        return;

    }

    // 出队逻辑

    printf("%d出队成功\n", Q->data[Q->front]);

    // 队头后移

    Q->front = (Q->front + 1) % MAX;

}

8 > 求队列的大小:只用队头和队尾完成

   

    // 求队列的大小

    int queue_size(SeqQueuePtr Q)

    // 在不使用循环的情况下求大小

    // 判断逻辑

    if (NULL == Q)

    {

        printf("失败\n");

        return -1;

    }

    return (Q->tail - Q->front + MAX) % MAX; // 核心语句

}

9 > 销毁队列

   

    // 销毁队列

    void queue_destroy(SeqQueuePtr Q)

{

    if (NULL != Q)

    {

        free(Q);

        Q = NULL;

    }

    printf("销毁成功\n");

}

2.4 所有代码

1 > loopseqqueue.h             

#ifndef LOOPSEQQUEUE_H

#define LOOPSEQQUEUE_H

#include <myhead.h>

#define MAX 8                          // 队列最大长度

                 typedef int datatype; // 数据元素类型

// 定义队列类型

typedef struct

{

    datatype data[MAX]; // 存储队列的容器

    int front;          // 记录队头下标

    int tail;           // 记录队尾下标

} SeqQueue, *SeqQueuePtr;

// 创建队列

SeqQueuePtr queue_create();

// 队列判空

int queue_empty(SeqQueuePtr Q);

// 队列判满

int queue_full(SeqQueuePtr Q);

// 入队

void queue_push(SeqQueuePtr Q, datatype e);

// 遍历队列

void queue_show(SeqQueuePtr Q);

// 出队

void queue_pop(SeqQueuePtr Q);

// 求队列的大小

int queue_size(SeqQueuePtr Q);

// 销毁队列

void queue_destroy(SeqQueuePtr Q);

#endif

2 > loopseqqueue.c    

#include "loopseqqueue.h"

 // 创建队列           

 SeqQueuePtr  queue_create()

{

    // 在堆区申请一个队列的大小

    SeqQueuePtr Q = (SeqQueuePtr)malloc(sizeof(SeqQueue));

    if (NULL == Q)

    {

        printf("创建失败\n");

        return NULL;

    }

    // 初始化

    bzero(Q->data, sizeof(Q->data)); // 初始化数组

    Q->front = Q->tail = 0;          // 队列为空

    printf("队列创建成功\n");

    return Q;

}

// 队列判空

int queue_empty(SeqQueuePtr Q)

{

    return Q->front == Q->tail;

}

// 队列判满

int queue_full(SeqQueuePtr Q)

{

    return (Q->tail + 1) % MAX == Q->front;

}

// 入队

void queue_push(SeqQueuePtr Q, datatype e)

{

    // 判断逻辑

    if (NULL == Q || queue_full(Q))

    {

        printf("入队失败\n");

        return;

    }

    // 入队逻辑

    Q->data[Q->tail] = e;

    // 队列变化:队尾后移

    Q->tail = (Q->tail + 1) % MAX;

    printf("入队成功\n");

}

// 遍历队列

void queue_show(SeqQueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("遍历失败\n");

        return;

    }

    // 遍历逻辑

    printf("从队头到队尾元素分别是:");

    for (int i = Q->front; i != Q->tail; i = (i + 1) % MAX)

    {

        printf("%d\t", Q->data[i]);

    }

    printf("\n");

}

// 出队

void queue_pop(SeqQueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("出队失败\n");

        return;

    }

    // 出队逻辑

    printf("%d出队成功\n", Q->data[Q->front]);

    // 队头后移

    Q->front = (Q->front + 1) % MAX;

}

// 求队列的大小

int queue_size(SeqQueuePtr Q)

{

    // 在不使用循环的情况下求大小

    // 判断逻辑

    if (NULL == Q)

    {

        printf("失败\n");

        return -1;

    }

    return (Q->tail - Q->front + MAX) % MAX; // 核心语句

}

// 销毁队列

void queue_destroy(SeqQueuePtr Q)

{

    if (NULL != Q)

    {

        free(Q);

        Q = NULL;

    }

    printf("销毁成功\n");

}

3 > main.c

#include <myhead.h>

#include "loopseqqueue.h"

    int main(int argc, const char *argv[])

{

    // 调用创建队列函数

    SeqQueuePtr Q = queue_create();

    if (NULL == Q)

    {

        return -1;

    }

    // 调用入队函数

    queue_push(Q, 520);

    queue_push(Q, 1314);

    queue_push(Q, 666);

    queue_push(Q, 999);

    // 调用遍历函数

    queue_show(Q);

    // 调用出队函数

    queue_pop(Q);

    queue_pop(Q);

    queue_pop(Q);

    queue_pop(Q);

    queue_pop(Q);

    queue_show(Q);

    // 销毁队列

    queue_destroy(Q);

    Q = NULL;

    return 0;

}

2.5 链式队列

1 > 链式存储的队列称为链式队列

2 > 实现原理:

单向链表头插尾删实现:链表的头部就是队尾,链表的尾部就是队头

单向链表头删尾插实现:链表的头部就是队头,链表的尾部就是队尾

但是:上述操作中,都要用到链表尾部节点,都需要遍历整个链表完成,效率较低

此时,我们可以引入尾指针的概念,专门指向最后一个节点的指针。

3 > 将一个头指针和一个尾指针封装成一个队列

 4 > 队列类型

 // 定义数据元素类型

 typedef int datatype;

// 定义结点类型

typedef struct Node

{

    union

    {

        datatype data; // 数据域

        int len;       // 长度

    };

    struct Node *next; // 指针域

} Node, *NodePtr;

// 定义队列类型

typedef struct

{

    NodePtr head; // 头指针

    NodePtr tail; // 尾指针

} Queue, *QueuePtr;

5 > 创建队列

 注意:先创建出队列、然后创建链表、将队列的两个指针指向链表

  // 创建队列          

 QueuePtr queue_create()

{

    // 堆区申请一个队列的空间

    QueuePtr Q = (QueuePtr)malloc(sizeof(Queue));

    if (NULL == Q)

    {

        printf("创建失败\n");

        return NULL;

    }

    // 此时 Q->head 和Q->tail是两个野指针

    // 创建一个链表

    Q->head = (NodePtr)malloc(sizeof(Node));

    if (Q->head == NULL)

    {

        printf("创建失败\n");

        free(Q); // 释放队列空间

        return NULL;

    }

    // 给头结点初始化

    Q->head->len = 0;

    Q->head->next = NULL;

    // 将两个指针指向头结点

    Q->tail = Q->head;

    printf("创建成功\n");

    return Q;

}

6 > 队列判空

只需要队头和队尾都指向头结点即可

    // 判空

    int queue_empty(QueuePtr Q)

{

    return Q->head == Q->tail;

}

7 > 入队

    // 入队

    void queue_push(QueuePtr Q, datatype e)

{

    // 判断逻辑

    if (NULL == Q)

    {

        printf("入队失败\n");

        return;

    }

    // 申请结点封装数据

    NodePtr p = (NodePtr)malloc(sizeof(Node));

    if (NULL == p)

    {

        printf("入队失败\n");

        return;

    }

    // 初始化

    p->data = e;

    p->next = NULL;

    // 尾插

    Q->tail->next = p; // 将结点连接到链表上

    // 更新尾指针

    Q->tail = p;

    // 表长变化

    Q->head->len++;

    printf("入队成功\n");

}

8 > 遍历队列

    // 遍历队列

    void queue_show(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("遍历失败\n");

        return;

    }

    // 定义遍历指针,从第一个结点出发

    NodePtr q = Q->head->next;

    while (q != NULL)

    {

        // 只要结点不为空,就输出数据域

        printf("%d\t", q->data);

        q = q->next; // 向后遍历

    }

    printf("\n");

}

9 > 出队

        出队时,需要注意,如果所有节点全部出队,则需要将尾指针重新指向头结点

    // 出队

    void queue_pop(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("出队失败\n");

        return;

    }

    // 出队:头删

    NodePtr p = Q->head->next; // 标记

    Q->head->next = p->next;   // 孤立

    printf("%d出队成功\n", p->data);

    free(p); // 删除

    p = NULL;

    // 判断是否已经全部删除

    if (Q->head->next == NULL)

    {

        // 尾指针重新指向头结点

        Q->tail = Q->head;

    }

    // 表长变化

    Q->head->len--;

}

10 > 销毁队列

    // 销毁队列

    void queue_destroy(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q)

    {

        return;

    }

    // 释放所有的结点

    while (!queue_empty(Q))

    {

        queue_pop(Q); // 不断将结点出队

    }

    // 释放头结点

    free(Q->head);

    Q->head = Q->tail = NULL; // 防止野指针

    // 释放队列空间

    free(Q);

    Q = NULL;

    printf("释放成功\n");

}

11 > 求队列长度

    // 求队列长度

    int queue_size(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q)

    {

        return -1;

    }

    return Q->head->len;

}

2.6 所有代码

1 > looplinkqueue.h

#ifndef LOOPLINKQUEUE_H

#define LOOPLINKQUEUE_H

#include <myhead.h>   

// 定义数据元素类型

typedef int datatype;

// 定义结点类型

typedef struct Node

{

    union

    {

        datatype data; // 数据域

        int len;       // 长度

    };

    struct Node *next; // 指针域

} Node, *NodePtr;

// 定义队列类型

typedef struct

{

    NodePtr head; // 头指针

    NodePtr tail; // 尾指针

} Queue, *QueuePtr;

// 创建队列

QueuePtr queue_create();

// 判空

int queue_empty(QueuePtr Q);

// 入队

void queue_push(QueuePtr Q, datatype e);

// 遍历队列

void queue_show(QueuePtr Q);

// 出队

void queue_pop(QueuePtr Q);

// 求队列长度

int queue_size(QueuePtr Q);

// 销毁队列

void queue_destroy(QueuePtr Q);

#endif

2 > looplinkqueue.c

#include "looplinkqueue.h"

// 创建队列

QueuePtr queue_create()

{

    // 堆区申请一个队列的空间

    QueuePtr Q = (QueuePtr)malloc(sizeof(Queue));

    if (NULL == Q)

    {

        printf("创建失败\n");

        return NULL;

    }

    // 此时 Q->head 和Q->tail是两个野指针

    // 创建一个链表

    Q->head = (NodePtr)malloc(sizeof(Node));

    if (Q->head == NULL)

    {

        printf("创建失败\n");

        free(Q); // 释放队列空间

        return NULL;

    }

    // 给头结点初始化

    Q->head->len = 0;

    Q->head->next = NULL;

    // 将两个指针指向头结点

    Q->tail = Q->head;

    printf("创建成功\n");

    return Q;

}

// 判空

int queue_empty(QueuePtr Q)

{

    return Q->head == Q->tail;

}

// 入队

void queue_push(QueuePtr Q, datatype e)

{

    // 判断逻辑

    if (NULL == Q)

    {

        printf("入队失败\n");

        return;

    }

    // 申请结点封装数据

    NodePtr p = (NodePtr)malloc(sizeof(Node));

    if (NULL == p)

    {

        printf("入队失败\n");

        return;

    }

    // 初始化

    p->data = e;

    p->next = NULL;

    // 尾插

    Q->tail->next = p; // 将结点连接到链表上

    // 更新尾指针

    Q->tail = p;

    // 表长变化

    Q->head->len++;

    printf("入队成功\n");

}

// 遍历队列

void queue_show(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("遍历失败\n");

        return;

    }

    // 定义遍历指针,从第一个结点出发

    NodePtr q = Q->head->next;

    while (q != NULL)

    {

        // 只要结点不为空,就输出数据域

        printf("%d\t", q->data);

        q = q->next; // 向后遍历

    }

    printf("\n");

}

// 出队

void queue_pop(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q || queue_empty(Q))

    {

        printf("出队失败\n");

        return;

    }

    // 出队:头删

    NodePtr p = Q->head->next; // 标记

    Q->head->next = p->next;   // 孤立

    printf("%d出队成功\n", p->data);

    free(p); // 删除

    p = NULL;

    // 判断是否已经全部删除

    if (Q->head->next == NULL)

    {

        // 尾指针重新指向头结点

        Q->tail = Q->head;

    }

    // 表长变化

    Q->head->len--;

}

// 求队列长度

int queue_size(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q)

    {

        return -1;

    }

    return Q->head->len;

}

// 销毁队列

void queue_destroy(QueuePtr Q)

{

    // 判断逻辑

    if (NULL == Q)

    {

        return;

    }

    // 释放所有的结点

    while (!queue_empty(Q))

    {

        queue_pop(Q); // 不断将结点出队

    }

    // 释放头结点

    free(Q->head);

    Q->head = Q->tail = NULL; // 防止野指针

    // 释放队列空间

    free(Q);

    Q = NULL;

    printf("释放成功\n");

}

3 > main.c

#include "looplinkqueue.h"

    int main(int argc, const char *argv[])

{

    // 调用创建函数

    QueuePtr Q = queue_create();

    if (NULL == Q)

    {

        return -1;

    }

    // 调用入队函数

    queue_push(Q, 111);

    queue_push(Q, 222);

    queue_push(Q, 333);

    queue_push(Q, 444);

    // 调用遍历函数

    queue_show(Q);

    // 调用出队函数

    queue_pop(Q);

    queue_pop(Q);

    // 释放队列空间

    queue_destroy(Q);

    Q = NULL;

    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值