数据结构ppt4自研练习

文章详细介绍了栈和队列在C++中的实现,包括顺序栈和链式栈,以及它们的创建、销毁、判空判满、压栈弹栈等操作。同时,讨论了栈的扩容策略和进制转换问题,以及括号匹配和表达式求值的应用。此外,还提到了浏览器前进/后退、递归与回溯、栈混洗和队列在单调栈和单调队列中的应用。
摘要由CSDN通过智能技术生成


流光不争先,争滔滔不绝。很喜欢教授的这句话

慕课自学内容

堆栈的定义和主要操作(C++)

在这里插入图片描述
在这里插入图片描述

参考还是觉得直接定义固长
数组更方便,下面是动态分配的数组,创建和释放都很麻烦。
运算符优先级查表
查++stack->top是否含义正确
在子函数中malloc和free方法
运行环境在codeblocks支持C89/90不支持99并且很简洁。

定义:LIFO

空间分配

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(操作系统)回收,分配方式倒是类似于链表。

缓存方式

栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
数据结构区别
堆(数据结构):堆可以被看成是一棵树,如:堆排序。
栈(数据结构):一种先进后出的数据结构。

主要操作

存储
#define  maxstacksize 100
typedef struct stack{
    int stackarray[maxstacksize];
    int top;
}stack;
typedef struct slnode{
    int data;
    struct slnode*next;
}slnode;
//为了防止传参需要传指针的指针的情况我结合之前的学习,设计了lstack,
//这样直接传lstack的指针,就可以用类似C++传引用的方式来写C语言的函数了,
//是一种简单过程和函数过程和谐的设计
typedef struct lstack{
    slnode*top;
}lstack;
对应C++的创建stack对象和析构stack对象
初始化
void createStack(stack*st){
    st->top=-1;
}
void createStack(lstack*lst){
    lst->top=NULL;
}
销毁栈
void destoryStack(stack*st){
    st->top=-1;
}
//清空栈
void destorylstack(lstack*lst){
    slnode*temp=NULL;
    while(!isempty(lst)){temp=lst->top->next;free(lst->top);lst->top=temp;}
}
判空和判满操作,和C++不同需要先定义
判空
int isempty(stack*st){
    return st->top==-1;
}

//判断栈是否为空
int isempty(lstack*lst){
    return lst->top==NULL;
}
判满
int isfull(stack*st){
    return st->top==maxstacksize-1;
}
压栈和弹栈操作,需要考虑栈的状态
进栈
int push(stack*st,int *item){
    if(isfull(st))return -1;
    st->stackarray[++st->top]=*item;
}
//压栈和弹栈,压栈的逻辑c++是直接构造了slnode,C需要先构建节点再移动
int push(lstack*lst,int*item){
    slnode*p=(slnode*)malloc(sizeof(slnode));
    p->data=*item;
    p->next=lst->top;
    lst->top=p;
    return 1;
}
出栈
int pop(stack*st,int*item){
    if(isempty(st))return -1;
    *item=st->stackarray[st->top--];
}
//压栈和弹栈,压栈的逻辑c++是直接构造了slnode,C需要先构建节点再移动
int push(lstack*lst,int*item){
    slnode*p=(slnode*)malloc(sizeof(slnode));
    p->data=*item;
    p->next=lst->top;
    lst->top=p;
    return 1;
}
//拓展:遍历栈,不要用top–,传的是地址,会改变内容的,不像参考资料传的是值,之前的操作传引用和传指针是一个样子的
读栈顶元素
int pop(stack*st,int*item){
    if(isempty(st))return -1;
    *item=st->stackarray[st->top--];
}
int peek(lstack*lst,int*item){
    if(isempty(lst))return -1;
    *item=lst->top->data;
    return 1;
}
遍历栈
void printStack(stack*st){
    int temp=st->top;
    while(temp!=-1){
        printf("%d ",st->stackarray[temp--]);
    }
    printf("\n");
}
//遍历栈
void printStack(lstack*lst){
    slnode*temp=lst->top;
    while(temp){printf("%d ",temp->data);temp=temp->next;}
}

顺序栈

#include<stdio.h>
#include<stdlib.h>
#define  maxstacksize 100
typedef struct stack{
    int stackarray[maxstacksize];
    int top;
}stack;
//对应C++的创建stack对象和析构stack对象
void createStack(stack*st){
    st->top=-1;
}
void destoryStack(stack*st){
    st->top=-1;
}
//判空和判满操作,和C++不同需要先定义
int isempty(stack*st){
    return st->top==-1;
}
int isfull(stack*st){
    return st->top==maxstacksize-1;
}

