数据结构-线性表之栈和队列

一:栈

(1)栈的概念

栈:栈是一种特殊的线性表,元素只能在一端插入和删除。进行插入和删除的一端称作栈顶,另一端称为栈底。栈中的元素遵循先进后出的原则。

(2)压栈与出栈

压栈:栈的插入操作称为压栈,栈顶入数据
出栈:栈的删除操作称为出栈,栈顶也出数据
如下:栈顶固定,栈顶随元素插入和删除动态变化
在这里插入图片描述
上面的栈使用数组实现的,栈也可以用链表保存,如果是双向链表,哪一边作头或作尾都是可以的,但如果是单链表,为了操作的便利性,要注意方向
在这里插入图片描述
实现栈建议使用数组

(3)栈的C语言实现

1:栈的结构定义

和顺序表一样,栈也有静态和动态的,但是静态的不使用,所以使用动态栈
在这里插入图片描述

2:栈的初始化

(注意修改一下ps->_top=-1)
在这里插入图片描述

3:增容

在这里插入图片描述

4:进栈

入栈时有一个放元素和移动指针的顺序问题,这一点要看你初始化时top的值是多少,如果初始化top=-1,那么就要先移动指针,再放元素。如果top=0,那么就要先放元素然后移动指针。
在这里插入图片描述

5:出栈

在这里插入图片描述

6:清空栈

在这里插入图片描述

7:取栈顶元素

在这里插入图片描述

8:打印
在这里插入图片描述

(4)总结

  • 栈的先进后出规则是相对于栈中的元素而言的,并不是绝对的
  • 栈经常用于有先进后出的需求场景中,如迷宫问题,括号匹配问题,表达式转换问题,还有递归转非递归等问题

(5)实现代码

Stack.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLDatatype;

//typedef struct Stack//静态栈,实际中不使用
//{
    
    
//	int top;
//	SLDatatype* _arr;
//}Stack;

typedef struct Stack
{
    
    
	SLDatatype* _arr;
	int _top;//栈顶
	int _capacity;//容量
}Stack;


void StackInit(Stack* ps);//栈的初始化
void StackDestory(Stack* ps);//销毁栈
void StackAddCapacity(Stack* ps);//打印栈
void StackPrint(Stack* ps);//打印栈

void StackPush(Stack* ps,SLDatatype x);//进栈
void StackPop(Stack* ps);//出栈
int StackEmpty(Stack* ps);//清空栈(1表示成功)
SLDatatype StackTop(Stack* ps);//获取栈顶元素

//Stack.c

#include "stack.h"

void StackInit(Stack* ps)
{
    
    
	assert(ps);
	ps->_arr = (SLDatatype*)malloc(sizeof(SLDatatype) * 4);
	ps->_top = -1;
	ps->_capacity = 4;
}

void StackDestory(Stack* ps)
{
    
    
	assert(ps);
	free(ps->_arr);
	ps->_arr = NULL;
	ps->_top = -1;
	ps->_capacity = 0;

}
void StackPrint(Stack* ps)
{
    
    
	assert(ps);
	int k = ps->_top+1;
	while (k--)
	{
    
    
		printf("%d ", ps->_arr[k]);
	}


}
void StackAddCapacity(Stack* ps)
{
    
    
	assert(ps);
	ps->_capacity *= 2;
	SLDatatype* temp = (SLDatatype)realloc(ps->_arr,sizeof(SLDatatype)*ps->_capacity);
	ps->_arr = temp;

}
void StackPush(Stack* ps,SLDatatype x)
{
    
    
	assert(ps);
	if (ps->_top == ps->_capacity-1)
		StackAddCapacity(ps);
	ps->_top++;
	ps->_arr[ps->_top] = x;
	
}
void StackPop(Stack* ps)
{
    
    
	assert(ps);
	if (ps->_top == -1)
	{
    
    
		printf("栈为空\n");
		exit(-1);
	}
	ps->_top--;
}
int StackEmpty(Stack* ps)
{
    
    
	assert(ps);
	if (ps->_top == -1)
	{
    
    
		printf("栈已经清空\n");
		return 1;
	}
	return 0;
}
SLDatatype StackTop(Stack* ps)
{
    
    
	assert(ps);
	if (StackEmpty)
	{
    
    
		printf("栈空\n");
		exit(-1);
	}
	return ps->_arr[ps->_top];

}

test.c

#include "stack.h"


void test()
{
    
    
	Stack stack;
	StackInit(&stack);
	StackPush(&stack, 1);
	StackPush(&stack, 2);
	StackPush(&stack, 3);
	StackPush(&stack, 4);
	StackPush(&stack, 5);
	StackPush(&stack, 5);
	StackPush(&stack, 5);
	StackPush(&stack, 5);
	StackPush(&stack, 5);
	StackPrint(&stack);
	//StackDestory(&stack);
}

