栈、队列、双向循环队列

数据结构复习3。
        复习到了这一块, 感觉写完链表、线性表,写这块很轻松!
栈、队列的实现是很简单的,因为它提供功能比较单一。 因此实现上就很简单。
栈、队列的性质在很多场合都会使用到。在解决一些问题的时候会独显它的特色。

1、栈呢,先进后出。
        设计的时候需要把握这个点, 线性表的尾插、尾删就很好的满足栈的特性, 而且线性表尾部操作效率为o(1), 因此栈的设计我会用线性表去做。
(当然队列也可以做哦, 不过设计起来方不方便就是一回事了。)

2、队列呢, 先进先出。
         线性表就不行了,效率不太好, 而链表却可以很好胜任。
链表的头插、头删可以满足先进先出的特点, 并且加以优化链表尾部操作可以为o(1)(其实就是加个尾部指针,专门负责尾部插入、删除), 因此队列我会用链表去做。

3、双向循环链表。
         我会采用定长的数组去做。不过在双向循环链表这块, 会有一些考点,也是设计需要考虑的问题。
既然是定长,就会存在队满、队空的判断条件。
        队满条件:(rear + 1) % (N + 1) == front;
        队空条件: rear == front;
这是面试很多都会考到的。 我说一些别的,在设计双向循环链表的时候,假如说我们开辟N大小空间, 实际上真正开辟时我们会开辟N+1大小空间。 方便设计! 这也是为什么上面写的队满条件里面是N+1。

写完这些我会在下一篇我会写往年栈、队列的考题,像链表题目一样去做!

下面是实现代码:

heap.h

#pragma once

/*
	project: 模拟实现栈
	date:    2020-6-12
	name:    HMW
*/

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

typedef int DataType;								//重定义int

//构建栈的结构 -- 采用线性表实现
typedef struct Stack
{
	DataType *_data;								//基地址
	size_t _top;									//栈顶
	size_t _capcity;								//容量
}Stack;

void StackInit(Stack* ps);							//栈的初始化
void StackDestory(Stack* ps);						//栈的释放
void StackPush(Stack* ps, DataType x);				//入栈操作
void StackPop(Stack* ps);							//出栈操作
DataType StackTop(Stack* ps);						//返回栈顶元素
int StackEmpty(Stack* ps);							//判断栈是否为空

heap.c

#include"heap.h"


//栈的初始化
void StackInit(Stack* ps)
{
	if (!ps)
		return;
	ps->_data = NULL;							//我不想在这里分配空间, 在插入操作进行分配空间操作
	ps->_capcity = ps->_top = 0;				//top 为存放的个数
}

//栈的释放
void StackDestory(Stack* ps)
{
	if (!ps)
		return;
	free(ps->_data);
	ps->_data = NULL;
}

//栈的插入操作
void StackPush(Stack* ps, DataType x)
{
	if (!ps)
		return;
	if (ps->_capcity == ps->_top)												//栈满
	{
		//开辟空间
		int news = ps->_data == NULL ? 10 : 2*ps->_capcity;						//三目运算开辟容量大小
		ps->_data = (DataType *)realloc(ps->_data, sizeof(DataType) * news);
		if (!ps->_data)
			exit(0);
		ps->_capcity = news;

	}
	ps->_data[ps->_top++] = x;													//插入元素
}

//出栈操作
void StackPop(Stack* ps)
{
	//在出栈操作就应该进行打印, 出一个元素打印一个元素!
	if (!ps)
		return;
	if (ps->_top != 0)
	{		
		ps->_top--; 
	}	
	else
	{
		//相当于没有元素可删
		printf("\n当前栈中没有元素!\n");
		return;
	}
}

//返回栈顶元素
DataType StackTop(Stack* ps)
{
	if (!ps)
		return;
	
	return ps->_data[ps->_top - 1];
}

//判断栈是否为空
int StackEmpty(Stack* ps)
{
	if (!ps)
		exit(0);
	return ps->_top == 0;				//默认返回1为为空, 0不为空
}

test.c

#include"heap.h"

void test_Stack()
{
	Stack ps;
	StackInit(&ps);
	StackPush(&ps, 1);
	StackPush(&ps, 2);
	StackPush(&ps, 4);
	StackPush(&ps, 5);
	StackPush(&ps, 6);
	printf("%d\n", StackTop(&ps));
	StackPop(&ps);
	printf("%d\n", StackTop(&ps));
	StackPop(&ps);
	printf("%d\n", StackTop(&ps));
	StackPop(&ps);
	printf("%d\n", StackTop(&ps));
	StackDestory(&ps);
}

