栈
栈(Stack)是一种遵循后进先出(LIFO, Last In First Out)原则的有序集合。这种数据结构只允许在栈顶进行添加(push)或删除(pop)元素的操作。换句话说,最新添加的元素会被最先移除。栈的这种特性使得它在多种编程和应用场景中非常有用,比如函数调用、括号匹配、表达式求值、页面访问历史等。
只允许从一端进行数据的插入和删除的线性存储结构
顺序栈:类似数组
满增栈,满减栈,空增栈,空减栈
空栈,满栈:栈顶所在位置是否存有元素
增栈,减栈:栈的增长方向
增栈:内存低地址-----》内存高地址
链式栈:类似链表
链式栈是一种基于单链表的数据存储结构,它通过单链表的方式来实现栈的所有操作。链式栈的主要特点和优势如下:
特点
- 动态分配内存:链式栈不需要像顺序栈那样在创建时就分配固定大小的数组空间,而是根据需要动态地分配和释放内存,因此更适合元素数量不确定的情况。
- 操作灵活:由于链式栈采用链表结构,因此在栈顶进行插入(入栈)和删除(出栈)操作时,只需改变相关节点的指针指向,而无需移动其他元素,这使得操作更加灵活和高效。
- 空间利用率高:链式栈能够克服用数组实现的顺序栈在空间利用率上的不足,特别是在元素数量远小于数组容量时,链式栈可以节省大量未使用的空间。
栈的基本操作
1.创建栈:
Stack_t *create_stack()
{
Stack_t *stack = (Stack_t*)malloc(sizeof(Stack_t));
if(stack ==NULL)
{
perror("fail malloc");
return NULL;
}
stack->ptop = NULL;
stack->clen = 0;
return stack;
}
2. 将一个元素添加到栈顶
int push_stack(Stack_t *stack,DataType data)
{
SNode_t *pstack = (SNode_t *)malloc(sizeof(SNode_t));
if(pstack ==NULL)
{
perror("fail malloc");
return -1;
}
pstack->data = data;
pstack->pnext = NULL;
pstack->pnext = stack->ptop;
stack->ptop = pstack;
stack->clen++;
return 0;
}
3. 移除栈顶的元素,并返回这个元素。如果栈为空,则可能抛出异常或返回特殊值
int is_empty_stack(Stack_t *stack)
{
return stack->ptop == NULL;
}
int pop_stack(Stack_t *stack ,DataType *data)
{
if(is_empty_stack(stack))
return 0;
SNode_t *pstack = stack->ptop;
stack->ptop = pstack->pnext;
if(data != NULL)
{
*data= pstack->data;
}
free(pstack);
stack->clen--;
return 0;
}
4.返回栈顶元素
int get_stack_top(Stack_t*stack,DataType *data)
{
if(stack == NULL)
return 0;
SNode_t *pstack = stack->ptop;
*data = pstack->data;
return 0;
}
5.清空栈
void clear_stack(Stack_t* stack)
{
DataType e;
while(!is_empty_stack(stack))
{
pop_stack(stack, &e);
}
}
void destroy_stack(Stack_t * stack)
{
clear_stack(stack);
free(stack);
}
6.打印栈的内容
void print_stack(Stack_t *stack)
{
SNode_t *p = stack->ptop;
while(p != NULL)
{
printf("%d ", p->data);
p = p->pnext;
}
printf("\n");
}
队列
一、队列的基本概念
- 定义:队列是一种只允许在一端(队尾)进行插入操作,而在另一端(队头)进行删除操作的线性表。队尾是允许插入的一端,队头是允许删除的一端。
- 特点:队列具有有序性和先进先出的特性,即最先进入队列的元素将最先被删除。
二、队列的基本操作
1.创建队列:
Queue_t *create_queue()
{
Queue_t *pque = malloc(sizeof(Queue_t));
if (NULL == pque)
{
perror("fail malloc");
return NULL;
}
pque->pfront = NULL;
pque->prear = NULL;
pque->clen = 0;
pthread_mutex_init(&(pque->mutex), NULL);
return pque;
}
2.入队:若队列未满,将新元素加入队列的队尾。
int is_empty_queue(Queue_t *pque)
{
return NULL == pque->pfront;
}
int push_queue(Queue_t *pque, QDataType data)
{
QNode_t *pnode = malloc(sizeof(QNode_t));
if (NULL == pnode)
{
perror("fail malloc");
return -1;
}
pnode->data = data;
pnode->pnext = NULL;
if (is_empty_queue(pque))
{
pque->pfront = pnode;
pque->prear = pnode;
}
else
{
pque->prear->pnext = pnode;
pque->prear = pnode;
}
pque->clen++;
return 0;
}
3.出队:若队列非空,删除并返回队列的队头元素。
int pop_queue(Queue_t *pque, QDataType *pdata)
{
if (is_empty_queue(pque))
return 0;
QNode_t *pdel = pque->pfront;
pque->pfront = pdel->pnext;
if (pdata != NULL)
{
*pdata = pdel->data;
}
free(pdel);
if (NULL == pque->pfront)
{
pque->prear = NULL;
}
pque->clen--;
return 1;
}
3.读队头元素:若队列非空,返回队列的队头元素,但不删除它
int get_queue_front(Queue_t *pque, QDataType *pdata)
{
if (is_empty_queue(pque))
return 0;
if (NULL == pdata)
return 0;
*pdata = pque->pfront->data;
return 1;
}
4.打印队列
void queue_for_each(Queue_t *pque)
{
QNode_t *p = pque->pfront;
while (p != NULL)
{
printf("%d ", p->data);
p = p->pnext;
}
printf("\n");
}
5.清空队列
void clear_queue(Queue_t *pque)
{
while (!is_empty_queue(pque))
{
pop_queue(pque, NULL);
}
}
void destroy_queue(Queue_t *pque)
{
clear_queue(pque);
free(pque);
}
栈与队列对比
队列 | 栈 | |
---|---|---|
插入操作 | 在队尾进行,称为入队 | 在栈顶进行,称为进栈或压栈 |
删除操作 | 在队头进行,称为出队 | 在栈顶进行,称为出栈或退栈 |
访问方式 | 通常只能通过队头和队尾访问元素,不支持随机访问 | 只能访问栈顶元素,不支持随机访问 |
应用场景
- 队列:由于其先进先出的特性,队列常用于需要保持元素顺序的场合。例如,在操作系统中用于任务调度、消息传递等;在计算机网络中用于数据包的传输等。此外,队列还常用于缓冲数据,如串口接收数据时可以使用环形队列来存储数据。
- 栈:由于其后进先出的特性,栈常用于需要逆序操作的场合。例如,在函数调用时保存和恢复寄存器状态、存储局部变量、传递函数参数等;在深度优先搜索遍历等算法中也有广泛应用。此外,栈还可以用于实现括号匹配、逆序打印等算法。