系统栈主要保存以下内容:
1.局部变量,2.函数的形参和返回值 3.函数的调用关系
一、栈
1.基本概念
栈是一种特殊的线性表,具有线性结构。表尾为栈顶,表头为栈顶。遵循先进后出原则,只能在栈顶进行插入和删除操作。
2.基本操作
栈的插入操作为入栈(进栈)。栈的删除操作为出栈。如下:
3.栈的顺序存储结构
它使用一个一维数组来存储栈中的数据元素,数组的下标小的一端作为栈底,另一端作为栈顶。栈顶位置随入栈和出栈操作而变化,因此需要一个整型变量top来记录当前栈顶元素在数组中的位置。不多做介绍。
分类:①满增栈 ②满减栈 ③空增栈 ④空减栈
基本类型 | 类型说明 | 出入栈操作 |
---|---|---|
满增栈 | 栈顶不为空,存储地址由低到高 | 入栈:栈顶++ 入栈数据 出栈:出栈数据,栈顶-- |
满减栈 | 栈顶不为空,存储地址由高到低 | 入栈:栈顶-- 入栈数据 出栈:出栈数据,栈顶++ |
空增栈 | 栈顶为空,存储地址由低到高 | 入栈:入栈数据,栈顶++ 出栈:栈顶--,出栈数据 |
空减栈 | 栈顶为空,存储地址由高到低 | 入栈:入栈数据,栈顶-- 出栈:栈顶++,出栈数据 |
4.栈的链式存储结构
本质为单向链表。对于链栈来说,基本不存在栈满的情况。除非内存中已经没有可以使用的空间。
基本操作:
#include"link.h"
#include<stdio.h>
Stack_t *create_stack() //创建栈
{
Stack_t*slink = malloc(sizeof(Stack_t));
if(slink == NULL)
{
perror("fail malloc\n");
return NULL;
}
slink->ptop = NULL;
slink->clen = 0;
return slink;
}
int is_empty_stack(Stack_t*slink)//判空
{
return slink->ptop == NULL;
}
int push_stack(Stack_t*slink,Datatype data)//入栈
{
Snode_t *pnode = malloc(sizeof(Snode_t));
if(NULL == pnode)
{
perror("fail malloc\n");
return -1;
}
pnode->data = data;
pnode->pnext = NULL;
pnode->pnext = slink->ptop;
slink->ptop = pnode;
slink->clen++;
return 0;
}
void stack_for_each(Stack_t*slink)//遍历打印
{
Snode_t*p = slink->ptop;
while(p )
{
printf("%d\n",p->data);
p = p->pnext;
}
printf("\n");
}
int pop_stack(Stack_t*slink,Datatype * data)//出栈
{
if (is_empty_stack(slink))
return 0;
Snode_t *pdel = slink->ptop;
slink->ptop = pdel->pnext;
if (data != NULL)
{
*data = pdel->data;
}
free(pdel);
slink->clen--;
return 1;
}
int get_stack_top(Stack_t*slink,Datatype *data)//获取栈顶的值
{
if(is_empty_stack(slink))
{
return 0;
}
Snode_t*p = slink->ptop;
*data = p->data;
return 1;
}
void clear_stack(Stack_t*slink)//清空栈
{
while(!is_empty_stack(slink))
{
pop_stack(slink,NULL);
}
}
void destroy_stack(Stack_t*slink)//销毁栈
{
clear_stack(slink);
free(slink);
}
栈的数据类型声明
typedef int Datatype;
typedef struct Snode
{
Datatype data;
struct Snode*pnext;
}Snode_t;
typedef struct Stack
{
Snode_t*ptop;
int clen;
}Stack_t;
extern Stack_t *create_stack();
extern int push_stack(Stack_t*slink,Datatype data);
extern int pop_stack(Stack_t*slink,Datatype * data);
extern int get_stack_top(Stack_t*slink,Datatype *data);
extern void clear_stack(Stack_t*slink);
extern void destroy_stack(Stack_t*slink);
extern void stack_for_each(Stack_t*slink);
5.循环栈与链队栈的比较
①在时间复杂度上是一样的,均为0(1)。
②空间性能,顺序栈需要事先确定一个固定的长度,可能会存在内存空间浪费的问题,但它的优势是存取时定位很方便,而链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制。所以它们的区别和线性表中讨论的一样,如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。
二、队列
1.概念
队列:允许从一端插入数据,另一端删除数据的线性存储结构(FIFO)
管道的本质也是队列,队列主要用于缓存数据
2.基本操作
插入操作为入队操作(队尾) 删除操作为出队操作(队头)
3.顺序队列
使用不当会出现存在假溢出现象,一般使用循环队列,充分利用循环空间。
4.链式队列
初始化:
typedef int QDataType;
typedef struct qnode
{
QDataType data;
struct qnode *pnext;
}QNode_t;
typedef struct queue
{
QNode_t *pfront;
QNode_t *prear;
int clen;
pthread_mutex_t mutex;
}Queue_t;
基本操作:
#include"link.h"
#include<stdio.h>
Queue_t*create_queue()
{
Queue_t *qlink = malloc(sizeof(QNode_t));
if(qlink == NULL)
{
perror("error malloc 1\n");
return NULL;
}
qlink->pfront = NULL;
qlink->prear = NULL;
pthread_mutex_init(&(qlink->mutex),NULL);
qlink->clen = 0;
return qlink;
}
int is_empty_queue(Queue_t*qlink)
{
if(NULL == qlink->pfront && qlink->prear == NULL)
{
return 1;
}
return 0;
}
int push_queue(Queue_t *qlink,QDataType data)
{
QNode_t * pnode = malloc(sizeof(QNode_t));
pnode->data = data;
pnode->pnext = NULL;
if(pnode == NULL)
{
perror("error malloc2\n");
return -1;
}
if(is_empty_queue(qlink))
{
qlink->pfront = pnode;
qlink->prear = pnode;
}
else
{
QNode_t * p = qlink->prear;
p->pnext = pnode;
qlink->prear = pnode;
qlink->clen++;
}
}
int pop_queue(Queue_t *qlink,QDataType * data)
{
QNode_t *del = qlink->pfront;
if(is_empty_queue(qlink))
{
return -1;
}
if(qlink->clen == 1)
{
qlink->pfront = NULL;
qlink->prear = NULL;
}
qlink->pfront = del->pnext;
if(data != NULL)
{
*data = del->data;
}
free(del);
qlink->clen--;
return 1;
}
void print_for_each(Queue_t*qlink)
{
QNode_t*p = qlink->pfront;
while(p)
{
printf("%d\n",p->data);
p = p->pnext;
}
printf("........................\n");
}
int get_queue_pop(Queue_t*qlink,QDataType *data)
{
if(is_empty_queue(qlink))
{
return -1;
}
QNode_t*p = qlink->pfront;
*data = p->data;
}
void clear_queue_pop(Queue_t*qlink)
{
while(!is_empty_queue(qlink))
{
pop_queue(qlink,NULL);
}
}
void destroy_queue(Queue_t*qlink)
{
clear_queue_pop(qlink);
free(qlink);
}
5.循环队列与链队列的比较
①从时间上,其实它们的基本操作都是常数时间,即都为0(1)的,不过循环队列是事先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销,如果入队出队频繁,则两者还是有细微差异。
②空间上来说,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链队列更加灵活。
三、问答。
1.程序运行过程中的栈区和数据结构的栈有什么区别?
程序运行过程中的栈区和数据结构中的栈是两个不同的概念,它们分别属于内存管理和数据结构领域。
-
数据结构中的栈是一种后进先出(LIFO)的数据结构,它只允许在栈顶进行数据的添加(压栈)或移除(出栈)操作。数据结构中的栈是一种抽象数据类型,用于管理数据的存储和访问方式,它不直接涉及物理内存的分配和管理。
-
程序运行过程中的栈区则是指内存中的一个特定区域,用于存储局部变量、函数调用的上下文信息等。在程序运行时,操作系统或运行时环境会自动管理这部分内存的分配和释放。栈区的特点是快速分配和释放内存,但空间大小通常受到限制,且只能从栈顶进行操作。
简而言之,数据结构中的栈是一种抽象数据类型,用于组织和管理数据;而程序运行过程中的栈区是内存中的一个具体区域,用于存储程序执行时的临时数据和函数调用信息,这两者在计算机科学中各有其用途和重要性。
2.队列和栈有什么区别?
①规则不同
1. 队列:先进先出(First In First Out)FIFO
2. 栈:先进后出(First In Last Out )FILO
②对插入和删除操作的限定不同
1. 队列:只能在表的一端进行插入,并在表的另一端进行删除;
2. 栈:只能在表的一端插入和删除。
③遍历数据速度不同
1. 队列:基于地址指针进行遍历,而且可以从头部或者尾部进行遍历,但不能同时遍历,无需开辟空间,因为在遍历的过程中不影响数据结构,所以遍历速度要快;
2. 栈:只能从顶部取数据,也就是说最先进入栈底的,需要遍历整个栈才能取出来,而且在遍历数据的同时需要为数据开辟临时空间,保持数据在遍历前的一致性。