栈和队列概念及相关实现

在线性逻辑结构中,可以是顺序存储和链式存储两种方式,其操作可以在线性表中的任意位置实现,可能不符合实际应用需求。优化的线性表,其中包含栈和队列
(注意:一下代码均以C语言实现)

1 栈的概述

所谓的栈,也称为堆栈,是一个特殊的线性逻辑关系。只能在固定端实现栈的读写访问,其固定端称为栈顶,与之对应的另外一端称为栈底(不参与数据运算)。

栈操作特点:先进后出、后进先出;

栈的存储结构:

顺序存储结构:是一个特殊的顺序表,在连续存储空间中顺序存储,通过数据元素存储位置序号表示数据元素之间的逻辑结构关系。

链式存储结构;是一个特殊的链表,在非连续存储空间中存储数据元素,通过指针表示数据元素逻辑结构关系。

2 顺序栈

2.1 顺序栈的操作

2.1.1 顺序栈数据类型的定义
/* 栈数据元素数据类型的定义 */
typedef int data_t;

/* 动态栈数据类型的定义 */
typedef struct sqstack {
    data_t *data;        /* 连续存储空间起始地址 */
    int nmemb;            /* 顺序栈可存储数据元素个数 */
    int top;            /* 栈顶元素存储位置序号 */
} sqstack_t ;
2.1.2 创建顺序栈
sqstack_t *CreateSqStack(int mynmemb)
{
    sqstack_t *stack;
    
    /* 动态创建栈信息存储结构体空间 */
    stack = malloc(sizeof(sqstack_t));
    if (stack == NULL)
        return NULL;
    
    memset(stack, 0, sizeof(sqstack_t));
    /* 动态创建顺序栈数据元素存储空间 */
    stack->data = calloc(mynmemb, sizeof(data_t));
    if (stack->data == NULL) {
        free(stack);
        return NULL;
    }
    stack->nmemb = mynmemb;
    stack->top = -1;        
    
    return NULL;
}
2.1.3 入栈

所谓的栈,也就是栈顶插入数据。

int PushSqStack(sqstack_t *stack, data_t mydata)
{
    if (stack == NULL)
        return -1;
    /* 判断栈空间是否为满,满则入栈失败*/
    if (stack->top == stack->nmemb-1)
        return -1;
    /* 入栈 */
    stack->data[++stack->top] = mydata;
    
    return 0;
}

2.1.4 出栈
/* 出栈 */
int PopSqStack(sqstack_t *stack)
{
    if (stack == NULL)
        return -1;
        
    /* 判断栈空间是否为空 */
    if (stack->top == -1)
        return -1;
    stack->top--;    /* 出栈操作 */
    return 0;
}

2.1.5 查看栈顶元素
/* 查看栈顶元素数据 */
int GetSqStack(sqstack_t *stack, data_t *mydata)
{
    if (stack == NULL)
        return -1;
        
    /* 判断栈空间是否为空 */
    if (stack->top == -1)
        return -1;
    *mydata = stack->data[stack->top];
    
    return 0;
}

2.1.6 其它操作
/* 清空栈 */
void ClearSqStack(sqstack_t *stack)
{
    if (stack == NULL)
        return;
    stack->top = -1;
}
/* 释放栈空间 */
void DestorySqStack(sqstack_t **stack)
{
    free((*stack)->data);
    free(*stack);
    *stack = NULL;
}

2.2 顺序栈分类

顺序栈数据元素存储空间,为连续存储空间,在连续存储空间中,

  1. 可以根据栈的地址生长空间分为递增栈和递减栈

    所谓的递增栈,指的是堆栈的地址是由低地址向高地址方向生长;

    所谓的递减栈,指的是堆栈的地址是由高地址向低地址方向生长。

  2. 可以根据栈顶指针(栈顶序号位置)数据是否有效分为满栈和空栈

    所谓的满栈,指的是栈顶序号位置的数据值为有效数据值;

    入栈,先修改栈顶位置,在写入值;出栈,先读取数据值,在修改栈顶位置;

    所谓的空栈,指的是栈顶序号位置的数据值为无效数据值;

    入栈,先写入数据值,在修改栈顶位置;出栈,先向栈顶位置,在读取数据值;

3 链式栈

所谓的链式栈,是一个特殊的链表,只能在链表的一端进行操作。将**链表的头结点**作为栈顶指针。

队列

1 队列的概述

1.1 队列的概念

所谓的队列,是特殊的线性表;不能在线性表中间位置进行操作,只能在线性表的两端实现操作,此时的两端分别为队头和队尾:

  1. 队尾(rear):只能实现数据元素的写访问(插入数据元素);

  2. 对头(front):只能实现数据元素的读访问(查看队头数据元素、取队头数据元素);

    队列的特点:先进先出、后进后出。

1.2 队列的存储结构

