数据结构栈与队列

Posted Suk_god

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构栈与队列相关的知识,希望对你有一定的参考价值。

概念及结构

一种特殊的线性表,只能在固定的一端进行插入和删除操作,进行插入和删除的一端称为栈顶,另一端称为栈底。
数据元素遵循:后进先出,LIFO(Last in first out);
压栈(入栈):插入数据的过程
出栈:删除栈顶元素的过程。

栈的实现

结构的选择

栈可以使用顺序表或者链表来实现,考虑到栈的操作来说,顺序表的结构实现更优一些。因为在数组末尾插入数据代价较小。
因此:选用顺序表来实现栈这一数据结构

分类

静态栈结构

实际上这种结构并不实用,所以不推荐使用。

typedef int STDataType;
#define N 10
typedef struct Stack

 STDataType a[N];//数组大小提前给定了
 int top; // 栈顶
Stack;

动态的栈结构

动态增长的栈,相对比较实用,用户可以根据情况确定使用栈的空间大小。

typedef int STDataType;
typedef struct Stack

 STDataType* array;
 int top; // 栈顶
 int capacity; // 容量
Stack;

动态增长的栈相关操作实现

1.栈的基本结构定义

#define DEFAULT 3
// 支持动态增长的栈
typedef int STDataType;

typedef struct Stack

	STDataType* arr;
	int top;		// 栈顶
	int capacity;  // 容量 
Stack;

2.初始化栈

对于栈的初始化,我们只需要为其开辟空间(使用malloc从堆上申请),然后将栈顶top置为0,也就是处于数组的第一个空间位置处。

void StackInit(Stack* ps)

	ps->arr = (STDataType*)malloc(sizeof(STDataType)* DEFAULT);
	if (NULL == ps->arr)
	
		assert(0);
		return;
	
	ps->capacity = DEFAULT;
	ps->top = 0;

3.栈的销毁

由于初始化时数组的空间是从堆上开辟的,所以在使用完毕之后需要释放申请的内存空间,否则会发生内存泄漏。

// 销毁栈 
void StackDestroy(Stack* ps)

	assert(ps);
	if (ps->arr)
	
		free(ps->arr);
		ps->arr = NULL;
		ps->capacity = 0;
		ps->top = 0;
	

判断栈是否为空

只需要判断top的值是否为0,若为0,则栈空,否则不空~

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)

	assert(ps);
	return 0 == ps->top;


入栈操作

数据入栈,只需要将数据插入到top所在的位置,然后将top加1。

但是:会不会出现无法插入的情况???
会的,如果栈空间满了,就无法开辟进行入栈操作

怎么办??当然是扩容之后再入栈了~~

如何扩容呢?
有以下几步构成:
1、重新开辟空间
2、将旧空间的元素copy到新空间
3、释放旧空间
4、使用新空间
具体操作见代码
扩容

static void CheckCapicity(Stack* ps)

	if (ps->top == ps->capacity)
	
		//1.申请新空间
		int newCapicity = ps->capacity * 2;
		STDataType* newArr = (STDataType*)malloc(sizeof(STDataType)*newCapicity);
		if (NULL == newArr)
		
			assert(0);
			return;
		
		//2.拷贝元素
		memcpy(newArr, ps->arr, sizeof(STDataType)*ps->top);
		//3.释放旧空间
		free(ps->arr);

		//4.使用新空间
		ps->arr = newArr;
		ps->capacity = newCapicity;
	

入栈

// 入栈 
void StackPush(Stack* ps, STDataType data)

	assert(ps);
	CheckCapicity(ps);

	ps->arr[ps->top] = data;
	ps->top++;

出栈

出栈就不需要考虑那么多了,只需要判断一下栈是否为空,若不空,让top–就可以实现出栈操作。(top–后尽管要出栈元素依然存在,但是已经无法访问到)

// 出栈 
void StackPop(Stack* ps)

	assert(ps);

	if (!StackEmpty(ps))
	
		ps->top--;
	

获取栈顶元素

1、判断栈是否为空
2、若不空,去array[top-1]处的元素即为栈顶元素
注意:取栈顶元素与出栈不同,不需要改变top自身的值

// 获取栈顶元素 
STDataType StackTop(Stack* ps)


	assert(!StackEmpty(ps));

	return ps->arr[ps->top-1];


获取栈中元素个数

