栈(stack)是一个后进先出(Last in first out,LIFO)的线性表,它要求只在表尾进行删除和插入操作。
所谓的栈,其实也就是一个特殊的线性表(顺序表、链表),但是他在操作上有一些特殊的要求和限制:
——栈的元素必须“后进先出”。
——栈的操作只能在这个线性表的表尾进行。
——注:对栈来说,这个表尾称为栈的栈顶(top),相应的表头称为栈底(bottom)。
栈的插入操作(Push),叫做进栈,也称为压栈,入栈。类似子弹放入弹夹的动作。
栈的删除操作(Pop),叫做出栈,也称为弹栈。如同弹夹中的子弹出夹。
- typedef int ElemType;
- typedef struct
- {
- ElemType* base;
- ElemType* top;
- int stackSize;
- }sqStack;
- #define STACK_INIT_SIZE 100
- 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;
- }
malloc函数向内存申请一块连续可用的空间,如果开辟成功,则返回一个指向这个开辟好空间的起始地址 ,开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查!
- #define STACKINCREMENT 10 //每次追加空间的大小
- 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
- }
realloc函数事实上也是调用malloc函数,在内存中开辟类外一个空间出来,将原来的内容拷贝到新的内存当中,要使用这个函数的话要加入头文件
- Pop(sqStack* s, ElemType* e)
- {
- if (s->top == s->base) //栈空了,没有数据存放,此时可以进行返回
- return;
- *e = *--(s->top); //先栈顶指针-1,然后再取出数据赋值
- }
注意:top指针指向的栈顶是没有数据存放的,所以要先指向-1,再进行取值操作。
- ClearStack(sqStack* s)
- {
- s->top = s->base; //栈顶指针指向栈底,但原来的数据还是存在,但是我们看不到
- }
- DestroyStack(sqStack* s)
- {
- int i, len;
- len = s->stackSize;
- for (i = 0; i < len; i++)
- {
- free(s->base); //利用栈底指针一个一个数据释放掉
- s->base++;
- }
- s->base = s->top = NULL; //栈顶和栈底指针都指向NULL
- s->stackSize = 0; //最后栈容量大小清零
- }
- int StackLen(sqStack *s)
- {
- return (s->top-s->s->base);
- }
- #include
- #include
- #include
-
- #define STACK_INIT_SIZE 20 //初始最大容量
- #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) = 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()
- {
- ElemType c;
- sqStack s;
- int len, i, sum = 0;
- InitStack(&s);
- printf("请输入二进制数,输入#符号表示结束!\n");
- scanf_s("%c",&c);
- while (c != '#')
- {
- Push(&s,c);
- scanf_s("%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;
- }
运行结果:


栈顶相当于单链表的表头,栈底相当于单链表的表尾。
- typedef struct StackNode
- {
- ElemType data; //存放栈的数据
- struct StackNode* next;
- }StackNode,*LinkStackPtr;
-
- typedef struct LinkStack
- {
- LinkStackPtr top;//top指针
- int count; //栈元素计数器
- }LinkStack;
- typedef struct StackNode
- {
- ElemType data; //存放栈的数据
- struct StackNode* next;
- }StackNode,*LinkStackPtr;
-
- typedef struct LinkStack
- {
- LinkStackPtr top;//top指针
- int count; //栈元素计数器
- }LinkStack;
-
- 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;
- }
- /*弹栈/出栈*/
- Status Pop(LinkStack* s, ElemType* e)
- {
- LinkStackPtr p;
- if (StackEmpty(*s)) //判断是否为空栈
- return ERROR;
- *s = s->top->data;
- p = s->top;
- s->top = s->top->next;
- free(p);
- s->count--;
- return OK;
- }
注意:使用纸笔进行画图更有助于理清逻辑。
逆波兰表达式
对于(1-2)*(4+5),如果用逆波兰表达法,应该是这样:1 2 - 4 5 + *
逆波兰计算器
正常的式子我们叫做中缀表达式,它方便人类的阅读计算,但计算机处理中序表达式(中缀表达式)非常复杂。计算机处理后缀表达式非常简便,因为计算机普遍采用的内存结构是栈式结构,遵循后入先出的原则。只需入栈和出栈两个操作就可以实现逆波兰表达式的计算。如果遇到数字就入栈,如果遇到符号就将栈顶的两个元素出栈并作相应的运算,之后将结果入栈,最终栈中剩下的那个数字就是最终结果。
代码:
- #include
- #include
- #include
-
- #define STACK_INIT_SIZE 20
- #define STACK_DILA_SIZE 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 x)
- {
- if (s->top - s->base >= s->stacksize)
- {
- s->base = (elemtype*)realloc(s->base, (STACK_DILA_SIZE + STACK_INIT_SIZE) * sizeof(elemtype));
- if (!s->base)
- {
- exit(0);
- }
- s->top = s->base + STACK_INIT_SIZE;
- s->stacksize += STACK_DILA_SIZE;
- }
- *(s->top) = x;
- s->top++;
- }
-
- void pop(sqstack*s, elemtype*x)
- {
- if (s->base == s->top)
- {
- return;
- }
- s->top--;
- *x = *s->top;
- }
-
- int stacklen(sqstack*s)
- {
- return (s->top - s->base);
- }
-
- int main()
- {
- sqstack*s = (sqstack*)malloc(sizeof(sqstack));
- initstack(s);
- char c;
- char str[10] = { '0' };//设置输入单个数字的缓冲区
- int i = 0;
- elemtype a, b, x;
- scanf_s("%c", &c,sizeof(c));
- while (c != '#')
- {
- while (isdigit(c)||c=='.')//过滤数字
- {
- str[i]=c;
- i++;
- scanf_s("%c", &c, sizeof(c));
- if (c == ' ')
- {
- x = atof(str);//将字符型转为double型
- push(s, x);
- i = 0;
- break;
- }
- }
- switch (c)
- {
- case '+':
- pop(s, &a);
- pop(s, &b);
- push(s, a + b);
- break;
- case '-':
- pop(s, &a);
- pop(s, &b);
- push(s, b - a);
- break;
- case '*':
- pop(s, &a);
- pop(s, &b);
- push(s, a*b);
- break;
- case '/':
- pop(s, &a);
- pop(s, &b);
- push(s, b / a);
- break;
- }
- scanf_s("%c", &c, sizeof(c));
- }
- pop(s, &x);
- printf("%f", x);
-
- return 0;
- }