数据结构——栈和队列(C语言实现)

news/2024/8/28 16:59:37 标签: 数据结构, c语言, 链表

写在前面:

        栈和队列是两种重要的线性结构。其也属于线性表,只是操作受限,本节主要讨论的是栈和队列的定义、表示方法以及C语言实现。

一、栈和队列的定义与特点

栈:是限定仅在表尾进行插入和删除的线性表。对栈来说,表尾有着特殊的含义,称为栈顶,表头称为栈底,不含元素的空表称为空栈;

栈的特点就是:按后进先出的原则进行的,栈又称为后进先出的线性表;

      与栈相反的是队列,队列是一种先进先出的特点。它只允许在标的一段进行插入,在另一端进行删除。允许插入的一段称为队尾,允许删除的一段称为队头。

二、栈

2.1顺序栈

        顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放在自栈底到栈顶的数据元素。

        设指针top指向栈顶元素在顺序表中的位置,base指针指向栈底元素在顺序栈中的位置。

2.1.1初始化

typedef struct Node
{
	int *base;
	int *top;
	int stacksize;
}Node;
//初始化栈
void  InitStack(Node *S)
{
	S->base = (int *)malloc(sizeof(int)*MAXSIZE);
	S->top = S->base ;
}

初始化,创建一个结构体类型,其中变量为栈顶指针、栈底指针,以及栈的最大空间;

使栈顶和栈底都指向空间的基地址;

2.1.2入栈

int PushStack(Node* S,int data)
{
	if (S->top - S->base == MAXSIZE)
	{
		return -1;
	}	
	else
	{
		*(S->top) = data;
		(S->top)++;
	}
}

判断栈是否满,不满将新元素压入栈顶,栈顶指针+1; 

2.1.3出栈

int PopStack(Node * S)
{
	if (S->top == S->base)
		return -1;
	else
	{
		(S->top)--;
		int i = *(S->top);
		return i;
	}
}

判断栈顶是否为空,若不为空,栈顶指针减1,栈顶元素出栈; 

2.1.4 打印

void PrintStack(Node S)
{
	int len = (S.top-S.base);
	for (int i = 0; i < len; i++)
	{
		(S.top)--;
		printf("%d->", *(S.top));
			}
	printf("NULL\n");
}

先计算栈的空间长度,再依次从栈顶打印除栈的元素; 

2.1.5案例 

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE  5

typedef struct Node
{
	int *base;
	int *top;
	int stacksize;
}Node;
//初始化栈
void  InitStack(Node *S)
{
	S->base = (int *)malloc(sizeof(int)*MAXSIZE);
	S->top = S->base ;
}
//入栈
	//判断栈是否满了
int PushStack(Node* S,int data)
{
	if (S->top - S->base == MAXSIZE)
	{
		return -1;
	}
	
	else
	{
		*(S->top) = data;
		(S->top)++;
	}
}
//出栈
	//判断栈是否为空
int PopStack(Node * S)
{
	if (S->top == S->base)
		return -1;
	else
	{
		(S->top)--;
		int i = *(S->top);
		return i;
	}
}
//打印
void PrintStack(Node S)
{
	int len = (S.top-S.base);
	for (int i = 0; i < len; i++)
	{
		(S.top)--;
		printf("%d->", *(S.top));
		
	}
	printf("NULL\n");
}

int  main()
{
	Node S;
	InitStack(&S);
	PushStack(&S, 1);
	PrintStack(S);
	PushStack(&S, 2);
	PrintStack(S);
	PushStack(&S, 3);
	PrintStack(S);
	PushStack(&S, 4);
	PrintStack(S);
	PushStack(&S, 5);
	PrintStack(S);
	int i = PopStack(&S);
	printf("delete:%d\n",i);
	i = PopStack(&S);
	printf("delete:%d\n", i);
	PrintStack(S);
	return 0;
}

运行结果: 

2.2链栈 

链栈是指采用链式存储结构实现的栈,通常采用单链表实现;

2.2.1初始化 

//新建结点
typedef struct Node
{
	int data;
	struct Node* next;
}Node;
//初始化
Node * StackIint()
{
	Node * head = (Node *)malloc(sizeof(Node));
	head->data=0;
	head->next = NULL;
	return head;
}

 初始化,首先创建一个结构体代表一个节点,然后创建一个空栈,即只有头结点的链表

 2.2.2入栈

void push(Node * node,int data)
{
	Node * S = (Node *)malloc(sizeof(Node));
	S->data = data;
	S->next = node->next;
	node->next = S;
	node->data++;	
}

链表的头插法一样,为入栈元素动态分配一个结点空间; 

2.2.3出栈

