栈的定义

  • 栈是一种后进先出(Last in first out,LIFO)的线性表,只能在表尾进行插入和删除操作

栈的插入和删除操作

  • 站的插入操作(push),叫做进栈,也称为压栈,入栈

  • 栈的删除操作(pop),叫做出栈,也叫做弹栈

栈的顺序存储结构

  • 因为栈的本质是一个线性表,线性表有两种存储形式,那么栈也分为栈的顺序存储结构和栈的链式存储结构

  • 最开始栈中不含有任何数据,叫做空栈,此时栈顶就是栈底.然后数据从栈顶进入,栈顶栈底分离,整个栈的当前容量变大.数据出栈时从栈顶弹出,栈顶下移,整个栈的当前容量变小

栈的顺序存储结构.jpg

栈的顺序存储结构

typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;
  • 这里定义了一个顺序存储的栈,它包含了三个元素:base,top,stackSize.其中base是指向栈底的指针变量,top是指向栈顶的指针变量,stsck
  • Size指示栈的当前可使用的最大容量

创建一个栈

#define STACK_INIT_SIZE 100
void initStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if(!s->base)
        exit(0);
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

入栈操作

  • 入栈操作要在栈顶进行,每次向栈中压入一个数据,top指针就要+1,指导栈满为止
#define STACKINCREMENT 10

void push(sqStack *s,ElemType e)
{
    //如果栈满,追加空间
    if(s->top - s->base >= s->stackSize)
    {
        s->base = (ElemType *)realloc(s->base,(s->stackSize + STACKINCREMENT) * 
            sizeof(ElemType));
        if(!s->base)
            exit(0);
        s->top = s->base + s->stackSize;//设置栈顶
        s->stackSize = s->stackSize + STACKINCREMENT;//设置栈的最大容量
    }
    *(s->top) = e;
    s->top++;
}

出栈操作

  • 出栈操作就是在栈顶取出数据,栈顶指针随之下移的操作

  • 每当从栈内弹出一个数据,栈的当前容量就-1

void pop(sqStack *s,ElemType *e)
{
    //如果栈为空返回
    if(s->top == s->base)
        return;
    *e = *--(s->top);
}
  • 清空一个栈
void ClearStack(sqStack *s)
{
    s->top = s->base;
}
  • 销毁一个栈
void DestoryStack(sqStack *s)
{
    int i,len;
    len = s->stackSize;
    for(i=0;i<len;i++)
    {
        free(s->base);
        s->base++;
    }
    s->base = s->top = NULL;
    s->stackSize = 0;
}
  • 当前容量
int StackLen(sqStack s)
{
    return (s.top - s.base);
}

二进制转10进制

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10

typedef char ElemType;

typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

void initStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if(!s->base)
        exit(0);
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

void push(sqStack *s,ElemType e)
{
    //如果栈满,追加空间
    if(s->top - s->base >= s->stackSize)
    {
        s->base = (ElemType *)realloc(s->base,(s->stackSize + STACKINCREMENT) *
            sizeof(ElemType));
        if(!s->base)
            exit(0);
        s->top = s->base + s->stackSize;//设置栈顶
        s->stackSize = s->stackSize + STACKINCREMENT;//设置栈的最大容量
    }
    *(s->top) = e;
    s->top++;
}

void pop(sqStack *s,ElemType *e)
{
    //如果栈为空返回
    if(s->top == s->base)
        return;
    *e = *--(s->top);
}

void ClearStack(sqStack *s)
{
    s->top = s->base;
}

void DestoryStack(sqStack *s)
{
    int i,len;
    len = s->stackSize;
    for(i=0;i<len;i++)
    {
        free(s->base);
        s->base++;
    }
    s->base = s->top = NULL;
    s->stackSize = 0;
}

int StackLen(sqStack s)
{
    return (s.top - s.base);
}

int main()
{
    ElemType c;
    sqStack s;
    int len,i,sum = 0;

    initStack(&s);

    printf("请输入二进制数,输入#号表示结束!\n");
    scanf("%c",&c);
    while(c!='#')
    {
        push(&s,c);
        scanf("%c",&c);
    }
    //把回车从缓冲区去掉
    getchar();

    len = StackLen(s);
    printf("栈的容量为:%d\n",len);

    for(i=0;i<len;i++)
    {
        pop(&s,&c);
        sum = sum + (c-48)*pow(2,i);
    }
    printf("%d\n",sum);
    return 0;
}

括号匹配

二进制转八进制

  • 二进制与十六进制对应关系
0            0
1            1
10           2
11           3
100          4
101          5
110          6
111          7
1000         8
1001         9
1010         A
1011         B
1100         C
1101         D
1110         E
1111         F
  • 一个字节(8bit)刚好用两个十六进制数可以表示完整,大大街上了显示空间

QQ截图20161229100808.png

QQ截图20161229100825.png

  • 先把每三位取出来,转换成8进制压入另一个栈,再从另外一个栈弹出

栈的链式储结构

