数据结构A2

基本概念

栈是一种逻辑结构,是特殊的线性表。特殊在:
只能在固定的一端操作
只要满足上述条件,那么这种特殊的线性表就会呈现一种“后进先出”的逻辑,这种逻辑就被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们、嵌套函数的参数等等。

由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的“插入”、“删除”,另起了
下面这些特定的名称:
栈顶:可以进行插入删除的一端
栈底:栈顶的对端
入栈:将节点插入栈顶之上,也称为压栈,函数名通常为push()
出栈:将节点从栈顶剔除,也称为弹栈,函数名通常为pop()
取栈顶:取得栈顶元素,但不出栈,函数名通常为top()

基于这种固定一端操作的简单约定,栈获得了“后进先出”的基本特性,如下图所示,最后一个放入的元素,最先被拿出来:

存储形式

栈只是一种数据逻辑,如何将数据存储于内存则是另一回事。一般而言,可以采用顺序存储形成顺序栈,或采用链式存储形成链式栈。

  • 顺序栈

顺序存储意味着开辟一块连续的内存来存储数据节点,一般而言,管理栈数据除了需要一块连续的
内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置,如果有多线程还需要
配互斥锁和信号量等信息,为了便于管理,通常将这些信息统一于在一个管理结构体之中:

// 顺序栈节点
struct seqStack
{
    datatype *data; // 顺序栈入口
    int size; // 顺序栈总容量
    int top; // 顺序栈栈顶元素下标
};
  • 链式栈

链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创
建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:

// 链式栈节点
typedef struct node
{
    datatype data;
    struct node *next;
}node;
// 链式栈管理结构体
struct linkStack
{
    node *top; // 链式栈栈顶指针
    int size; // 链式栈当前元素个数
};

基本操作

不管是顺序栈,链式栈,栈的操作逻辑都是一样的,但由于存储形式不同,代码的实现是不同的。下面分别将顺序栈和链式栈的基本核心操作罗列出来:

  • 顺序栈

sstack.h

#ifndef __SSTACK_H
#define __SSTACK_H
// 数据类型
typedef int DATA;
// 顺序栈结构体
typedef struct
{
    DATA *pData; // 栈中元素的地址
    int size; // 栈的总容量
    int top; // 栈顶元素下标
}SeqStack;
// 初始化栈
int SStack_init(SeqStack *s, int num);
// 判断栈是否已满
int SStack_isfull(SeqStack *st);
// 判断栈是否为空
int SStack_isempty(SeqStack *st);
// 入栈/压栈
int SStack_push(SeqStack *st,DATA data);
// 出栈/弹栈
int SStack_pop(SeqStack *st,DATA *data);
// 回收栈
int SStack_free(SeqStack *st);
#endif

sstack.c

#include 
#include "sstack.h"
// 初始化栈
int SStack_init(SeqStack* s,int num)
{
    s -> pData = (DATA*)calloc(sizeof(DATA),num);
    if(s -> pData == NULL)
        return -1;
    s -> size = num ;
    s -> top = -1;
    return 0;
}
// 判断栈是否已满
int SStack_isfull(SeqStack *st)
{
    return st -> top + 1 == st -> size;
}
// 判断栈是否为空
int SStack_isempty(SeqStack *st)
{
    return st -> top == -1;
}
// 压栈/入栈
int SStack_push(SeqStack *st,DATA data)
{
    if(SStack_isfull(st))
        return -1;
    st -> top++;
    st -> pData[st -> top] = data;
    return 0;
}
// 出栈/弹栈
int SStack_pop(SeqStack *st,DATA *data)
{
    if(SStack_isempty(st))
        return -1;
    *data = st -> pData[st -> top];
    st -> top--;
    return 0;
}
// 回收栈
int SStack_free(SeqStack *st)
{
    if(st -> pData)
    {
        free(st->pData);
        st -> pData = NULL;
    }
    st -> top = -1;
}    

sstack_main.c

#include "sstack.h"
#include 
int main(void)
{
    SeqStack st;
    SStack_init(&st,10);
    register int i = 1;
    for(; i <=10; i++)
        SStack_push(&st,i);
    if(-1 == SStack_push(&st,1024))
        fprintf(stderr,"满栈,插入失败\n");
    while(!SStack_isempty(&st))
    {
        DATA data = 0;
        SStack_pop(&st,&data);
        printf("%4d",data);
    }
    printf("\n");
    SStack_free(&st);
    return 0;
}
  • 链式栈

linkstack.h

#ifndef __LINKSTACK_H
#define __LINKSTACK_H
// 数据类型
typedef int DATA;
// 链式栈节点
typedef struct _node
{
    DATA data; // 数据
    struct _node *next; // 指向下一个栈的节点
}NODE;
// 链式栈管理结构体
typedef struct
{
    NODE *pHead;// 链式栈栈顶指针
    int size; // 链式栈当前元素个数
    int num;
}LinkStack;
// 初始化链式栈
int LStack_init(LinkStack *s, int num);
// 判断栈是否已满
int LStack_isfull(LinkStack *st);
// 判断栈是否为空
int LStack_isempty(LinkStack *st);
// 压栈/入栈
int LStack_push(LinkStack *st,DATA data);
// 弹栈/出栈
int LStack_pop(LinkStack *st,DATA *data);
// 回收栈
int LStack_free(LinkStack *st);
#endif    

