栈和队列的基本知识

一.栈

1.栈的定义

栈(Stack)又称堆栈,仅允许在表的一端进行插入和删除运算。允许插入和删除的一端称为“栈顶”,另一端叫“栈底”。

具有后进后出的特性。

2.栈的顺序存储结构

1.顺序栈

顺序栈的类型描述:

#define MAXSIZE  //栈的最大元素数
typedef int ELemType;
typedef struct 
{
    ELemType elem[MAXSIZE];
    int top;
}SepStack;
SepStack *s;  //s为指向栈顶的指针

通常将0下标设为栈底,空栈时是s->top = -1,入栈s->top++,出栈s->top--。

2.基本操作:

(1)置空栈

SeqStack *InitStack()
{   SeqStack *s;
    s->top = -1;
    return s;
}

(2)判空栈

int Empty(SeqStack *s)
{
    if(s->top == -1) return 1;
    else return 0;
}

(3)入栈

int Push(SeqStack *s,ELemType x)
{
    if(s->top == MAXSIZE-1) return 0;    //栈满不能入栈
    else{   s->top++;
            s->elem[s->top] = x;
            return 1;
    }
}

(4)出栈

int pop(SeqStack *s,ELemType  *x)
{
    if(Empty(s)) return 0;   //栈空不能出栈
    else {  *x=s->elem[s->top];
            s->top--;
            return 1;
    }
}

(5)取栈顶元素

ElemType GetTop(SeqStack *s)
{
    if(Empty(s)) return 0;
    else return (s->elem[s->top]);
}

3.栈的链式存储结构

在链栈中,栈底总是链表的最后一个结点,栈顶是链表的第一个结点,链表的表头指针top就作为栈顶指针,top始终指向当前栈顶元素前面的头结点,即 top>next 为栈顶元素,当 top>next == NULL,则代表栈空。

typedef struct Stacknode
{
    ELemType data;
    struct Stacknode *next;
}slStacktype;

(1)入栈操作

int PushLstack(slStacktype *top,int x)
{
    slStacktype *p;  //申请一个结点
    if((p=(slStacktype*)malloc(sizeof(slStacktype)))==NULL)
        return FALSE;
    p->data = x;
    p->next = top->next;
    top->next = p;
    return p;
}

(2)出栈操作

int PopLstack(slStacktype *top)
{
    slStacktype *p;
    int x;
    if(top->next == NULL) 
        return ;   //此时栈为空
    p = top->next;
    top->next = top->next->next;
    x = p->data;
    free(p);
    return x;
}

(3)多个链表的操作

涉及多个链栈的操作,可以将这些链栈的栈顶指针放在一个一维数组中。

即 设 slStacktype *top[M];

多个链表的入栈、出栈操作,只需将上面代码的 top 改为 top[i] 。

4.例题

1.有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

1.左括号必须用相同类型的右括号闭合。 2.左括号必须以正确的顺序闭合。

输入:s = "([)]" 输出:false

输入:s = "()[]{}" 输出:true

输入:s = "(]" 输出:false

char pairs(char a)
{
    if(a == '(') return ')';
    if(a == '{') return '}';
    if(a == '[') return ']';
    return 0;
}
bool isValid(char *s)
{
    int n=strlen(s);
    if(n%2 == 1)
        return FALSE;
    int stk[n],top=0;
    for(int i=0;i<n;i++)
    {
        char ch=pairs(s[i]);
        if(ch)
        {
            if(top == 0 && stk[top-1] != ch )
                return FALSE;
            top--; 
        }
        else {
            stk[top++] = s[i]; 
        }
    }
    return top == 0;   //如果top为0,结果为true,否则为false,即不配对
}

 

队列

1.概念

队列只允许插入在表的一端进行,而删除在表的另一端进行,允许插入的叫队尾,允许删除的叫队头。满足先入先出