​ 顺序存储和链式存储两种方式

2 顺序队列

所谓顺序队列,指的是队列的顺序存储结构,此时队列中的数据元素在练习内存空间中存储。

2.1 顺序队列的理解

  1. 普通的顺序队列

在这里插入图片描述

  1. 顺序循环队列

在这里插入图片描述

2.2 顺序队列的操作

2.2.1 顺序循环队列数据类型的定义
/* 数据元素数据类型的定义 */
typedef int data_t;

/* 顺序循环队列 */
typedef struct sqqueue
    data_t *data;
    int nmemb;
    int front;            /* 队头游标:存储队头元素序号 */
    int rear;            /* 队尾游标:存储队尾元素序号 */
} sqqueue_t;
2.2.2 顺序循环队列的创建
sqqueue_t *CreateSqQueue(int mynmemb)
{
    sqqueue_t *queue;
    
    /* 动态创建顺序循环队列信息存储结构体空间 */
    queue = malloc(sizeof(sqqueue_t));
    if (queue == NULL)
        return NULL;
    
    /* 动态开辟顺序循环队列数据元素存储空间 */
    queue->data = calloc(mynmemb, sizeof(data_t));
    if (queue->data == NULL) {
        free(queue);
        return NULL;
    }
    queue->nmemeb = mynmemb;
    queue->front = 0;
    queue->rear = 0;
    
    return queue;
}
2.2.3 顺序队列操作实现
/* 入队 */
int EnSqQueue(sqqueue_t *queue, data_t mydata)
{
    if (queue == NULL)
        return -1;
    /* 判断队列是否为满队列 */    
    if ((queue->rear - queue->front) % queue->nmemb == queue->nmemb -1)
        return -1;
    
    queue->data[queue->rear] = mydata;
    queue->rear = (queue->rear+1) % queue->nmemb;
    
    return 0;
}
/* 出队 */
int DeSqQueue(sqqueue_t *queue)
{
    if (queue == NULL)
        return -1;
    /* 判断队列是否为空队列 */
    if (queue->rear == queue->front)
        return -1;
    /* 出队运算 */
    queue->front = (queue->front+1) % queue->nmemb;
    return 0;
}
/* 查看队头数据元素 */
int GetSqQueue(sqqueue_t *queue, data_t *mydata)
{
    if (queue == NULL)
        return -1;
    /* 判断队列是否为空队列 */
    if (queue->rear == queue->front)
        return -1;
    
    *mydata = queue->data[queue->front];
    
    return 0;
}
/* 判断队列是否为空 */
int isEmptySqQueue(sqqueue_t *queue)
{
    return (queue->rear == queue->front);
}
/* 判断队列是否为空 */
int isFillSqQueue(sqqueue_t *queue)
{
     return ((queue->rear - queue->front) % queue->nmemb == queue->nmemb -1);
}
/* 清空队列 */
void ClearSqQueue(sqqueue_t *queue)
{
    queue->front = queue->rear = 0;
}
/* 销毁队列 */
void DestorySqQueue(sqqueue_t **queue)
{
    free((*queue)->data);
    free(*queue);
    *queue = NULL;
}

3 链式队列

所谓的链式队列,表示为队列的链式存储结构,是特殊的链表。只能在链表的两端进行访问,其中一端(链表头部)进行数据的读操作(读取队头数据元素的数据、出队操作),另外一端(链表尾部)进行数据的写操作(入队操作)。

3.1 链式队列数据类型的定义

/* 链式队列中结点数据域的数据类型定义 */
typedef int data_t;

/* 链式队列中结点是数据类型定义 */
typedef struct node {
    data_t data;        /* 数据域 */
    struct node *next;    /* 指针域 */
} node_t;

/* 链式队列数据类型 */
typedef struct linkqueue {
    node_t * front;        /* 队头指针 */
    node_t * rear;        /* 队尾指针 */
} linkqueue_t;

3.2 链式队列的创建(初始化)

linkqueue_t *CreateLinkQueue(void)
{
    linkqueue_t *queue;
    
    /* 动态创建链式队列队头指针和队尾指针存储空间 */
    queue = malloc(sizeof(linkqueue_t));
    if (queue == NULL)
        return NULL;
        
    /* 创建无效数据结点,作为队头指针结点 */
    queue->front = malloc(sizeof(node_t));
    if (queue->front == NULL) {
        free(queue);
        return NULL;
    }
    queue->front->next = NULL;
    queue->rear = queue->front;    /* 空队列 */
    
    return queue;
}

3.3 链式队列的操作