top:既表示栈顶,也表示有效元素的个数

// 获取栈中有效元素个数 
int StackSize(Stack* ps)

	assert(ps);
	return ps->top;

测试代码

void testStack()

	Stack s;
	StackInit(&s);

	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);
	StackPush(&s, 4);
	StackPush(&s, 5);
	int num = StackTop(&s);
	int size = StackSize(&s);
	printf("%d %d\\n",num,size);

	StackPop(&s);
	StackPop(&s);
	StackPop(&s);
	int num1 = StackTop(&s);
	int size1 = StackSize(&s);
	printf("%d %d\\n", num1, size1);

	StackDestroy(&s);

输出结果:

队列

概念及结构

只允许在一端进行数据插入操作,另一端进行数据删除操作的线性结构。
队头:进行数据删除的一端
队尾:进行数据插入的一端
队列特点:先进先出

队列的实现

结构的选择

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

因此,选用链表来实现队列

相关操作的实现

数据结构的定义

typedef int QDataType;

// 链式结构:表示队列 
typedef struct QListNode

	struct QListNode* next;
	QDataType data;
QNode;

// 队列的结构 
typedef struct Queue

	QNode* front;
	QNode* rear;
	int size;//队列里有效元素个数
Queue;

队列的初始化

刚开始,队列没有元素,只需要申请一个空队列,即front和rear都置为NULL,size置为0

//申请一个节点
QNode* buyQNode(QDataType data)

	QNode* Node = (QNode*)malloc(sizeof(QNode));
	if (NULL == Node)
	
		assert(0);
		return NULL;
	
	Node->data = data;
	Node->next = NULL;
	return Node;

// 初始化队列 
void QueueInit(Queue* q)

	assert(q);
	q->front = q->rear = NULL;
	q->size = 0;


销毁队列

对于队列的销毁,我们只需要循环遍历整个队列,采用“单链表头删”的方式进行释放,直到将整个队列全部释放。

// 销毁队列 
void QueueDestroy(Queue* q)

	assert(q);
	QNode* delNode = q->front;
	while (q->front)
	
		q->front = delNode->next;
		free(delNode);
		delNode = q->front;
	
	q->rear = NULL;
	q->size = 0;

判断队列是否为空

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)

	assert(q);
	return NULL == q->front;


入队操作

1.队列为空

2.队列不空

// 队尾入队列 
void QueuePush(Queue* q, QDataType data)

	assert(q);
	QNode* newNode = buyQNode(data);
	//1.队列为空
	if (NULL ==	q->front)
	
		q->front = newNode;
	
	//2.队列不空
	else
	
		q->rear->next = newNode;
	
	q->rear = newNode;
	q->size++;

出队操作

如果队列不空,就可以执行出队列操作,否则直接返回
1.队列有多个结点

2.队列只有1个元素

// 队头出队列 
void QueuePop(Queue* q)

	if (QueueEmpty(q))
	
		return;
	
	else
	
		QNode* delNode = q->front;
		q->front = delNode->next;
		free(delNode);
		if (q->front == NULL)
		
			q->rear = NULL;
		
		q->size--;
	


获取队头元素

// 获取队列头部元素 
QDataType QueueFront(Queue* q)

	assert(!QueueEmpty(q));

	return q->front->data;


获取队尾元素

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)

	assert(!QueueEmpty(q));

	return q->rear->data;


获取有效元素个数

// 获取队列中有效元素个数 
int QueueSize(Queue* q)

	assert(q);

	return q->size;


测试代码

void testQueue()

	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	printf("队头:%d 队尾:%d 有效元素个数:%d \\n", QueueFront(&q), QueueBack(&q), QueueSize(&q));

	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	printf("队头:%d 队尾:%d 有效元素个数:%d \\n", QueueFront(&q), QueueBack(&q), QueueSize(&q));
	QueueDestroy(&q);

以上就是对栈和队列基础操作的分享,各位看官们请留下你们的足迹~~

获取原码:

栈-我的Git
队列-我的Git

以上是关于数据结构栈与队列的主要内容,如果未能解决你的问题,请参考以下文章

栈与队列:链队列算法+可执行代码

从今天开始好好学数据结构02栈与队列

数据结构栈与队列

数据结构栈与队列

栈与队列试题中的操作代码

4-7 Python数据结构常考题之栈与队列