QQ截图20161229101641.png

typedef struct StackNode
{
    ElemType data;
    struct StackNode *next;
}StackNode,LinkStackPtr;

typedef struct LinkStack
{
    //top指针
    LinkStack top;
    //栈元素计数器
    int count;
}
  • 进栈操作
Status push(LinkStack *s,ElemType e)
{
    LinkStackPtr p = (LinkStackPtr)malloc
    (sizeof(StackNode));
    p->data = e;
    p->next = s->top;
    s->top = p;
    s->count++;
    return OK;
}
  • 出栈操作
Ststus pop(LinkStack *s,ElemType *e)
{
    LinkStackPtr p;
    if(StackEmpty(*s))
        return ERROR;
    *e = s->top->data;
    p = s->top;
    s->top = s->top->next;
    free(p);
    s->count--;
    return OK;
}

逆波兰表达式

  • 20世纪三十年代,波兰逻辑学家Jan.Lukasiewicz发明了一种不需要括号的后缀表达式,我们通常把它称为逆波兰表达式(RPN)

  • 对于(1-2)(4+5),如果用逆波兰表示发,应该是这样:1 2 - 4 5 +

  • 数字1和2进栈,遇到减号运算符则弹出两个元素进行运算,并把结果入栈

  • 4和5入栈,遇到加号运算符,4和5弹出栈,相加后结果9入栈

  • 然后又遇到乘法运算符,将9和-1弹出栈进行乘法计算,此时栈空并无数据压栈,-9为最终结果

逆波兰表达式计算器

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define MAXBUFFER 10

typedef double ElemType;

typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

void initStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if(!s->base)
        exit(0);
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

void push(sqStack *s,ElemType e)
{
    //如果栈满,追加空间
    if(s->top - s->base >= s->stackSize)
    {
        s->base = (ElemType *)realloc(s->base,(s->stackSize + STACKINCREMENT)
         * sizeof(ElemType));
        if(!s->base)
            exit(0);
        s->top = s->base + s->stackSize;//设置栈顶
        s->stackSize = s->stackSize + STACKINCREMENT;//设置栈的最大容量
    }
    *(s->top) = e;
    s->top++;
}

void pop(sqStack *s,ElemType *e)
{
    //如果栈为空返回
    if(s->top == s->base)
        return;
    *e = *--(s->top);
}

int main()
{
    sqStack s;
    char c;
    double d,e;
    int i = 0;
    char str[MAXBUFFER];

    initStack(&s);

    printf("请按逆波兰表达式输入待计算数据,数据与运算符之间用空格隔开,以#作为结束标志!\n");
    scanf("%c",&c);

    while(c!='#')
    {
        while(isdigit(c)||c=='.')
        {
            str[i++] = c;
            str[i] = '\n';
            if(i>=10)
            {
                printf("出错:输入的单个数据过大!\n");
                return -1;
            }
            //过滤空格
            scanf("%c",&c);
            if(c==' ')
            {
                d = atof(str);
                push(&s,d);
                i = 0;
                break;
            }
        }
        switch(c)
        {
            case '+':
                pop(&s,&e);
                pop(&s,&d);
                push(&s,d+e);
                break;
            case '-':
                //下面减上面
                pop(&s,&e);
                pop(&s,&d);
                push(&s,d-e);
                break;
            case '*':
                pop(&s,&e);
                pop(&s,&d);
                push(&s,d*e);
                break;
            case '/':
                pop(&s,&e);
                pop(&s,&d);
                if(e!=0)
                {
                    push(&s,d/e);
                }
                else
                {
                    printf("\n出错:除0!\n");
                }
                break;

        }
        scanf("%c",&c);
    }
    pop(&s,&d);
    printf("最终的计算结果为:%lf",d);
    return 0;
}

中缀表达式转换为后缀表达式

  • 从左到右遍历中缀表达式的每个数字和符号,若是数字则直接输出,若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号,则栈顶元素一次出栈并输出,直到遇到左括号或栈空才将那个符号入栈
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define MAXBUFFER 10

typedef char ElemType;

typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

void initStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if(!s->base)
        exit(0);
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

void push(sqStack *s,ElemType e)
{
    //如果栈满,追加空间
    if(s->top - s->base >= s->stackSize)
    {
        s->base = (ElemType *)realloc(s->base,(s->stackSize + STACKINCREMENT) * sizeof(ElemType));
        if(!s->base)
            exit(0);
        s->top = s->base + s->stackSize;//设置栈顶
        s->stackSize = s->stackSize + STACKINCREMENT;//设置栈的最大容量
    }
    *(s->top) = e;
    s->top++;
}

void pop(sqStack *s,ElemType *e)
{
    //如果栈为空返回
    if(s->top == s->base)
        return;
    *e = *--(s->top);
}

int StackLen(sqStack s)
{
    return (s.top - s.base);
}