int main()
{
	test_Stack();
	system("pause");
	return 0;
}

测试结果:
在这里插入图片描述

队列实现:

queue.h

#pragma once

/*
	project: 模拟队列实现(采用不带头的单链表)
	date:	 2020-6-12
	name:	 HMW
*/

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

typedef int QUDataType;

//采用链表实现队列, 不带头
typedef struct QueueNode
{
	QUDataType _data;						
	struct Queue *_next;
}QueueNode;

//创建两个结构体指针 front, rear, 进行操作!
typedef struct Queue
{
	QueueNode *front;
	QueueNode *rear;
	size_t lengh;
}Queue;

void QueueInit(Queue* pq);						//队列的初始化
void QueueDestory(Queue* pq);					//队列的销毁
QueueNode* BuyQueueNode(QUDataType x);			//构建节点
void QueuePush(Queue* pq, QUDataType x);		//队列的插入操作
void QueuePop(Queue* pq);						//队列的删除操作
QUDataType QueueFront(Queue* pq);				//返回队头元素	
QUDataType QueueBack(Queue* pq);				//返回队尾元素
int QueueEmpty(Queue* pq);						//判断队列是否为空

queue.c

#include"queue.h"

//队列的初始化, 切记: 链表实现
void QueueInit(Queue* pq)
{
	if (!pq)
		return;
	pq->front = pq->rear = NULL;	//头结点都不会在初始化开辟空间
	pq->lengh = 0;
}

//队列的销毁
void QueueDestory(Queue* pq)
{
	if (!pq)
		return;
	if (pq->front == NULL)
		return;
	else
	{
		while (pq->front != NULL)
		{
			QueueNode *p = pq->front->_next;
			free(pq->front);
			pq->front = p;
		}
	}
}

//构建节点
QueueNode* BuyQueueNode(QUDataType x)
{
	QueueNode *pq = (QueueNode *)malloc(sizeof(QueueNode));
	if (!pq)
		exit(0);

	pq->_data = x;
	pq->_next = NULL;
	return pq;
}

//队列的入队操作
void QueuePush(Queue* pq, QUDataType x)
{
	if (!pq)
		return;

	if (pq->front == NULL)
	{
		pq->front = pq->rear = BuyQueueNode(x);
		++pq->lengh;
	}
	else
	{
		pq->rear->_next = BuyQueueNode(x);
		pq->rear = pq->rear->_next;
		++pq->lengh;
	}
}

//队列的出队操作
void QueuePop(Queue* pq)
{
	if (!pq)
		return;

	if (pq->front == NULL)		//队空
	{
		return;
	}
	else
	{
		QueueNode *p = pq->front->_next;
		free(pq->front);
		pq->front = p;
		if (pq->front == NULL)
			pq->rear = NULL;	//最后一个元素删除,rear也要进行操作!
	}
}

//返回队头元素
QUDataType QueueFront(Queue* pq) 
{
	if (!pq)
		return;

	if (pq->front != NULL)
		return pq->front->_data;
	else
		return -1;
}

//返回队尾元素	--    这个没必要吧???
QUDataType QueueBack(Queue* pq)
{
	if (!pq)
		return;
	if (pq->rear != NULL)
		return pq->rear->_data;
	else
		return -1;								//这个返回值不太好, 如果是unsigend类型倒可以。
}

//判断队列是否为空
int QueueEmpty(Queue* pq)
{
	if (!pq)
	if (pq->front == NULL)
		return 0;
	else
		return 1;
}

//队列的长度
int QueueSize(Queue* pq)
{
	return pq->lengh;
}

test.c

#include"queue.h"

void testQueue()
{
	Queue pq;
	QueueInit(&pq);
	printf("插入元素1,2,3,4,5,6,7\n");
	QueuePush(&pq, 1);
	QueuePush(&pq, 2);
	QueuePush(&pq, 3);
	QueuePush(&pq, 4);
	QueuePush(&pq, 5);
	QueuePush(&pq, 6);
	QueuePush(&pq, 7);

	printf("队顶元素\n");
	printf("%d\n", QueueFront(&pq));
	printf("出队\n");
	QueuePop(&pq);
	printf("队顶元素\n");
	printf("%d\n", QueueFront(&pq));

	QueueDestory(&pq);
}


