郝斌数据分析视频p30-p35讲了栈内容
重点:
动态栈较常用,动态栈本质上就是受限的链表。只能从链表顶部插入或删除元素。
会用到两个结构体,1是链表结构体,2是栈结构体
栈结构体中的pTop实际上就是链表最顶部元素本身,不要以为是pTop->pNext,想象成箱子里的顶部和底部即可,不是箱子外指向里面,老师讲课的过程中这块容易误会
先放笔记,再放练习代码
线性结构常见2种应用之一 栈
栈,静态变量是由操作系统在栈中分配内存
压栈和出栈的方式分配内存
堆,malloc是由程序员手动进行动态内存分配
堆排序的方式分配内存
定义
一种可以实现先进后出的存储结构
栈类似于箱子,先放进去的书后取出来
分类
静态栈(数组)
动态栈(链表,最常用)
动态栈就是有限制的链表,比如不允许从尾部插入或删除,只允许从头部
算法
出栈
入栈(压栈)
应用
函数调用(看linux内核实现或者编译的书里可能会提到)
调用时会将被调函数的形参等信息以及下一条语句的地址一起压入栈中,处理完函数后会根据后面的地址找到该继续执行的程序
f() g() k()
{ { {
g() k()
p1函数后面一条语句的地址 p2
} } }
中断(略)
表达式求值(高阶,用两个栈实现,一个栈存放数字,另一个栈存放运算符,具体原理不详)
内存分配(调用函数会将形参全部压入栈中)
缓冲处理
迷宫(高阶,CS中机器人走路采用这个原理)
关于栈的应用,函数调用,在严蔚敏《数据结构》第二版3.4.2递归过程与递归工作站中有详细描述,摘录至此
根据郝斌P54中ppt内容略作修改
一个递归函数, 在函数的执行过程中, 需多次进行自我调用。 那么, 这个递归函数是如何执 行的?先看任意两个函数之间进行调用的情形。
与汇编语言程序设计中主程序和子程序之间的链接及信息交换相类似,在高级语言编制的程序 中, 调用函数和被调用函数之间的链接及信息交换需通过栈来进行。
通常, 当在一个函数的运行期间调用另一个函数时, 在运行被调用函数之前, 系统需先完成 3件事:.
(1) 将所有的实参、 返回地址等信息传递给被调用函数保存;
(2)为被调用函数的局部变量(也包括形参)分配存储区;
(3)将控制转移到被调函数的入口。
而从被调用函数返回调用函数之前, 系统也应完成3件工作:
(1)保存被调函数的计算结果;
(2)释放被调函数的存储区;
(3)依照被调函数保存的返回地址将控制转移到调用函数。
当有多个函数构成嵌套调用时, 按照 “ 后调用先返回 " 的原则, 上述函数之间的信息传递和 控制转移必须通过 "栈 ” 来实现, 即系统将整个程序运行时 所需的数据空间安排在一个栈中, 每当调用一个函数时,就为它在栈顶分配一个存储区,进行压栈操作,每当从一个函数退出时,就释放它的存储区,进行出栈操作,则当前正运行的函数永远在栈顶位置。
A函数调用A函数和A函数调用B函数在计算机看来是没有任何去别的,只是不过用我们日常思维方式理解比较怪异而已。
练习代码
/*
注意:pTop和最顶部的有效数据结点是同一个东西,不要当成是pTop->pNext才是最顶部有效数据结点
*/
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct Node
{
int data;
struct Node * pNext;
}NODE,*PNODE;
typedef struct Stack
{
PNODE pTop;
PNODE pBottom;
}STACK,*PSTACK;
void initStack(PSTACK);
void push(PSTACK,int);
void traverseStack(PSTACK);
bool pop(PSTACK,int*);
void clear(PSTACK);
bool is_empty(PSTACK pS);
int main(void)
{
STACK S;//声明一个栈指针变量
int val;
initStack(&S);
push(&S,1);
push(&S,2);
push(&S,3);
push(&S,4);
push(&S,5);
push(&S,6);
traverseStack(&S);
if(pop(&S,&val))
printf("出栈成功!出栈的的元素为%d\n",val);
else
printf("出栈失败!\n");
traverseStack(&S);
clear(&S);
if(is_empty(&S))
printf("栈为空!\n");
traverseStack(&S);
if(pop(&S,&val))
printf("出栈成功!出栈的的元素为%d\n",val);
else
printf("出栈失败!\n");
return 0;
}
void initStack(PSTACK pS)
{
pS->pTop = (PNODE)malloc(sizeof(NODE));//头结点
if(NULL == pS->pTop)
{
printf("动态内存分配失败!\n");
exit(-1);
}
else
{
pS->pBottom = pS->pTop;
pS->pTop->pNext = NULL;//pS->pBottom = NULL也可以
}
}
void push(PSTACK pS,int val)
{
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("动态内存分配失败!\n");
}
pNew->data = val;
pNew->pNext = pS->pTop;//pS->pTop不能改成 pS->pBottom
pS->pTop = pNew;
return;
}
void traverseStack(PSTACK pS)
{
PNODE p = pS->pTop;
while(p != pS->pBottom)
{
printf("%d ",p->data);
p = p->pNext;
}
printf("\n");
return;
}
bool is_empty(PSTACK pS)
{
if(pS->pTop == pS->pBottom)
return true;
else
return false;
}
bool pop(PSTACK pS,int* pVal)
{
if(is_empty(pS))
{
return false;
}
else
{
PNODE t = pS->pTop;
pS->pTop = t->pNext;
*pVal = t->data;
free(t);
t = NULL;
return true;
}
}
void clear(PSTACK pS)
{
if(is_empty(pS))
{
return;
}
else
{
PNODE p = pS->pTop;
PNODE q = NULL;
while(p!=pS->pBottom)
{
q = p->pNext;
free(p);
p = q;
}
pS->pTop = pS->pBottom;
}
}