linkstack.c

#include "linkstack.h"
#include 
#include 
// 初始化栈
int LStack_init(LinkStack *st, int num)
{
    st -> pHead = NULL;
    st -> size = num;
    st -> num = 0;
    return 0 ;
}
// 判断栈是否已满
int LStack_isfull(LinkStack *st)
{
    return st -> num == st -> size;
}
// 判断栈是否为空
int LStack_isempty(LinkStack *st)
{
    return st -> num == 0;
}
// 入栈
int LStack_push(LinkStack *st,DATA data)
{
    if(LStack_isfull(st))
        return -1;
    NODE* p = (NODE*)malloc(sizeof(NODE));
    if(!p)
        return -1;
    p -> data = data;
    p -> next = st -> pHead;
    st -> pHead = p;
    (st -> num)++;
    return 0;
}
// 出栈
int LStack_pop(LinkStack *st,DATA *data)
{
    if(LStack_isempty(st))
        return -1;
    NODE* p = st -> pHead;
    if(!p)
        return -1;
    *data = p -> data;
    st -> pHead = p -> next;
    free(p);
    (st -> num)--;
    return 0;
}
// 回收栈
int LStack_free(LinkStack *st)
{
    NODE* p = st -> pHead, *q = NULL;
    while(p)
    { 
        q = p;
        p = p -> next;
        free(q);
    }
    st -> pHead = NULL;
    st -> num = 0;
    return 0;
}    

linkstack_main.c

#include "linkstack.h"
#include 
int main(void)
{
    LinkStack st;
    LStack_init(&st,10);
    register int i = 1;
    for(; i <= 10; i++)
        LStack_push(&st,i);
    if(-1 == LStack_push(&st,1024))
        fprintf(stderr,"满栈,插入失败\n");
    while(!LStack_isempty(&st))
    {
        DATA data = 0;
        LStack_pop(&st,&data);
        printf("%4d",data);
    }
    printf("\n");
    LStack_free(&st);
    return 0;
}

队列

基本概念

队列是最常见的概念,日常生活经常需要排队,仔细观察队列会发现,队列是一种逻辑结构,是一种特殊的线性表。特殊在:

  • 只能在固定的两端操作线性表

只要满足上述条件,那么这种特殊的线性表就会呈现一种“先进先出”的逻辑,这种逻辑就被称为队列。

由于约定了只能在线性表固定的两端进行操作,于是给队列这种特殊的线性表的插入删除,起个特殊的名称:

  • 队头:可以删除节点的一端

  • 队尾:可以插入节点的一端

  • 入队:将节点插入到队尾之后,函数名通常为enQueue()

  • 出队:将队头节点从队列中剔除,函数名通常为outQueue()

  • 取队头:取得队头元素,但不出队,函数名通常为front()

顺序存储的队列:循环队列

与其他的逻辑结构类似,队列可以采用顺序存储形成循环队列,也可以采用链式存储形成链式队列。顺序存储的队列之所以被称为循环队列,是因为可以利用更新队头队尾的下标信息,来循环地利用整个数组,出队入队时也不必移动当中的数据。

从上述动图中可以观察到,需要牺牲至少数组中的一个存储位置,来区分循环队列中的满队和空队。
满队和空队的约定如下:

  • 当front与rear相等时,队列为空
  • 当rear循环加一与front相等时,队列为满

与其他数据结构一样,管理循环队列除了需要一块连续的内存之外,还需要记录队列的总容量、当前队列的元素个数、当前队头、队尾元素位置,如果有多线程还需要配互斥锁和信号量等信息,为了便于管理,通常将这些信息统一于在一个管理结构体之中:

typedef int DATA;
typedef struct
{
    DATA *pData; // 队列入口
    int size; // 队列总容量
    int head; // 队列队头元素下标
    int tail; // 队列队尾元素下标
}SQueue;
  • 循环队列的基本操作

squeue.h

#ifndef __SQUEUE_H
#define __SQUEUE_H
typedef int DATA;
typedef struct
{
DATA *pData; // 队列入口
int size; // 队列总容量
int head; // 队列队头元素下标
int tail; // 队列队尾元素下标
}SQueue;
// 初始化队列
int SQ_init(SQueue *q, int num);
// 判断队列是否已满
int SQ_isfull(SQueue *q);
// 判断队列是否为空
int SQ_isempty(SQueue *q);
// 入队
int SQ_push(SQueue *q,DATA data);
// 出队
int SQ_pop(SQueue *q,DATA *data);
// 回收队
int SQ_free(SQueue *q);
#endif