//压栈和弹栈操作,需要考虑栈的状态
int push(stack*st,int *item){
    if(isfull(st))return -1;
    st->stackarray[++st->top]=*item;
    return 1;
}
int pop(stack*st,int*item){
    if(isempty(st))return -1;
    *item=st->stackarray[st->top--];
    return 1;
}
//读栈顶
void peek(stack*st,int*item){
    *item=st->stackarray[st->top];
}
//拓展:遍历栈,不要用top--,传的是地址,会改变内容的,不像参考资料传的是值,之前的操作传引用和传指针是一个样子的
void printStack(stack*st){
    int temp=st->top;
    while(temp!=-1){
        printf("%d ",st->stackarray[temp--]);
    }
    printf("\n");
}
int main(){
    stack*st=(stack*)malloc(sizeof(stack));
    createStack(st);
    int item=1;
    push(st,&item);
    item=2;
    push(st,&item);
    item=3;
    push(st,&item);
    item=4;
    push(st,&item);
    item=5;
    push(st,&item);
    peek(st,&item);
    printf("栈顶元素为%d",item);
    printf("出战顺序为:\n");
    printStack(st);
    pop(st,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素:");
    printStack(st);
    pop(st,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素");
    printStack(st);
    if(!isempty(st)){
       printf("当前栈不为空");
    }else{
         printf("当前栈为空");
    }
    return 0;
}

链式栈

#include<stdio.h>
#include<stdlib.h>
typedef struct slnode{
    int data;
    struct slnode*next;
}slnode;
//为了防止传参需要传指针的指针的情况我结合之前的学习,设计了lstack,
//这样直接传lstack的指针,就可以用类似C++传引用的方式来写C语言的函数了,
//是一种简单过程和函数过程和谐的设计
typedef struct lstack{
    slnode*top;
}lstack;
//判断栈是否为空
int isempty(lstack*lst){
    return lst->top==NULL;
}
//按照C++对象的构造和析构函数逻辑顺序
//创建栈
void createStack(lstack*lst){
    lst->top=NULL;
}
//清空栈
void destorylstack(lstack*lst){
    slnode*temp=NULL;
    while(!isempty(lst)){temp=lst->top->next;free(lst->top);lst->top=temp;}
}
//压栈和弹栈,压栈的逻辑c++是直接构造了slnode,C需要先构建节点再移动
int push(lstack*lst,int*item){
    slnode*p=(slnode*)malloc(sizeof(slnode));
    p->data=*item;
    p->next=lst->top;
    lst->top=p;
    return 1;
}
int pop(lstack*lst,int*item){
    if(isempty(lst))return -1;
    *item=lst->top->data;
    slnode*temp=lst->top;
    lst->top=lst->top->next;
    free(temp);
    return 1;
}

int peek(lstack*lst,int*item){
    if(isempty(lst))return -1;
    *item=lst->top->data;
    return 1;
}
//遍历栈
void printStack(lstack*lst){
    slnode*temp=lst->top;
    while(temp){printf("%d ",temp->data);temp=temp->next;}
}
int main(){
    lstack*lst=(lstack*)malloc(sizeof(lstack));
    createStack(lst);
    int item=1;
    push(lst,&item);
    item=2;
    push(lst,&item);
    item=3;
    push(lst,&item);
    item=4;
    push(lst,&item);
    item=5;
    push(lst,&item);
    peek(lst,&item);
    printf("栈顶元素为%d",item);
    printf("出战顺序为:\n");
    printStack(lst);
    pop(lst,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素:");
    printStack(lst);
    pop(lst,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素");
    printStack(lst);
    if(!isempty(lst)){
       printf("当前栈不为空\n");
    }else{
         printf("当前栈为空\n");
    }
    destorylstack(lst);
     if(!isempty(lst)){
       printf("当前栈不为空\n");
    }else{
         printf("当前栈为空\n");
    }
    free(lst);
    return 0;
}


顺序栈与链式栈的比较

栈的链接表示比顺序数组表示占用空间更多,链式表不需要连续的单元;
多种类似的链式结构和一些别的结构可以共用自由空间中相同的地方,其他链式结构也可以控制指针操作这个栈。

队列的定义和主要操作

参考资料
如果只是数组,rear删除后移存储不够;
后面移动到前面浪费时间;
所以构造循环形式
在这里插入图片描述

队列的定义:FIFO

主要操作

存储
#include<stdio.h>
#include<stdlib.h>
#define MaxQueueSize 100
//方案1
typedef struct aqueue{
    int size;
    int qarray[MaxQueueSize];
    int front,rear,count;
}aqueue;
#include<stdio.h>
#include<stdlib.h>
#define MaxQueueSize 100
//方案1
typedef struct aqueue{
    int size;
    int qarray[MaxQueueSize];
    int front,rear;
}aqueue;
#include<stdio.h>
//方法1
typedef struct qlnode{
    int data;
    struct qlnode*next;
}qlnode;

typedef struct lqueue{
    int count;
    qlnode*rear;
    qlnode*front;
}lqueue;
创建和销毁
创建
//创建
void create(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
    aq->count=0;
}
//创建
void create(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
}
void createlqueque(lqueue*lq){
    lq->count=0;
    lq->front=lq->rear=NULL;
}
销毁
void destoryaqueue(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
    aq->count=0;
}
//销毁
void destoryaqueue(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
}
int isempty(lqueue*lq){
    if(lq->count==0)return 1;
    else return 0;
}
void destorylqueque(lqueue*lq){
    while(isempty(lq)){
        lq->rear=lq->front;
        lq->front=lq->front->next;
        free(lq->rear);
        lq->count--;
    }
    lq->rear=NULL;
}

判空和判满
判空
//队空,队满,队中元素个数
int isempty(aqueue*aq){return aq->count==0;}

//队空,队满,队中元素个数
int isempty(aqueue*aq){return aq->front==aq->rear;}

int isempty(lqueue*lq){
    if(lq->count==0)return 1;
    else return 0;
}
判满
int isfull(aqueue*aq){return aq->count==MaxQueueSize;}
//两种情况r+1=f;r+1=f+max;(r+1)%max=f
int isfull(aqueue*aq){return (aq->rear+1)%aq->size==aq->front;}
入队和出队
入队
//入队
int qinsert(aqueue*aq,int*item){
    if(isfull(aq))return -1;
    aq->qarray[aq->rear]=*item;
    aq->rear=(aq->rear+1)%aq->size;
    aq->count++;
    return 1;
}

//入队
int qinsert(aqueue*aq,int*item){
    if(isfull(aq))return -1;
    aq->qarray[aq->rear]=*item;
    aq->rear=(aq->rear+1)%aq->size;
    return 1;
}


//入队,出队
int qinsert(lqueue*lq,int*item){
    if(isempty(lq)){
        lq->front=lq->rear=(qlnode*)malloc(sizeof(qlnode));
        lq->front->next=NULL;
        lq->front->data=*item;
        lq->count++;
    }
    else{
        lq->rear->next=(qlnode*)malloc(sizeof(qlnode));
        lq->rear=lq->rear->next;
        lq->rear->next=NULL;
        lq->rear->data=*item;
        lq->count++;
    }
    return 1;
}



出队
//出队
int qdelete(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    aq->front=(aq->front+1)%aq->size;//front顺时针移动一格
    aq->count--;
    return 1;
}
//出队
int qdelete(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    aq->front=(aq->front+1)%aq->size;//front顺时针移动一格
    return 1;
}
int qdelete(lqueue*lq,int* item){
    if(isempty(lq))return -1;
    *item=lq->front->data;
    qlnode*temp=lq->front;
    lq->front=lq->front->next;
    free(temp);
    temp=NULL;
    lq->count--;
    if(lq->count==0)lq->rear=0;//尾结点消失
    return 1;
}

查看队头和遍历队列
查看队头
//将队首元素值赋值给变量item
int qfront(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    return 1;
}



//将队首元素值赋值给变量item
int qfront(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    return 1;
}
//存取队首元素
int qfront(lqueue*lq,int*item){
    if(isempty(lq))return -1;
    *item=lq->front->data;
    return 1;
}

遍历队列
//遍历队列
int print(aqueue*aq){
    if(isempty(aq)){printf("队列为空\n");return -1;}
    int temp=aq->front;
    while(temp!=aq->rear){
        printf("%d ",aq->qarray[temp]);
        temp++;
    }
    printf("\n");
    return 1;
}
//遍历队列
int print(aqueue*aq){
    if(isempty(aq)){printf("队列为空\n");return -1;}
    int temp=aq->front;
    while(temp!=aq->rear){
        printf("%d ",aq->qarray[temp]);
        temp++;
    }
    printf("\n");
    return 1;
}
//遍历队列
void printqueue(lqueue*lq){
    qlnode*temp=lq->front;
    while(temp){
        printf("%d ",temp->data);temp=temp->next;
    }
    printf("\n");
}

顺序队列

有两种方案
1.方案一有rear,front,count,用count==0,count==MaxSize来判断队列是否空满,cout是队列长度
2.方案二,有rear,front省掉count,用rear和front,front==rear来判断是否空,(rear+1)%MaxSize==front判断是否满,一共(rear-front+MaxSize)%MaxSize
这个式子:在队列都在数组里时候等价于rear-front;当进入循环之后,相当于
front在后,而rear在前,等价于rear-front+MaxSize;然后统一一下,给上面加上MaxSize再整体取余统一形式。


//两种情况r+1=f;r+1=f+max;(r+1)%max=f
长度
//两种情况r-f=l;r-f+max=l;(r-f+max)%max=l

//方式一:

#include<stdio.h>
#include<stdlib.h>
#define MaxQueueSize 100
//方案1
typedef struct aqueue{
    int size;
    int qarray[MaxQueueSize];
    int front,rear,count;
}aqueue;

//创建
void create(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
    aq->count=0;
}
//销毁
void destoryaqueue(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
    aq->count=0;
}
//队空,队满,队中元素个数
int isempty(aqueue*aq){return aq->count==0;}
int isfull(aqueue*aq){return aq->count==MaxQueueSize;}
//入队
int qinsert(aqueue*aq,int*item){
    if(isfull(aq))return -1;
    aq->qarray[aq->rear]=*item;
    aq->rear=(aq->rear+1)%aq->size;
    aq->count++;
    return 1;
}
//出队
int qdelete(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    aq->front=(aq->front+1)%aq->size;//front顺时针移动一格
    aq->count--;
    return 1;
}
//将队首元素值赋值给变量item
int qfront(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    return 1;
}
//遍历队列
int print(aqueue*aq){
    if(isempty(aq)){printf("队列为空\n");return -1;}
    int temp=aq->front;
    while(temp!=aq->rear){
        printf("%d ",aq->qarray[temp]);
        temp++;
    }
    printf("\n");
    return 1;
}
int main(){
    aqueue*aq=(aqueue*)malloc(sizeof(aqueue));
    create(aq);
    print(aq);
    int item=5;
    qinsert(aq,&item);
    print(aq);
    item=4;
    qinsert(aq,&item);
    print(aq);
    item=3;
    qinsert(aq,&item);
    print(aq);
    item=2;
    qinsert(aq,&item);
    print(aq);
    item=1;
    qinsert(aq,&item);
    print(aq);

    qdelete(aq,&item);
    print(aq);
   qdelete(aq,&item);
    print(aq);
    qdelete(aq,&item);
    print(aq);
    qdelete(aq,&item);
    print(aq);
    qdelete(aq,&item);
    print(aq);
    destoryaqueue(aq);
    print(aq);
    free(aq);
}


//方式二:节省空间

#include<stdio.h>
#include<stdlib.h>
#define MaxQueueSize 100
//方案1
typedef struct aqueue{
    int size;
    int qarray[MaxQueueSize];
    int front,rear;
}aqueue;

//创建
void create(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
}
//销毁
void destoryaqueue(aqueue*aq){
    aq->size=MaxQueueSize;
    aq->front=0;
    aq->rear=0;
}
//队空,队满,队中元素个数
int isempty(aqueue*aq){return aq->front==aq->rear;}
//两种情况r+1=f;r+1=f+max;(r+1)%max=f
int isfull(aqueue*aq){return (aq->rear+1)%aq->size==aq->front;}
//入队
int qinsert(aqueue*aq,int*item){
    if(isfull(aq))return -1;
    aq->qarray[aq->rear]=*item;
    aq->rear=(aq->rear+1)%aq->size;
    return 1;
}
//出队
int qdelete(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    aq->front=(aq->front+1)%aq->size;//front顺时针移动一格
    return 1;
}
//将队首元素值赋值给变量item
int qfront(aqueue*aq,int*item){
    if(isempty(aq))return -1;
    *item=aq->qarray[aq->front];
    return 1;
}
//遍历队列
int print(aqueue*aq){
    if(isempty(aq)){printf("队列为空\n");return -1;}
    int temp=aq->front;
    while(temp!=aq->rear){
        printf("%d ",aq->qarray[temp]);
        temp++;
    }
    printf("\n");
    return 1;
}
int main(){
    aqueue*aq=(aqueue*)malloc(sizeof(aqueue));
    create(aq);
    print(aq);
    int item=5;
    qinsert(aq,&item);
    print(aq);
    item=4;
    qinsert(aq,&item);
    print(aq);
    item=3;
    qinsert(aq,&item);
    print(aq);
    item=2;
    qinsert(aq,&item);
    print(aq);
    item=1;
    qinsert(aq,&item);
    print(aq);

    qdelete(aq,&item);
    print(aq);
   qdelete(aq,&item);
    print(aq);
    qdelete(aq,&item);
    print(aq);
    qdelete(aq,&item);
    print(aq);
    qdelete(aq,&item);
    print(aq);
    destoryaqueue(aq);
    print(aq);
    free(aq);
}

链式队列

#include<stdio.h>
//方法1
typedef struct qlnode{
    int data;
    struct qlnode*next;
}qlnode;

typedef struct lqueue{
    int count;
    qlnode*rear;
    qlnode*front;
}lqueue;

int isempty(lqueue*lq){
    if(lq->count==0)return 1;
    else return 0;
}

void createlqueque(lqueue*lq){
    lq->count=0;
    lq->front=lq->rear=NULL;
}

void destorylqueque(lqueue*lq){
    while(isempty(lq)){
        lq->rear=lq->front;
        lq->front=lq->front->next;
        free(lq->rear);
        lq->count--;
    }
    lq->rear=NULL;
}

//入队,出队
int qinsert(lqueue*lq,int*item){
    if(isempty(lq)){
        lq->front=lq->rear=(qlnode*)malloc(sizeof(qlnode));
        lq->front->next=NULL;
        lq->front->data=*item;
        lq->count++;
    }
    else{
        lq->rear->next=(qlnode*)malloc(sizeof(qlnode));
        lq->rear=lq->rear->next;
        lq->rear->next=NULL;
        lq->rear->data=*item;
        lq->count++;
    }
    return 1;
}

int qdelete(lqueue*lq,int* item){
    if(isempty(lq))return -1;
    *item=lq->front->data;
    qlnode*temp=lq->front;
    lq->front=lq->front->next;
    free(temp);
    temp=NULL;
    lq->count--;
    if(lq->count==0)lq->rear=0;//尾结点消失
    return 1;
}

//存取队首元素
int qfront(lqueue*lq,int*item){
    if(isempty(lq))return -1;
    *item=lq->front->data;
    return 1;
}
//遍历队列
void printqueue(lqueue*lq){
    qlnode*temp=lq->front;
    while(temp){
        printf("%d ",temp->data);temp=temp->next;
    }
    printf("\n");
}

int main(){
    lqueue*lq=(lqueue*)malloc(sizeof(lqueue));
    createlqueque(lq);
    printqueue(lq);
    int item=5;
    qinsert(lq,&item);
    printqueue(lq);
    item=4;
    qinsert(lq,&item);
    printqueue(lq);
    item=3;
    qinsert(lq,&item);
    printqueue(lq);
    item=2;
    qinsert(lq,&item);
    printqueue(lq);
    item=1;
    qinsert(lq,&item);
    printqueue(lq);

    qdelete(lq,&item);
    printqueue(lq);
   qdelete(lq,&item);
    printqueue(lq);
    qdelete(lq,&item);
    printqueue(lq);
    qdelete(lq,&item);
    printqueue(lq);
    qdelete(lq,&item);
    printqueue(lq);
    destorylqueque(lq);
    printqueue(lq);
    free(lq);
}


栈扩容

加倍扩容

#include<stdio.h>
#include<stdlib.h>
#define  maxstacksize 100
typedef struct stack{
    int*stackarray;
    int size;
    int top;
}stack;
//对应C++的创建stack对象和析构stack对象
void createStack(stack*st){
    st->top=-1;
    st->size=3;
    st->stackarray=malloc(sizeof(int)*st->size);
}
void destoryStack(stack*st){
    st->top=-1;
}
//判空和判满操作,和C++不同需要先定义
int isempty(stack*st){
    return st->top==-1;
}
int isfull(stack*st){
    return st->top==st->size-1;
}

//压栈和弹栈操作,需要考虑栈的状态
int push(stack*st,int *item){
    if(isfull(st)){
        int*B=malloc(sizeof(int)*2*st->size);
        for(int i=0;i<st->size;i++)
            B[i]=st->stackarray[i];
        free(st->stackarray);
        st->stackarray=B;
        st->size*=2;
    }
    st->stackarray[++st->top]=*item;
    return 1;
}
int pop(stack*st,int*item){
    if(isempty(st))return -1;
    *item=st->stackarray[st->top--];
    return 1;
}
//读栈顶
void peek(stack*st,int*item){
    *item=st->stackarray[st->top];
}
//拓展:遍历栈,不要用top--,传的是地址,会改变内容的,不像参考资料传的是值,之前的操作传引用和传指针是一个样子的
void printStack(stack*st){
    int temp=st->top;
    while(temp!=-1){
        printf("%d ",st->stackarray[temp--]);
    }
    printf("\n");
}
int main(){
    stack*st=(stack*)malloc(sizeof(stack));
    createStack(st);
    int item=1;
    push(st,&item);
    item=2;
    push(st,&item);
    item=3;
    push(st,&item);
    item=4;
    push(st,&item);
    item=5;
    push(st,&item);
    peek(st,&item);
    printf("栈顶元素为%d",item);
    printf("出战顺序为:\n");
    printStack(st);
    pop(st,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素:");
    printStack(st);
    pop(st,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素");
    printStack(st);
    if(!isempty(st)){
       printf("当前栈不为空");
    }else{
         printf("当前栈为空");
    }
    return 0;
}






在编写代码运行时:出现codeblocks乱码:
采用修改编码设置
看评论,修改之后编译 运行,否则直接点击它默认没改动是不会给编译的重新;

递增扩容

#include<stdio.h>
#include<stdlib.h>
#define  maxstacksize 100
typedef struct stack{
    int*stackarray;
    int size;
    int top;
}stack;
//对应C++的创建stack对象和析构stack对象
void createStack(stack*st){
    st->top=-1;
    st->size=3;
    st->stackarray=malloc(sizeof(int)*st->size);
}
void destoryStack(stack*st){
    st->top=-1;
}
//判空和判满操作,和C++不同需要先定义
int isempty(stack*st){
    return st->top==-1;
}
int isfull(stack*st){
    return st->top==st->size-1;
}

//压栈和弹栈操作,需要考虑栈的状态
int push(stack*st,int *item){
    if(isfull(st)){
        int*B=malloc(sizeof(int)*(st->size+2));
        for(int i=0;i<st->size;i++)
            B[i]=st->stackarray[i];
        free(st->stackarray);
        st->stackarray=B;
        st->size+=2;
    }
    st->stackarray[++st->top]=*item;
    return 1;
}
int pop(stack*st,int*item){
    if(isempty(st))return -1;
    *item=st->stackarray[st->top--];
    return 1;
}
//读栈顶
void peek(stack*st,int*item){
    *item=st->stackarray[st->top];
}
//拓展:遍历栈,不要用top--,传的是地址,会改变内容的,不像参考资料传的是值,之前的操作传引用和传指针是一个样子的
void printStack(stack*st){
    int temp=st->top;
    while(temp!=-1){
        printf("%d ",st->stackarray[temp--]);
    }
    printf("\n");
}
int main(){
    stack*st=(stack*)malloc(sizeof(stack));
    createStack(st);
    int item=1;
    push(st,&item);
    item=2;
    push(st,&item);
    item=3;
    push(st,&item);
    item=4;
    push(st,&item);
    item=5;
    push(st,&item);
    peek(st,&item);
    printf("栈顶元素为%d",item);
    printf("出战顺序为:\n");
    printStack(st);
    pop(st,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素:");
    printStack(st);
    pop(st,&item);
    printf("%d出栈",item);
    printf("栈中剩余元素");
    printStack(st);
    if(!isempty(st)){
       printf("当前栈不为空");
    }else{
         printf("当前栈为空");
    }
    return 0;
}

进制转换

10进制转换为8进制

#include<stdio.h>
#define maxsize 40
int main(){
    int n;
    int stack[maxsize];
    int top=0;
    scanf("%d",&n);
    //最先压入的最后出栈
    //结束在n小于8的时候,这时候已经存进去了
    while(n>0){
        stack[top++]=n%8;
        n/=8;
    }
    //如果top=-1开始的栈入栈++top,出栈top--。判空top==-1
    while(top!=0){
        printf("%d",stack[--top]);
    }
    return 0;
}

10进制转换为16进制

#include<stdio.h>
#define maxsize 40
#define base 16
int main(){
    int n;
    scanf("%d",&n);
    char stack[maxsize];
    int top=0;

    char digit[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','E','F'};

    while(n>0){
        stack[top++]=digit[n%base];
        n/=base;
    }
    while(top!=0){
        printf("%c",stack[--top]);
    }
    return 0;
}

思考:这里数组初始化知识生疏了;
数组初始化

括号匹配

字符串操作库函数

#include<stdio.h>
#include<string.h>
#define maxsize 40
int match_bracket(char*s){
    char stack[maxsize];
    int top=0;
    for(int i=0;i<strlen(s);i++){
        if(s[i]=='{'||s[i]=='['||s[i]=='('){
                stack[top++]=s[i];
           }
        if(s[i]=='}'||s[i]==']'||s[i]==')'){
            if(top==0)return 0;
            int t=stack[--top];
            int is_match=(s[i]==')'&&t=='(')||(s[i]==']'&&t=='[')||(s[i]=='}'&&t=='{');
            if(!is_match)return 0;
        }
    }
    //还有一种情况就是匹配完之后栈内剩余左括号未匹配
    if(top!=0)return 0;
    return 1;
}
int main(){
    char ch[maxsize];
    scanf("%s",ch);
     printf("%d\n",strlen(ch));
    printf("%d",match_bracket(ch));
}

只考虑一种括号,n对括号共有多少种可能的合法匹配序列
卡特兰数
上面的链接是我看了很多资料讲解最简单最好的文章了。
真的是抓住了卡特兰数的数学本质。

表达式求值

前缀表达式,中缀表达式,后缀表达式

表达式由:操作数 运算符 界限符组成
界限符指 左右括号 标识结束的结束符

中缀表达式

  • 括号内再括号外
  • 先乘除后加减
  • 同一优先级,从左到右

后缀表达式

编译系统采用
由波兰数学家提出,也叫逆波兰式
特点:

  • 后缀表达式没有括号
  • 不存在优先级差别
  • 计算过程完全按照运算符出现的先后次序进行

用后缀表达式求值操作

  • 设置栈
  • 读操作数、运算符、结束符
  • 读到的是操作数将它压入堆栈
  • 若对到的是运算符,就从堆栈中连续弹出两个元素运算,将结果压入
  • 读到结束符,栈顶元素就是计算结果

ppt上面的例子:
ABC*/DE+AC*-=;
其中=就是结束符。

涉及操作

  • creates(s)创建堆栈S
    //进出栈
  • s<-x
    x<-S
  • operate(x,p,y)

实现过程需要参考ASCII码表
数字0是48
数字9是57

//考试时直接测就可以
#include<stdio.h>
int main(){
    char p;
    scanf("%c",&p);
    printf("%d",p);
}

数字可能有几位数,代码参考文章

#include<stdio.h>
#include<string.h>
#define maxsize 30
int operate(char x,char opt,char y){
    int a=x-48;int b=y-48;
    if(opt=='+')return a+b;
    if(opt=='-')return a-b;
    if(opt=='*')return a*b;
    if(opt=='/')return a/b;
}
char cal(char*p,int n){
    char s[maxsize];
    int top=0;

    for(int i=0;i<n;i++){
        if(p[i]!=' '){
                int num=0;
            if(p[i]>=48&&p[i]<=57&&p[i]!=' '){
                while(p[i]>=48&&p[i]<=57&&p[i]!=' '){
                    num=num*10+p[i]-48;
                    i++;
                }
                s[top++]=num+48;
            }
            //p[i]!=' '&&p[i]<48||p[i]>57这个逻辑明显不对;嵌套可以
            else if(p[i]!=' '){
                //注意出栈次序:首先出栈的是y,然后是x
                char y=s[--top];
                char x=s[--top];
                s[top++]=operate(x,p[i],y)+48;
            }
        }
    }
    //如果是top=-1开始的话栈顶是top
    return s[top-1];
}
int main(){
    char p[maxsize];
    scanf("%[^\n]",p);
    int len=strlen(p);
    printf("%s\n",p);
    printf("%d",cal(p,len)-48);
}
//我的理解:回车正常情况给到一个程序的接口然后继续调用io,如果给下一个变量赋值的话,直接退出,不检测后面的io操作
//p指针乱指了,scanf的实质是输入给到指向的地址,地址没有赋值不行的

样例:
输入:
9 3 1 - 3 * +10 2 / +
输出
9 3 1 - 3 * +10 2 / +
20
思考:

  • scanf遇到’ '终止,而gets不安全不用,我当时就没辙了想要不判断一点点输入,但是觉得是常见情况于是查资料,得到了格式化输入,可能学过哈哈;
  • 然后是‘ ’的筛掉,筛掉之后遇到数字情况i记得往后挪,这里没犯错;
  • 理论判断,在判断运算符时想着p[i]!=’ '&&p[i]<48||p[i]>57但是当是空格的时候就错了,这明显筛选不出来,这里犯错是想到左右两个同时成立,oo右边加括号就可以嘶;
  • 最后字符数组里面一直存字符,别一会儿数字一会字符。
  • 对了,在scanf的时候我直接搞了个野指针输入字符串了,scanf是将数据传入地址,没有地址就很荒唐
  • 最后直接用’0’,'9’判断也可以当然
    leetcode关于逆波兰表达式求值

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

  • 设置栈存放运算符
  • 从左到右读入中缀表达式中每一个元素
  • 操作数规则:直接放入后缀表达式
  • 运算符规则
    栈空或栈顶是左括号:运算符压栈
    当前运算符优先级>栈顶运算符:压栈
    当前运算符优先级 ≤ \leq 栈顶运算符:弹栈直到当前运算符的优先级大于 栈顶??或者栈空或者栈顶为左括号,再压栈
  • 括号规则
    遇到左括号压栈
    遇到右括号弹栈直左括号
  • 结束符规则:弹栈至栈空
#include<stdio.h>
#define maxsize 40
int per(char a){
    //这里为什么不if else呢
    //这里两个条件互相排斥,不可能同时成立
    //为什么不直接返回
    //因为函数最下边得有出口否则报错,干脆都交给它了,但是void类型的还是直接return的
    int val=0;
    if(a=='-'||a=='+')val=1;
    if(a=='*'||a=='/')val=2;
    return val;
}
int t(char s[],int q[]){
    int top=-1;
    int front=0,rear=0;
    char x=getchar();
    while(x!='#'){
        //如果x是操作数
        if(x>='0'&&x<='9'){
            int num=0;
            while(x>='0'&&x<='9'){
                num=num*10+x-'0';
                x=getchar();
            }
            //我队列都用循环队列实现节省空间
            //其实这里没必要,因为只有进入队列操作,没动脑子不改了
            q[rear]=num;
            rear=(rear+1)%maxsize;
        }
        //如果x是左右括号
        //如果是左括号的话直接压栈
        if(x!='#'){
            if(x=='('){
                s[++top]='(';
            }
            //如果是右括号的话需要弹栈到左边的括号,所以要先取到栈顶,可以用直到型循环
            else if(x==')'){
                char t=s[top--];
                while(t!='('){
                    q[rear]=t;
                    rear=(rear+1)%maxsize;

                    t=s[top--];
                }
            }
            /*
            else if(x==')'){
                char t;
                do{
                    t=s[top--];
                    q[rear]=t;
                    rear=(rear+1)%maxsize;
                }while(t!=')')
            }
            */
            //如果x是运算符1*3+4,*在栈里遇到+,此时13*应该先弹出来,然后把+压栈,如果是/或者*的话按照左右规则一样
            else{
                //这里我在想top!=-1是否可以写成!top==-1嘶intereting
                while(top!=-1&&s[top]!='('&&per(x)<=per(s[top])){
                        char t=s[top--];

                        q[rear]=t;
                        rear=(rear+1)%maxsize;
                      }
                      s[++top]=x;
            }
            x=getchar();
        }
    }
    while(top!=-1){
        char t=s[top--];
        q[rear]=t;
        rear=(rear+1)%maxsize;
    }
    return rear;
}
int main(){
    //全存队列里面了*V*,感觉真的这么写好麻烦
    //直接用上面的函数节省很多力气倒是,还有判空和判满操作,可操作性加强
    //看上面发现遍历队列我写成那样纯纯偷懒,要取余的temp
    char s[maxsize];
    int q[maxsize];
    int rear=t(s,q);
    //接口设计蠢了不改了QAQ
    for(int i=0;i<rear;i++){
        if(q[i]=='+'){
           printf("+ ");
        }
        else if(q[i]=='-'){
            printf("- ");
        }
        else if(q[i]=='*'){
            printf("* ");
        }
        else if(q[i]=='/'){
            printf("/ ");
        }
        else printf("%d ",q[i]);
    }
}
//(90*2)-10+20/2*(10/10)+3#

思考:

  • 这次其实应该把stack和aqueque抽象出来,然后传值方便,或者整两个接口;
  • 不过最后用return返回一个也够用;这样表述也很直观;
  • 犯了很多错误:我用char类型数组想存’90’,一位一位的当时可以考虑将数据一位一位传进去但是,上面
    -后缀表达式求值,不是有算多位数吗,直接记住了;
  • 如果一位一位读取数字的话,是可以直接存到队列里面的省去很多麻烦,属于庸人自扰之了。。。;
  • 然后因为后缀表达式那个求法直接分析出一种getchar需要两层嵌套的方法,也巩固了之前的知识;
  • 对于getchar的思考:参考
    在这里插入图片描述

今天哥们问我scanf输入整数然后输入*号会输入进去吗,我当时想格式化输入肯定不行啊:
结果果然不行,贴出来帮我笑话他一遍。
在这里插入图片描述这里复习下:在这里插入图片描述
原理的话就是(个人理解没有扒底层代码)

  • scanf默认拿空格分隔字符串,拿回车作为清空缓存区的手段,后面跟了赋值直接退出,因为两个设计这里矛盾了,C的解决方案就是优先照顾赋值,导致缓存清空了并且没有被接口接收直接退出程序。
  • getchar就一个个读取缓存区字符

直接计算中缀表达式值


浏览器前进/后退

递归与回溯

栈混洗

队列应用

单调栈和单调队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值