int main()
{
	testQueue();
	system("pause");
	return 0;
}

测试结果:
在这里插入图片描述
双向循环队列:

rqueue.h

#pragma once

/*
	project:		模拟实现环形队列
	date:			2020-6-12
	name:			HMW
	description:	环形队列使用定长的数组实现。	
*/

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

#define N 5						//循环队列的存储个数

typedef int DataType;			//重定义int


//循环队列 -- 线性表实现
typedef struct RNode
{
	DataType *_data;			//基地址
	int _front;					//front下标
	int _rear;					//rear下标
}RQueue;

void RQueueInit(RQueue *qu);					//初始化
void RQueueDestroy(RQueue *qu);					//销毁
void RQueuePush(RQueue *qu, DataType x);		//插入
void RQueuePop(RQueue *qu);						//删除
void RQueuePrint(RQueue *qu);					//打印

rqueue.c

#include"rqueue.h"

//初始化
void RQueueInit(RQueue *qu)
{
	if (!qu)														//刚进来会是随机值
		return;

	qu->_data = (DataType *)malloc(sizeof(DataType) * (N + 1));		//分配N+1个空间 - 有一个空间是不会存储元素的
	if (!qu)
		return;														//分配失败
	qu->_front = qu->_rear = 0;										//起始front、rear下标置为0
}

//销毁
void RQueueDestroy(RQueue *qu)
{
	if (!qu)
		return;

	free(qu->_data);						//释放空间
	qu->_data = NULL;		
}

//判断是否队满
int RQueueOver(RQueue * qu)
{
	if (!qu)
		exit(0);

	if ((qu->_rear + 1) % (N + 1) == (qu->_front))						
		return 1;							//队满条件 -- (read + 1) % (N + 1) == front
	else
		return 0;
}

//插入
void RQueuePush(RQueue *qu, DataType x)
{
	if (!qu)
		return;

	if (RQueueOver(qu))						//判断是否队满
	{
		//printf("队满啦!\n");
		return;
	}

	qu->_data[qu->_rear++] = x;				//插入

	if (qu->_rear == N + 1)
	{
		qu->_rear = 0;						//重置rear下标
		printf("队满, 更新rear下标!\n");
	}	
}

//判断是否队空
int RQueueEmpty(RQueue *qu)
{
	if (!qu)
		return;

	if (qu->_front == qu->_rear)			//队满条件front == rear
		return 1;
	else
		return 0;
}

//删除
void RQueuePop(RQueue *qu)
{
	if (!qu)
		return;

	if (RQueueEmpty(qu))				//判断是否队满
	{
		printf("队空!\n");
		return;
	}

	++qu->_front;						//front偏移
	if (qu->_front == N + 1)
	{
		qu->_front = 0;					//重置front
		printf("更新front下标\n");
	}
		
}

//打印
void RQueuePrint(RQueue *qu)
{
	if (!qu)
		return;
	int i = qu->_front;
	for (i; i < qu->_rear; ++i)
	{
		printf("%d ", qu->_data[i]);
	}
	printf("\n");
}

test.c

	#include"rqueue.h"


	void test_RQueue()
	{
		RQueue qu;
		RQueueInit(&qu);
	
		printf("N = 5, 存放元素1,2,3,4,5\n");
		RQueuePush(&qu, 1);
		RQueuePush(&qu, 2);
		RQueuePush(&qu, 3);
		RQueuePush(&qu, 4);
		RQueuePush(&qu, 5);

		RQueuePrint(&qu);
		printf("后续每出一个元素,打印一次\n");
		RQueuePop(&qu);
		RQueuePrint(&qu);
		RQueuePop(&qu);
		RQueuePrint(&qu);
		RQueuePop(&qu);
		RQueuePrint(&qu);
		RQueuePop(&qu);
		RQueuePrint(&qu);
		RQueuePop(&qu);
		RQueuePrint(&qu);

		RQueuePop(&qu);

		RQueueDestroy(&qu);
	}

	int main()
	{

		test_RQueue();
		system("pause");
		return 0;
	}

测试结果:
在这里插入图片描述

好了。 算是将以前写的代码总结了! 既圆了发博客的心, 又填补了最近复习的知识。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值