/* 入队操作 */
int EnLinkQueue(linkqueue_t *queue, data_t mydata)
{
    node_t *q;
    
    /* 判断链式队列是否存在 */
    if(queue == NULL)
        return -1;
    /* 判断链式队列尾部结点是否存在 */
    if(queue->rear == NULL)
        return -1;
    /* 创建插入数据结点 */    
    q = malloc(sizeof(node_t));
    if (q == NULL) 
        return -1;
    memset(q, 0, sizeof(node_t));
    q->data = mydata;    /* 结点数据初始化 */
    q->next = NULL;
    queue->rear->next = q;    /* 插入结点:链表尾部结点后插入结点 */
    queue->rear = q;        /* 修改队尾指针 */
    
    return 0;
}

/* 读取队头结点数据 */
int GetLinkQueue(linkqueue_t *queue, data_t *mydata)
{    
    /* 判断链式队列是否存在 */
    if(queue == NULL)
        return -1;
    /* 判断链式队列头部结点是否存在 */
    if(queue->front == NULL)
        return -1;
    /* 判断链式队列是否为空队列 */    
    if (queue->front->next == NULL)
        return -1;
    
    *mydata = queue->front->next->data;
    
    return 0;
}

int DeLinkQueue(linkqueue_t *queue)
{
    node_t *q;
    
    /* 判断链式队列是否存在 */
    if(queue == NULL)
        return -1;
    /* 判断链式队列头部结点是否存在 */
    if(queue->front == NULL)
        return -1;
    /* 判断链式队列是否为空队列 */    
    if (queue->front->next == NULL)
        return -1;
    
    /* 出队操作 */
    q = queue->front->next;
    queue->front->next = q->next;
    free(q);
    
    if(queue->front->next == NULL)
        queue->rear = queue->front;
        
    return 0;
}

4 练习

  1. 如何使用两个队列实现一个栈

在这里插入图片描述

void push(linkqueue_t *queue_a, linkqueue_t *queue_b, data_t mydata)
{
   if (isEmptyLinkQueue(queue_a)) {
       EnLinkQueue(queue_b, mydata);
   } else {
       EnLinkQueue(queue_a, mydata);
   }
}

int pop(linkqueue_t *queue_a, linkqueue_t *queue_b, data_t *mydata)
{
   if (isEmptyLinkQueue(queue_a)) {
       if (isEmptyLinkQueue(queue_b)) {
           return -1;    /* 没有数据可以出栈 */
       }
       while(1) {
           if (GetLinkQueue(queue_b, mydata) == -1) 
               return -1;
           DeLinkQueue(queue_b);
           if (isEmptyLinkQueue(queue_b)) {
               return 0;
           }
           EnLinkQueue(queue_a, *mydata);
       }
   } else {
       while(1) {
           if (GetLinkQueue(queue_a, mydata) == -1) 
               return -1;
           DeLinkQueue(queue_a);
           if (isEmptyLinkQueue(queue_a)) {
               return 0;
           }
           EnLinkQueue(queue_b, *mydata);
       }
   }
}


int main()
{
   data_t mydata;
   linkqueue_t *queue_a;
   linkqueue_t *queue_b;
   
   queue_a = CreateLinkQueue();
   if (queue_a == NULL)
       return -1;
   
   queue_b = CreateLinkQueue();
   if (queue_b == NULL)
       return -1;    
   
   mydata = 5;
   while(mydata) {
       printf("push data : %d\n", mydata);
       push(queue_a, queue_b, mydata);        /* 数据元素的入栈先后顺序为:5 4 3 2 1 */
       mydata--;
   }
   printf("-----------%d------------\n", __LINE__);
   
   while(1) {
       if (pop(queue_a, queue_b, &mydata) == -1)    /* 数据元素的出栈先后顺序:1  2 3 4 5 */
           break;
       printf("pop data : %d\n", mydata);    
   }
}
  1. 如何使用两个栈实现一个队列

在这里插入图片描述

int DeQueue(node_t * top_a, node_t * top_b, data_t *mydata)
{
    if (top_b->next == NULL) {
        while(1) {
            if (GetLinkStack(top_a, mydata) == -1)
                break;
            PopLinkStack(top_a);
            PushLinkStack(top_b, *mydata);
        }
    } 
    
    if (top_b->next == NULL)
        return -1;
    GetLinkStack(top_b, mydata);
    PopLinkStack(top_b);
    
    return 0;
}

int EnQueue(node_t * top_a, node_t * top_b, data_t mydata)
{
    PushLinkStack(top_a, mydata);
}

int main()
{
    node_t * top_a;
    node_t * top_b;
    data_t mydata;
    
    top_a = CreateLinkStack();
    if (top_b == NULL)
        return -1;
    top_b = CreateLinkStack();
    if (top_b == NULL)
        return -1;
        
    mydata = 5;    
    while(mydata) {
        printf("%d push is %d\n", mydata--, EnQueue(top_a, top_b, mydata));
    }
    
    while(1) {
        if (-1 == DeQueue(top_a, top_b, &mydata)) 
            break;
        printf("mydata = %d\n", mydata);
    }
}
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值