int main()
{
    sqStack s;
    char c,e;

    initStack(&s);

    printf("请输入中缀表达式,以#作为结束标志:\n");
    scanf("%c",&c);

    while(c!='#')
    {
        while(c>='0'&&c<='9')
        {
            printf("%c",c);
            scanf("%c",&c);
            if(c<'0'||c>'9')
            {
                printf(" ");
            }
        }
        if(')' == c)
        {
            pop(&s,&e);
            while('('!=e)
            {
                printf("%c ",e);
                pop(&s,&e);
            }
        }
        else if('+'==c || '-'==c)
        {
            if(!StackLen(s))
            {
                push(&s,c);
            }
            else
            {
                do
                {
                    pop(&s,&e);
                    if('('==e)
                    {
                        push(&s,e);
                    }
                    else
                    {
                        printf("%c ",e);
                    }
                }while(StackLen(s)&&'('!=e);
                push(&s,c);
            }
        }
        else if('*'==c||'/'==c||'('==c)
        {
            push(&s,c);
        }
        else if('#'==c)
        {
            break;
        }
        else
        {
            printf("\n输入错误!\n");
            return -1;
        }
        scanf("%c",&c);
    }

    while(StackLen(s))
    {
        pop(&s,&e);
        printf("%c ",e);
    }
    return 0;
}

队列

什么是队列

  • 队列(queue)是只允许在一段进行插入操作,而在另一端进行删除操作的线性表

  • 与栈相反,队列是一种先进先出(First In First Out,FIFO)的线性表

  • 与栈相同的是,队列也是一种重要的线性结构,实现一个队列同样需要顺序表或链表作为基础

  • 队列既可以用链表实现,也可以用顺序表实现.跟栈相反的是,栈一般用顺序表来实现,二队列常4用链表来实现,简称为链队列

队列的链式存储结构

typedef struct QNode
{
    ElemType data;
    struct QNode *next;
}QNode,*queuePrt;

typedef struct
{
    //队头,尾指针
    queuePrt front,rear;
}LinkQueue;
  • 我们将头指针指向队列的头结点,而队尾指针指向终端节点.(注:头结点不是必须的,但为了方便操作,我们加上了)

QQ截图20161229232341.jpg

  • 空队列时,front和rear都指向头结点

创建一个队列

void initQueue(LinkQueue *q)
{
    q->front = q->rear = (queuePrt)malloc(sizeof(QNode));
    if(!q->front)
        exit(0);
    q->front->next=NULL;
}

入队列操作

void InsertQueue(LinkQueue *q,ElemType)
{
    p = (queuePrt)malloc(sizeof(QNode));
    if(p==NULL)
        exit(0);
    p->data = e;
    p->next = NULL;
    q->rear->next = p;
    q->rear = p;
}

出队列操作

  • 出队列操作是将队列中的第一个元素移除,队头指针不发生改变,改变头结点的next指针即可
void DeleteQueue(LinkQueue *q,ElemType *e)
{
    queuePrt p;
    if(q->front == q->rear)
        return;
    p = q->front->next;
    *e = p->data;
    q->front->next = p->next;
    if(q->rear==p)
        q->rear = q->front;
    free(p);
}

销毁一个队列

  • 由于连队列建立在内存的动态区,因此当一个队列不在有用时应当把它及时销毁掉,以免过多的占用空间
void DestoryQueue(LinkQueue *q)
{
    while(q->front)
    {
        q->rear = q->front->next;
        free(q->front);
        q->front = q->rear;
    }
}

队列的顺序存储结构

  • 队列顺序存储会出现对头有空余而队尾溢出的情况,这种情况叫做假溢出.

QQ截图20161230095302.png

循环队列的定义

  • 要解决假溢出的办法就是如果后面满了,就再从头开始,也就是头尾相接的循环

  • 循环队列它的容量是固定的,并且它的队头和队尾指针都可以随着元素出入队列而发生改变,这样循环队列逻辑上就好像是一个环形的存储空间

  • 循环队列的实现只需要灵活改变front和rear指针即可

  • 也就是让front或rear指针不断加1,即使超出了地址范围,也会自动从头开始,我们可以采取取模运算处理

    • (rear+1) % QueueSize

    • (front+1) % QueueSize

  • 定义一个循环队列

#define MAXSIZE 100

typedef struct
{
    ElemType *base;
    int front;
    int rear;
}
  • 初始化一个循环队列
void initQueue(cycleQueue *q)
{
    q->base = (ElemType *)malloc(MAXSIZE * sizeof(ElemType));
    if(!q->base)
        exit(0);
    q->front = q->rear = 0;
}
  • 如队列操作
void InsertQueue(cycleQueue *q,ElemType e)
{
    if((q->rear+1)%MAXSIZE == q->front)
        return;
    q->base[q->rear] = e;
    q->rear = (q->rear+1)%MAXSIZE;
}

出队列操作

void DeleteQueue(cycleQueue *q,ElemType *e)
{
    if(q->front == q->rear)
        return;
    *e = q->base[q->front];
    q->front = (q->front+1)%MAXSIZE;
}