基本操作:

  • 队列的初始化:InitQueue(q)

  • 入队操作:InQueue(q,x)

    初始条件:队列q已存在。

    结果:对于存在的队列q,插入一个元素x到队尾。操作成功,返回值为TRUE,否则为false。

  • 出队操作:OutQueue(q,x)

    初始条件:队列q存在且非空。

    操作结果:删除队首元素,并返回其值,队列不变。

    C语言中还约定,初始化队列时,空队列 front = rear = -1,当插入新元素时,尾指示针rear加一,队头元素出队时,头指示针加一。

2.循环队列

为避免假溢出,将队列看成首尾相连的循环结构,即规定最后一个单元的后继为第一个单元。但头尾指针的关系不变,即头指针front总是指向队列中实际队头元素的前面一个位置,而队尾指示器rear总是指向队尾元素。

头尾相接循环表的构造方法可以利用数学上的求余运算

入队时,队尾指针加一操作修改为:sq->rear = (sq->rear+1) % MAXSIZE

出队时,队头指针加一操作修改为:sq->front = (sq->front+1) % MAXSIZE

此时,队满和队空的条件是一样的,即:rear == front 。

解决方法一:少用一个空间,即:(rear + 1) % MAXSIZE == front .

解决方法二:统计队列元素的个数(num),num == 0,队列为空,num == MAXSIZE,则队满。

用法一解决,循环队列的类型定义及基本运算:

typedef struct
{
    ElemType elem[MAXSIZE];
    int front,rear;
}CSeQueue;

(1)置空队

CSeQueue * InitQueue()
{
    CSeQueue *q;
    q = (CSeQueue*)malloc(sizeof(CSeQueue));
    q->front = q->rear = MAXSIZE-1;
    return q;
}

(2)入队

bool InSeQueue(CSeQueue *q,int x)
{
    if(( q->rear+1 ) % MAXSIZE == q->front )
    {
        printf("队满");
        return FALSE;
    }
    else
    {
        q->rear = (q->rear+1) % MAXSIZE;
        q->elem[q->rear] = x;
        return TRUE;
    }
}

(3)出队

bool OutQueue(CSeQueue *q,int *x)
{
    if(q->rear == q->front)
    {
        printf("队空");
        return FALSE;
    }
    else
    {
        q->front = (q->front+1) %MAXSIZE;
        *x = q->elem[q->front]; //front永远指向队首元素的前一个
        return TRUE;
    }
}

(4)判队空

bool EmptyQueue(CSeQueue *q)
{
    if(q->front == q->rear) return TRUE;
    else return FALSE;
}

     

3.链队列

如果不知道队列所需的最大空间,可以采用链式结构来存储,又叫做“链队列”。

链队列的数据结构类型描述:

typedef struct node
{
    int data;
    struct node * next;
}Qnode;
typedef struct
{
    Qnode * front;
    Qnode * rear;
}LQueue;
  • 创建一个带头节点的空队

LQueue *InitQueue()
{
    Qnode *p;
    LQueue *q;
    q = (LQueue*)malloc(sizeof(LQueue));
    p = (Qnode*)malloc(sizeof(Qnode));
    p->next = NULL;
    q->front = q->next = p;
    return q;
}
  • 入队

void inQueue(LQueue *q,int x)
{
    Qnode *p;
    p = (Qnode*)malloc(sizeof(Qnode));
    p->data = x;
    p->next = NULL;
    q->rear->next = p;
    q->rear = p;
}
  • 判队空

int Empty_LQueue(LQueue *q)
{
    if(q->rear == q->front) return TRUE;
    else return FALSE;
}
  • 出队

int Out_LQueue(LQueue *q,int *x)
{
    Qnode *p;
    if(Empty_LQueue(q)) 
    {
        printf("队空");  return FALSE;
    }
    else
    {
        p = q->front->next;
        q->front->next = p->next;
        *x=p->data;
        free(p);
        if(q->front->next == NULL) //只有一个元素时,出队后队空,修改队尾指针。
            q->front = q->rear;
        return TRUE;
    }
}

                                                                                  (部分参考于《数据结构与算法》by 王曙燕)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值