//判断是否为栈空
int isEmpty(Node * node)
{
	if (node->data == 0 || node->next == NULL)
		return 1;
	else
		return 0;
}
//出栈
int pop(Node * node)
{
	if (isEmpty(node))
		return -1;
	else
	{
		Node *S = node->next;
		int i = S->data;
		node->next = S->next;
		free(S);
		node->data--;
		return i;
	}
}

        出栈,出栈前需要判断其栈是否为空,如果为空就无法进行出栈。出栈时需要把栈顶元素进行输出,并且释放栈顶空间。

2.2.4取栈顶元素

//判断是否为栈空
int isEmpty(Node * node)
{
	if (node->data == 0 || node->next == NULL)
		return 1;
	else
		return 0;
}
//获取栈元素
int getvalue(Node * node)
{
	if (isEmpty(node))
	{
		return -1;
	}
	else
	{
		return(node->next->data);
	}
}

与出栈一样,需要判断栈是否为空,如果不为空,则输出栈顶元素;

2.2.5案例

#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
	int data;
	struct Node* next;
}Node;

//初始化
Node * StackIint()
{
	Node * head = (Node *)malloc(sizeof(Node));
	head->data=0;
	head->next = NULL;
	return head;
}

//判断是否为栈空
int isEmpty(Node * node)
{
	if (node->data == 0 || node->next == NULL)
		return 1;
	else
		return 0;
}
//获取栈元素

int getvalue(Node * node)
{
	if (isEmpty(node))
	{
		return -1;
	}
	else
	{
		return(node->next->data);
	}
}

//出栈
int pop(Node * node)
{
	if (isEmpty(node))
		return -1;
	else
	{
		Node *S = node->next;
		int i = S->data;
		node->next = S->next;
		free(S);
		node->data--;
		return i;
	}
}


//入栈
void push(Node * node,int data)
{
	Node * S = (Node *)malloc(sizeof(Node));
	S->data = data;
	S->next = node->next;
	node->next = S;
	node->data++;	
}
void printStack(Node * node)
{
	Node * S = node->next;
	while (S)
	{
		printf("%d->", S->data);
		S = S->next;
	}
	printf("NULL\n");
}
int main()
{
	Node * S = StackIint();
	push(S, 1);
	push(S, 2);
	push(S, 3);
	push(S, 4);
	printStack(S);
	int i=pop(S);
	printf(" top data:%d\n", i);
	printStack(S);
	return 0;
}

运行结果:

三、队列

        有与栈结构相同,队列也有两种存储表示,顺序表示与链式表示;

3.1顺序队列(循环队列)

        在顺序队列中。除了设置一组地址连续的存储空间依次存放元素外,另外需要两个正向变量,分别指示队列头元素和队列尾元素的位置。

        在初始化空队列时,令front=rear=0;每当插入新的队列尾元素时,尾指针rear增1;每当删除队列头元素时,头指针front增1;在非空队列中,头指针始终指向队列头元素,而尾指针始终指向尾元素的下一个位置。但是这种操作会使得队列的指针最终越界处理,围殴了限制这种越界处理,我们提出循环队列的形式;

        循环队列的头尾以及队列元素之间的关系不变,只是在循环队列中,头指针、尾指针,依次环状增1的操作可以取模进行运算。通过取模运算,头指针和尾指针可以在顺序变种以头尾衔接的方式“循环”移动;

        对于循环队列 种,不能以头指针、尾指针的值是否相等来判断,队列的空间是“满”还是“空”,采取的方式是:

        少用一个元素空间,即队列空间为m时,有m-1个元素就认为时队满,这样设置的意义在于:当头尾指针相等时,队列为空,当尾指针加1等于头指针时,则认为队列满;

队空条件:front==rear;

队满条件:(rear+1)%MAXQSIZE==front;

3.1.1初始化

//新建一个结构体
typedef struct Queue
{
	int front;
	int rear;
	int data[MAXSIZE];
}Queue;
//1、初始化队列
Queue * initQueue()
{
	Queue * Q = (Queue *)malloc(sizeof(Queue));
	Q->front = Q->rear = 0;
	return Q;
}

创建一个结构体类型,结构体类型中有三个元素:数据数组、头指针地址、尾指针地址。然后初始化创建一个空队列;

3.1.2入队