int main()
{
    
    
	test();
}

二:队列

(1)队列的概念

队列:是一种特殊的线性表,只允许在一端插入数据,在另一端删除数据。链表的特性是先进先出。进行插入操作的一端称之为队尾,进行删除操作的一端称之为队头

(2)入队与出队

入队:队列的插入操作,在队尾插入元素
出队:队列的删除操作,在队尾删除元素
在这里插入图片描述
和栈一样,队列同样可以用数组和链表实现,但是如果使用数组实现队列,插入操作可以很好的实现,但是删除操作势必会移动元素,效率不高。所以使用单链表来实现链表,而且单链表的结构很好的满足了链表。
插入操作就是对链表的尾插,删除操作就是链表的头删
在这里插入图片描述

(3)队列的C语言实现

1:队列的结构定义

队列使用单链表存储的,单链表的每个结点定义为QNode,然后使用两个指针frontrear指向QNode的头结点和尾节点
在这里插入图片描述

2:初始化和销毁

在这里插入图片描述

3:入队

在这里插入图片描述

4:出队

正常情况出栈很简单,但是要注意一个非常特殊的情况那就是队列中只有一个结点,也即pq->front==pq->rear,此时要特殊处理,将frontrear都置为空(同时注意打印函数也要相应做处理),否则将发生NULL错误
在这里插入图片描述

5:取队头和队尾

在这里插入图片描述

6:判断链表是否为空

在这里插入图片描述

7:调用接口进行出队操作

在这里插入图片描述

(4)总结

  • 队列常用于有先进先出的需求场景中,比如要保持序列公平(排队抽号机 ),生产消费者模, 广度优先遍历

(5)实现代码

queue.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int DataType;

typedef struct QListNode//队列结点
{
    
    
	struct QListNode* _next;
	DataType _val;
}QNode;

typedef struct Queue//队列
{
    
    
	QNode* _front;//队头,删除元素
	QNode* _rear;//队尾删除元素
}queue;

void QueueInit(queue* pq);//初始化
void QueueDestory(queue* pq);//销毁
void QueuePrint(queue* pq);//打印

void QueuePush(queue* pq,DataType x);//入队
void QueuePop(queue* pq);//出队
DataType QueueFront(queue* pq);//取队头
DataType QueueRear(queue* pq);//取队尾

int QueueEmpty(queue* pq);//判断队列是否为空(返回1则为空)

queue.c

#include "queue.h"


void QueueInit(queue* pq)
{
    
    
	assert(pq);
	pq->_front = NULL;
	pq->_rear = NULL;
	
}
void QueueDestory(queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->_front;
	QNode* pre=NULL;
	while (cur)//节点释放
	{
    
    
		pre = cur;
		cur = cur->_next;
		free(pre);
	}
	//释放头尾指针
	pq->_front = NULL;
	pq->_rear = NULL;
	free(pq);
}
void QueuePrint(queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->_front;
	if (cur == NULL)//这是队列已经空了
	{
    
    
		printf("NULL\n");
	}
	else
	{
    
    
		while (cur)
		{
    
    
			printf("%d-> ", cur->_val);
			cur = cur->_next;
		}
	}
}
void QueuePush(queue* pq,DataType x)
{
    
    
	assert(pq);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	NewNode->_val = x;
	NewNode->_next = NULL;

	if (pq->_rear == NULL)//插入第一个节点,rear和front要同时指向newnode
	{
    
    
		pq->_rear = NewNode;
		pq->_front = NewNode;
	}
	else//正常情况
	{
    
    
		pq->_rear->_next = NewNode;
		pq->_rear = NewNode;
	}
}
void QueuePop(queue* pq)
{
    
    
	assert(pq);
	assert(pq->_front);
	if (pq->_front == pq->_rear)//注意特殊情况,队列中只剩一个元素,需要特殊处理,不然发生错误
	{
    
    
		free(pq->_rear);
		pq->_rear = NULL;
		pq->_front = NULL;
	}
	else//正常情况
	{
    
    
		QNode* cur = pq->_front;
		pq->_front = pq->_front->_next;
		free(cur);
	}
	
}


DataType QueueFront(queue* pq)
{
    
    
	assert(pq);
	assert(pq->_front);
	return pq->_front->_val;
}
DataType QueueRear(queue* pq)
{
    
    
	assert(pq);
	assert(pq->_rear);
	return pq->_rear->_val;	
}
int QueueEmpty(queue* pq)
{
    
    
	assert(pq);
	if (pq->_front == NULL)
		return 1;
	else
		return 0;

}

test.c

#include "queue.h"



void test()
{
    
    
	queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	while (!(QueueEmpty(&q)))
	{
    
    
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	//QueuePrint(&q);
}

int main()
{
    
    
	test();
}

猜你喜欢

转载自blog.csdn.net/qq_39183034/article/details/113179959