栈
栈的定义
是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
栈又称为*后进先出(Last In First Out)的线性表,简称LIFO结构。
有关栈的一些定义
- 栈顶(top):允许插入和删除的一端。
- 栈底(bottom):不允许插入和删除。
- 空栈:不含任何数据元素的栈。
- 压栈(入栈):即在栈顶添加(插入)一个元素
栈的两种实现方式
栈的顺序存储结构
如用数组实现,栈底是:下标为0的一端:
demo:
//
// main.c
// 栈
//
// Created by haoqianbiao on 2021/9/25.
//
#include <stdio.h>
#include <stdlib.h>
#define StackSize 5
typedef struct {
int data[StackSize];
int top;
}SeqStack;
void InitStack(SeqStack* S);
void Push(SeqStack* S, int x);
int Pop(SeqStack* S, int* ptr);
int GetTop(SeqStack* S, int* ptr);
int Empty(SeqStack* S);
int Print(SeqStack* S);
int main(int argc, const char * argv[]) {
int x;
SeqStack S;
InitStack(&S);
printf("对1~5执行入栈操作");
for(int i = 1; i <= 5; i++) {
Push(&S, i);
}
Print(&S);
if(GetTop(&S, &x) == 1) {
printf("当前栈顶元素为%d\n", x);
}
if (Pop(&S, &x) == 1) {
printf("执行一次出栈操作,删除元素:%d\n", x);
}
if(GetTop(&S, &x) == 1) {
printf("当前栈顶元素为:%d\n", x);
}
printf("请输入待入栈元素:");
scanf("%d", &x);
Push(&S, x);
Print(&S);
if (Empty(&S) == 1) {
printf("栈为空\n");
} else {
printf("栈非空\n");
}
return 0;
}
void InitStack(SeqStack* S) {
S->top = -1;
printf("初始化成功!\n");
}
void Push(SeqStack* S, int x) {
if (S->top == StackSize - 1) {
printf("上溢错误,插入失败!\n");
}
S->data[++S->top] = x;
printf("入栈成功!\n");
}
int Pop(SeqStack* S, int* ptr) {
if (S->top == -1) {
printf("下溢错误,删除失败!\n");
return 0;
}
*ptr = S->data[S->top--];
return *ptr;
}
int GetTop(SeqStack* S, int* ptr) {
if (S->top == -1) {
printf("下溢错误,取栈顶失败!\n");
return 0;
}
*ptr = S->data[S->top];
return 1;
}
int Empty(SeqStack* S) {
if (S->top == -1) {
return 1;
} else {
return 0;
}
}
int Print(SeqStack* S) {
printf("栈内元素:\n");
for (int i = 0; i <= S->top; i++) {
printf("%d ", S->data[i]);
}
printf("\n");
return 1;
}
栈的链式存储结构
- 入栈操作:
- 出栈操作
demo:
//
// main.c
// 栈之链式存储结构
//
// Created by haoqianbiao on 2021/9/25.
//
#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
typedef struct Node {
DataType data;
struct Node* next;
}Node;
Node* InitStack(Node* top);
Node* Push(Node* top, DataType x);
Node* GetTop(Node* top, DataType* ptr);
void DestoryStack(Node* top);
Node* Pop(Node* top, DataType* ptr);
int Empty(Node* top);
void Print(Node* top);
int main(){
DataType x = 0;
Node * top = InitStack(top);
printf("对0~4执行入栈操作:\n");
for(int i = 0; i < 5; i++) {
top = Push(top, i);
}
Print(top);
top = GetTop(top, &x);
printf("当前栈顶元素为:%d\n", x);
top = Pop(top,&x);
printf("执行一次出栈操作,删除元素:%d\n",x);
top =GetTop(top,&x);
printf("当前栈顶元素为:%d\n",x);
printf("请输入待入栈元素:");
scanf("%d",&x);
top = Push(top,x);
Print(top);
if(Empty(top)==1)
printf("栈为空\n");
else
printf("栈非空\n");
DestoryStack(top);
return 0;
}
//初始化
Node* InitStack(Node* top) {
top = NULL;
printf("初始化成功!");
return top;
}
//入栈
Node* Push(Node* top, DataType x) {
Node* s = (Node*)malloc(sizeof(Node));
s->data = x;
s->next = top;
top = s;
printf("%d入栈成功!\n", x);
return top;
}
Node* GetTop(Node* top, DataType* ptr) {
if (top == NULL) {
printf("下溢错误,取栈顶失败\n");
return 0;;
}
*ptr = top->data;
return top;
}
//销毁
void DestoryStack(Node* top) {
Node* p = top;
while (top != NULL) {
top = top->next;
free(p);
p = top;
}
}
Node* Pop(Node* top, DataType* ptr) {
Node* p = top;
if(top == NULL) {
printf("下溢错误,删除失败\n");
return 0;
}
*ptr = top->data;
top = top->next;
free(p);
return top;
}
int Empty(Node* top) {
if (top == NULL) {
return 1;
} else {
return 0;
}
}
void Print(Node* top) {
Node* p = top;
while(p) {
printf("%d\n", p->data);
p = p->next;
}
}
队列
队列的定义:
只允许在一端插入数据操作,在另一端进行删除数据操作的特殊线性表;进行插入操作的一端称为队尾(入队列),进行删除操作的一端称为队头(出队列);队列具有先进先出(FIFO)的特性。
demo:
#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
typedef struct Node {
DataType data;
struct Node* next;
}Node;
Node* InitStack(Node* top);
Node* Push(Node* top, DataType x);
Node* GetTop(Node* top, DataType* ptr);
void DestoryStack(Node* top);
Node* Pop(Node* top, DataType* ptr);
int Empty(Node* top);
void Print(Node* top);
int main(){
DataType x = 0;
Node * top = InitStack(top);
printf("对0~4执行入栈操作:\n");
for(int i = 0; i < 5; i++) {
top = Push(top, i);
}
Print(top);
top = GetTop(top, &x);
printf("当前栈顶元素为:%d\n", x);
top = Pop(top,&x);
printf("执行一次出栈操作,删除元素:%d\n",x);
top =GetTop(top,&x);
printf("当前栈顶元素为:%d\n",x);
printf("请输入待入栈元素:");
scanf("%d",&x);
top = Push(top,x);
Print(top);
if(Empty(top)==1)
printf("栈为空\n");
else
printf("栈非空\n");
DestoryStack(top);
return 0;
}
//初始化
Node* InitStack(Node* top) {
top = NULL;
printf("初始化成功!");
return top;
}
//入栈
Node* Push(Node* top, DataType x) {
Node* s = (Node*)malloc(sizeof(Node));
s->data = x;
s->next = top;
top = s;
printf("%d入栈成功!\n", x);
return top;
}
Node* GetTop(Node* top, DataType* ptr) {
if (top == NULL) {
printf("下溢错误,取栈顶失败\n");
return 0;;
}
*ptr = top->data;
return top;
}
//销毁
void DestoryStack(Node* top) {
Node* p = top;
while (top != NULL) {
top = top->next;
free(p);
p = top;
}
}
Node* Pop(Node* top, DataType* ptr) {
Node* p = top;
if(top == NULL) {
printf("下溢错误,删除失败\n");
return 0;
}
*ptr = top->data;
top = top->next;
free(p);
return top;
}
int Empty(Node* top) {
if (top == NULL) {
return 1;
} else {
return 0;
}
}
void Print(Node* top) {
Node* p = top;
while(p) {
printf("%d\n", p->data);
p = p->next;
}
}
队列的相关操作
- 入队: 通常命名为push()
- 出队: 通常命名为pop()
- 求队列中元素个数
- 判断队列是否为空
- 获取队首元素
队列的两种实现方式
队列的顺序存储结构(循环队列)
数组作为底层数据结构时,一般讲队列实现为循环队列。这是因为队列在顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作:
所谓的循环队列,可以把数组看出一个首尾相连的圆环,删除元素时将队首标志往后移动,添加元素时若数组尾部已经没有空间,则考虑数组头部的空间是否空闲,如果是,则在数组头部进行插入。
demo(数组)
#include <stdio.h>
// 保存数据的数组
int arr[500];
// 队列的实际大小
int count;
// 将val添加到队列的末尾
void add(int val)
{
arr[count++] = val;
}
// 返回“队列开头元素”
int front()
{
return arr[0];
}
// 返回并删除“队列开头元素”
int pop()
{
int i = 0;;
int ret = arr[0];
count--;
while (i++ < count)
arr[i - 1] = arr[i];
return ret;
}
// 返回“队列”的大小
int size()
{
return count;
}
// 返回“队列”是否为空
int is_empty()
{
return count==0;
}
int main()
{
int tmp = 0, i;
// 将10, 20, 30 依次推入队列中
add(10);
add(20);
add(30);
for(i = 0;i < size(); i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 将“队列开头的元素”赋值给tmp,并删除“该元素”
tmp = pop();
printf("tmp=%d\n", tmp);
// 只将“队列开头的元素”赋值给tmp,不删除该元素.
tmp = front();
printf("tmp = %d\n", tmp);
add(40);
// 打印队列
printf("is_empty = %d\n", is_empty());
printf("size = %d\n", size());
while (!is_empty()) {
printf("%d\n", pop());
}
return 0;
}
demo(数组+循环队列):
#include <stdio.h>
// 保存数据的数组
int arr[50];
// 队列的实际大小
int count;
//队头和队尾下标
int front;
int tail;
int size()
{
return tail-front;
}
// 返回“队列”是否为空
int is_empty()
{
if(front == tail){
return 1;
}
return 0;
}
// 返回“队列”是否为满
int is_full()
{
if(front == (tail + 1) % 50) return 1;
return 0;
}
// 将val添加到队列的末尾
void add(int val)
{
if(!is_full())
{
arr[tail] = val;
tail = (tail + 1) % 50;
}
else
{
printf("队列满\n");
}
}
// 返回“队列开头元素”
int first()
{
return arr[front];
}
// 返回并删除“队列开头元素”
int pop()
{
int ret;
if(!is_empty())
{
ret=arr[front] ;
front=(front+1)%50;
}
else
{
printf("队列空\n");
}
return ret;
}
int main()
{
int tmp = 0, i;
// 将10, 20, 30 依次推入队列中
add(10);
add(20);
add(30);
for(i=0;i<size();i++) {
printf("%d ",arr[i]);
}
printf("\n");
// 将“队列开头的元素”赋值给tmp,并删除“该元素”
tmp = pop();
printf("tmp = %d\n", tmp);
// 只将“队列开头的元素”赋值给tmp,不删除该元素.
tmp = first();
printf("tmp = %d\n", tmp);
add(40);
// 打印队列
printf("is_empty()=%d\n", is_empty());
printf("size()=%d\n", size());
while (!is_empty())
{
printf("%d\n", pop());
}
return 0;
}
队列的链式存储结构
链式队列的实现思想同顺序队列类似,只需创建两个指针(命名为 top 和 rear)分别指向链表中队列的队头元素和队尾元素,如图 1 所示:
入队:
链队队列中,当有新的数据元素入队,只需进行以下 3 步操作:
将该数据元素用节点包裹,例如新节点名称为 elem;
与 rear 指针指向的节点建立逻辑关系,即执行 rear->next=elem;
最后移动 rear 指针指向该新节点,即 rear=elem;
出队:
当链式队列中,有数据元素需要出队时,按照 “先进先出” 的原则,只需将存储该数据的节点以及它之前入队的元素节点按照原则依次出队即可。这里,我们先学习如何将队头元素出队。
链式队列中队头元素出队,需要做以下 3 步操作:
通过 top 指针直接找到队头节点,创建一个新指针 p 指向此即将出队的节点;
将 p 节点(即要出队的队头节点)从链表中摘除;
释放节点 p,回收其所占的内存空间;
#include <stdio.h>
#include <stdlib.h>
typedef struct QNode{
int data;
struct QNode * next;
}QNode;
QNode * initQueue(){
QNode * queue=(QNode*)malloc(sizeof(QNode));
queue->next=NULL;
return queue;
}
QNode* enQueue(QNode * rear,int data){
QNode * enElem=(QNode*)malloc(sizeof(QNode));
enElem->data=data;
enElem->next=NULL;
//使用尾插法向链队列中添加数据元素
rear->next=enElem;
rear=enElem;
return rear;
}
QNode* DeQueue(QNode * top,QNode * rear){
QNode * p = NULL;
if (top->next==NULL) {
printf("\n队列为空");
return rear;
}
p=top->next;
printf("%d ",p->data);
top->next=p->next;
if (rear==p) {
rear=top;
}
free(p);
return rear;
}
int main() {
QNode * queue,*top,*rear;
queue=top=rear=initQueue();//创建头结点
//向链队列中添加结点,使用尾插法添加的同时,队尾指针需要指向链表的最后一个元素
rear=enQueue(rear, 1);
rear=enQueue(rear, 2);
rear=enQueue(rear, 3);
rear=enQueue(rear, 4);
//入队完成,所有数据元素开始出队列
rear=DeQueue(top, rear);
rear=DeQueue(top, rear);
rear=DeQueue(top, rear);
rear=DeQueue(top, rear);
rear=DeQueue(top, rear);
return 0;
}