squeue.c

#include 
#include "squeue.h"
// 初始化队列
int SQ_init(SQueue* q,int num)
{
    q -> pData = (DATA*)calloc(sizeof(DATA),num);
    if(q -> pData == NULL)
        return -1;
    q -> size = num ;
    q -> head = q -> tail = 0;
    return 0;
}
// 判断队列是否已满
int SQ_isfull(SQueue *q)
{
    return (q -> tail + 1) % q -> size == q -> head;
}
// 判断队列是否为空
int SQ_isempty(SQueue *q)
{
    return q -> tail == q -> head;
}
// 出队
int SQ_push(SQueue *st,DATA data)
{
    if(SQ_isfull(st))
        return -1;
    st -> pData[st -> tail] = data;
    st -> tail = (st -> tail+1) % st -> size;
    return 0;
}
// 入队
int SQ_pop(SQueue *st,DATA *data)
{
    if(SQ_isempty(st))
        return -1;
    *data = st -> pData[st -> head];
    st -> head = (st -> head+1) % st -> size;
    return 0;
}
// 回收队列
int SQ_free(SQueue *st)
{
    if(st -> pData)
    {
        free(st->pData);
        st -> pData = NULL;
    }
    st -> head = st -> tail = 0;
}

链式存储的队列:链式队列

链式队列的组织形式与链表无异,只不过插入删除被约束在固定的两端。为了便于操作,通常也会创建所谓管理结构体,用来存储队头指针、队尾指针、队列元素个数等信息:

以下是队列链表节点设计和管理结构体设计的示例代码:

typedef int DATA;
// 链式队列节点
typedef struct _node
{
    DATA data;
    struct _node *next;
}NODE/*,*PNODE*/;
// 链式队列管理结构体
typedef struct
{
    NODE *pHead; // 队头指针
    NODE *pTail; // 队尾指针
    int size ; // 队列当前元素个数
    int num ;
}LinkQueue;
  • 链式队列的基本操作

linkQueue.h

#ifndef __LINKQUEUE_H
#define __LINKQUEUE_H
typedef int DATA;
// 链式队列节点
typedef struct _node
{
    DATA data;
    struct _node *next;
}NODE/*,*PNODE*/;
// 链式队列管理结构体
typedef struct
{
    NODE *pHead; // 队头指针
    NODE *pTail; // 队尾指针
    int size ; // 队列当前元素个数
    int num ;
}LinkQueue;
void LQ_init(LinkQueue *q,int sz);
int LQ_isfull(LinkQueue *q);
int LQ_isempty(LinkQueue *q);
int LQ_push(LinkQueue *q,DATA data);
int LQ_pop(LinkQueue *q,DATA *data);
int LQ_free(LinkQueue *q);
#endif

linkQueue.c

#include "LinkQueue.h"
#include 
void LQ_init(LinkQueue *q,int sz)
{
    q -> pHead = q -> pTail = NULL;
    q -> size = sz;
    q -> num = 0;
}
int LQ_isfull(LinkQueue *q)
{
    return q -> num == q -> size;
}
int LQ_isempty(LinkQueue *q)
{
    return q -> num == 0;
}
int LQ_push(LinkQueue *q,DATA data)
{
    if(LQ_isfull(q))
        return -1;
    NODE* pNew = (NODE*)malloc(sizeof(NODE));
    if(!pNew)
        return -1;
    pNew -> data = data;
    pNew -> next = NULL;
    if(!(q -> pTail))
        q -> pHead = q -> pTail = pNew;
    else
    {
        q -> pTail -> next = pNew;
        q -> pTail = pNew;
    }
    q -> num ++;
    return 0;
}
int LQ_pop(LinkQueue *q,DATA *data)
{
    if(LQ_isempty(q))
        return -1;
    NODE* p = q -> pHead;
    *data = p -> data;
    if(p == q -> pTail)
        q -> pHead = q -> pTail = NULL;
    else
        q -> pHead = p -> next;
    free(p);
    q -> num --;
    return 0;
}
int LQ_free(LinkQueue *queue)
{
    NODE* p = queue -> pHead, *q = NULL;
    while(p)
    {
        q = p;
        p = p -> next;
        free(q);
    }
    queue -> pHead = queue -> pTail = NULL;
    queue -> num = 0;
    return 0;
}

linkQueue_main.c

#include "LinkQueue.h"
#include 
int main(void)
{
    LinkQueue queue;
    LQ_init(&queue,10);
    register int i = 1;
    for(; i <= 10 ; i++)
        LQ_push(&queue, i);
    if( -1 == LQ_push(&queue,1024))
        fprintf(stderr,"满队,入队失败!\n");
    while(!LQ_isempty(&queue))
    {
        DATA data;
        LQ_pop(&queue,&data);
        printf("%4d",data);
    }
    printf("\n");
    LQ_free(&queue);
    return 0;
}    

你可能感兴趣的:(数据结构,c语言,开发语言,学习)