//判断是否队满
int isFULL(Queue * Q)
{
	if ((Q->rear +1) % MAXSIZE == Q->front)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

//入队
int  enQueue(Queue * Q, int data)
{
	if (isFULL(Q))
	{
		return -1;
	}
	else
	{
		Q->data[Q->rear] = data;
		Q->rear = (Q->rear + 1) % MAXSIZE;
		return 1;
	}
}

首先判断队列是否满,如果没满进行入队,并且操作的都是尾指针; 

3.1.3出队

int isNULL(Queue * Q)
{
	if (Q->rear == Q->front)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int deQueue(Queue * Q)
{
	if (isNULL(Q))
	{
		return -1;
	}
	else
	{
		int e = Q->data[Q->front];
		Q->front = (Q->front + 1) % MAXSIZE;
		return e;
	}
}

首先判断是否队列为空,如果没有为空,就从队头出队,操作的都是队头指针;

3.1.4打印队列

void printfQueue(Queue * Q)
{
	//要知道队列当前有多少个元素
	int len = (MAXSIZE + Q->rear - Q->front) % MAXSIZE;
	int index = Q->front;
	for (int i =0; i <len; i++)
	{
		printf("%d->", Q->data[index]);
		index = (index + 1) % MAXSIZE;
	}
	printf("NULL\n");
}

进行队列元素的遍历,然后依次进行打印; 

3.1.5案例 

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 6
//新建一个结构体
typedef struct Queue
{
	int front;
	int rear;
	int data[MAXSIZE];
}Queue;
//1、初始化队列
Queue * initQueue()
{
	Queue * Q = (Queue *)malloc(sizeof(Queue));
	Q->front = Q->rear = 0;
	return Q;
}

int isFULL(Queue * Q)
{
	if ((Q->rear +1) % MAXSIZE == Q->front)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//2、入队
int  enQueue(Queue * Q, int data)
{
	if (isFULL(Q))
	{
		return -1;
	}
	else
	{
		Q->data[Q->rear] = data;
		Q->rear = (Q->rear + 1) % MAXSIZE;
		return 1;
	}
}
//3、出队
int isNULL(Queue * Q)
{
	if (Q->rear == Q->front)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int deQueue(Queue * Q)
{
	if (isNULL(Q))
	{
		return -1;
	}
	else
	{
		int e = Q->data[Q->front];
		Q->front = (Q->front + 1) % MAXSIZE;
		return e;
	}
}
//4、打印队列
void printfQueue(Queue * Q)
{
	//要知道队列当前有多少个元素
	int len = (MAXSIZE + Q->rear - Q->front) % MAXSIZE;
	int index = Q->front;
	for (int i =0; i <len; i++)
	{
		printf("%d->", Q->data[index]);
		index = (index + 1) % MAXSIZE;
	}
	printf("NULL\n");
}
int main()
{
	Queue * Q = initQueue();
	enQueue(Q, 1);
	printfQueue(Q);
	enQueue(Q, 2);
	printfQueue(Q);
	enQueue(Q, 3);
	printfQueue(Q);
	enQueue(Q, 4);
	printfQueue(Q);
	int i = deQueue(Q);
	printf(" delete	Queue:%d\n",i);
	printfQueue(Q);
	i = deQueue(Q);
	printf(" delete	Queue:%d\n",i);
	printfQueue(Q);
	return 0;
}

 运行结果:

3.2链队

3.2.1初始化

//定义结构体类型
typedef struct Node
{
	int data;
	struct Node* next;
}Node;
//初始化
Node * QueueInit()
{
	Node * head = (Node *)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

与栈操作相同,先创建一个结构类型,再创建一个空的队列,只含有一个头结点的队列。 

3.2.2入队

//入队
void Queueen(Node *Q,int data)
{
	Node * node = (Node *)malloc(sizeof(Node));
	node->data = data;
	Node * q = Q;
	while (q->next != NULL)
	{
		q = q->next;
	}
	node->next = q->next;
	q->next = node;
	Q->data++;
}

入队操作和链表的尾插法是一样的,需要再队尾进行插入; 

3.2.3出队

//判断
int isEmpty(Node * Q)
{
	if (Q->data)
		return 1;
	else
		return 0;
}
//出队
int dequeue(Node * Q)
{
	if (isEmpty(Q))
	{
		Node * node = Q->next;
		int i = node->data;
		Q->next = node->next;
		free(node);
		Q->data--;
		return i;
	}
	else
	{
		return -1;
	}
}

        出队,首先要判断队列是否为空,而且还有遵循先进先出的原则。 

3.2.4打印队列

//打印队列
void printQueue(Node * node)
{
	Node* p = node->next;
	while (p)
	{
		printf("%d->", p->data);
		p = p->next;
	}
	printf("NULL\n");
}

遍历队列,然后打印,同链表操作基本一致; 

3.2.5案例

#include <stdio.h>
#include <stdlib.h>

//定义结构体类型
typedef struct Node
{
	int data;
	struct Node* next;
}Node;
//初始化
Node * QueueInit()
{
	Node * head = (Node *)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
//入队
void Queueen(Node *Q,int data)
{
	Node * node = (Node *)malloc(sizeof(Node));
	node->data = data;
	Node * q = Q;
	while (q->next != NULL)
	{
		q = q->next;
	}
	node->next = q->next;
	q->next = node;
	Q->data++;
}
//判断
int isEmpty(Node * Q)
{
	if (Q->data)
		return 1;
	else
		return 0;
}
//出队
int dequeue(Node * Q)
{
	if (isEmpty(Q))
	{
		Node * node = Q->next;
		int i = node->data;
		Q->next = node->next;
		free(node);
		Q->data--;
		return i;
	}
	else
	{
		return -1;
	}
}

//打印队列
void printQueue(Node * node)
{
	Node* p = node->next;
	while (p)
	{
		printf("%d->", p->data);
		p = p->next;
	}
	printf("NULL\n");
}

int main()
{
	Node * Q = QueueInit();
	Queueen(Q, 1);
	printQueue(Q);
	Queueen(Q, 2);
	printQueue(Q);
	Queueen(Q, 3);
	printQueue(Q);
	Queueen(Q, 4);
	printQueue(Q);
	Queueen(Q, 5);
	printQueue(Q);
	int i = dequeue(Q);
	printf("delete queue:%d\n", i);
	printQueue(Q);
	return 0;
}

运行结果: 

四、总结

        本次主要介绍了两个特殊的线性表:栈和队列;

1、栈是限定仅在表尾进行插入或者删除的线性表,又称为后进先出的线性表。栈有两种存储结构表示方式,顺序表示(顺序栈)和链式表示(链栈);栈的主要操作是入栈和出栈,注意入栈和出栈时需要分别判断栈满和栈空;

2、队列是一种先入先出的线性表,它只允许在表的一端进行插入,在另一端删除元素。队列也有两种存储表示,顺序表示(循环队列)和链式表示(链队)。队列主要的操作是进队和出队,对于顺序的循环队列的进队和出队需要注意判断队满或队空。凡是涉及到队头和队尾指针的都要对其进行求模;


http://www.niftyadmin.cn/n/5559997.html

相关文章

rtf是什么格式的文件?rtf格式和word的区别是什么?

RTF是什么格式的文件? RTF&#xff08;富文本格式&#xff0c;Rich Text Format&#xff09;和Word文档&#xff08;以.doc和.docx为扩展名的Microsoft Word文档&#xff09;是两种常用的文本文件格式。它们在文件结构、兼容性、功能和使用场景等方面存在一些显著差异。 比如…

昇思25天学习打卡营第24天|基于MindSpore的Diffusion扩散模型

Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 关于扩散模型&#xff08;Diffusion Models&#xff09;有很多种理解&#xff0c;本文的介绍是基于denoising di…

git、huggingface 学术加速

1、git 有时候服务器不能直接访问 github&#xff0c;下载代码会很麻烦&#xff1b;安装库的时候&#xff0c;pip xx git 就更难了 比如&#xff0c;这次我需要安装 unsloth&#xff0c;官方给出的脚本是&#xff1a; pip install “unsloth[cu121-torch220] githttps://git…

400多辆萝卜快跑无人驾驶出租车将武汉送上热搜

【科技明说 &#xff5c; 科技热点关注】 不管你信不信&#xff0c;AI颠覆百行百业&#xff0c;正在悄然进行着…… 有好事者针对萝卜快跑的无人驾驶算账&#xff0c;每天成本471元&#xff0c;这个价格贵么&#xff1f; 按照武汉市交通运输局对媒体的说法&#xff0c;现在萝…

Facebook:数字时代的社交瑰宝

在当今数字化飞速发展的时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分&#xff0c;而Facebook作为其中的领军者&#xff0c;不仅连接了全球数十亿的用户&#xff0c;更深刻地改变了人们的社交方式和生活方式。本文将探讨Facebook如何成为数字时代的社交瑰宝…

LVS+Keepalive高可用

1、keepalive 调度器的高可用 vip地址主备之间的切换&#xff0c;主在工作时&#xff0c;vip地址只在主上&#xff0c;vip漂移到备服务器。 在主备的优先级不变的情况下&#xff0c;主恢复工作&#xff0c;vip会飘回到住服务器 1、配优先级 2、配置vip和真实服务器 3、主…

基于单片机的智能窗帘系统设计

【 摘 要 】 随着物联网技术的发展,智能家居越来越受到业界的关注,针对目前市场上智能窗帘的弊端,设计了一款基于单片机的智能窗帘 。 普遍窗帘需要手工进行控制,遥控窗帘通常需要远程控制,智能窗帘与之相比,可以实现自主控制。 系统前端探测器采用光敏传感器,对光线进行…

C#小结:未能找到类型或命名空间名“xxx”(是否缺少 using 指令或程序集引用?)

方案一&#xff1a;移除类库这些失效的引用&#xff0c;下载对应版本的dll&#xff08;如有则不需要重复下载&#xff09;&#xff0c;重新添加引用 方案二&#xff1a;类库右键属性-调整目标框架版本&#xff08;一般是降低版本&#xff09; 方案三&#xff1a;调整类库编译顺…