0
点赞
收藏
分享

微信扫一扫

【数据结构】2.栈和队列

蚁族的乐土 2022-04-06 阅读 57

【数据结构】栈和队列

文章目录

1.栈(Stack)

1.1 栈的定义

​ 栈是一种特殊的线性表,在其上进行DELETE操作所移除的元素是预先预定的,被删除的元素是最近插入的元素,实现的是一种后进先出(last-in,first-out,LIFO)策略

INSERT操作被称为压入、压栈PUSH

DELETE操作被称为弹出、出栈POP

我们称进行压栈与出栈的一端为栈顶,另一端称为栈底

示意图:

栈示意图

这个名称很容易让我们联想到现实中的栈:比如餐厅里装有弹簧的摞盘子的栈。盘子从栈中弹出的次序正好同压入的次序相反。

栈—盘子栈示例

​ (脑补一下竖着放的情形)

1.2 栈的实现

1.2.1对栈结构的分析设计:

经历了[上一节]((2条消息) 【数据结构】1.线性表___exile__的博客-CSDN博客)的学习,我们手上有两种工具来实现栈——数组(实现为数组栈)链表(链式栈)

基于栈只能在一端进行插入删除的操作,相对而言,用数组来实现栈更优。因为数组在尾部插入删除效率高。(ps:用链表来实现栈时,最好在头部进行插入删除操作)

数组栈与链式栈示意图:

数组栈与链式栈1

typedef int DataType;
typedef struct Stack
{
	DataType* a;
	int top;		//栈顶后一位
	int capacity;	//栈容量

}Stack;
1.2.2 接口汇总
//初始化栈
void StackInit(Stack* pst);
//入栈
void StackPush(Stack* pst,DataType x);
//出栈
void StackPop(Stack* pst);
//获取栈顶元素
DataType StackTop(Stack* pst);
//获取栈中元素数目
int StackSize(Stack *pst);
//判断栈是否为空
int StackEmpty(Stack* pst);
//销毁栈
void StackDestroy(Stack* pst);
1.2.3 初始化栈
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}
1.2.4 入栈
void StackPush(Stack* pst, DataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
        
		int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
        
		DataType* tmp = (DataType*)realloc(pst->a, sizeof(DataType) * newcapacity);
		if (tmp!=NULL)
		{
			pst->a = tmp;
			pst->capacity = newcapacity;
		}
		else
		{
			printf("realloc failed\n");
			exit(-1);
		}
	}
	pst->a[pst->top++] = x;
}
1.2.5 出栈
void StackPop(Stack* pst)
{
	assert(pst);
	if (StackEmpty(pst))		return;
	pst->top--;
}
1.2.6 获取栈顶元素
DataType StackTop(Stack* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}
1.2.7获取栈中元素数目
int StackSize(Stack* pst)
{
	assert(pst);
	return pst->top;
}
1.2.8 判断栈是否为空
int StackEmpty(Stack* pst)
{
	return pst->top == 0;
}
1.2.9 销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}

2. 队列(Queue)

2.1 队列的定义

​ 队列也是一种特殊的线性表,在其上进行DELETE操作所移除的元素是预先预定的,被删除的元素是在队列中存在时间最长的那个元素,实现的是一种先进先出(first-in,first-out,FIFO)策略

队列上的INSERT操作称为:入队(ENQUEUE)

DELETE操作称为:出队(DEQUEUE)

队列有队头(Front)队尾(Rear),数据在==队尾入队,在队头出队==,同我们平时排队一样,很好理解。

队列示意图:

队列

2.2 队列的实现

2.2.1 队列结构的设计分析

同队列一样,我们也有两种方式来实现队列——数组队列链式队列

由于队列会频繁的在头部进行删删除操作,而数组在头删上效率低,因此用链表来实现一个队列更优

typedef struct QueueNode
{
	DataType data;
	struct QueueNode* next;

}QueueNode;

typedef struct Queue
{
	QueueNode* front;
	QueueNode* rear;
}Queue;
2.2.2 接口汇总
//初始化队列
void QueueInit(Queue* pq);
//入队
void QueuePush(Queue* pq,DataType x);
//出队
void QueuePop(Queue* pq);
//获取队头元素
DataType QueueFront(Queue* pq);
//获取队尾元素
DataType QueueRear(Queue* pq);
//判断队列是否为空
int QueueEmpty(Queue* pq);
//获取队列长度
int QueueSize(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
2.2.3 初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->front = pq->rear = NULL;
}
2.2.4 入队
void QueuePush(Queue* pq, DataType x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		printf("malloc failed\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->rear == NULL)
	{
		pq->front = pq->rear = newnode;
	}
	else
	{
		pq->rear->next = newnode;
		pq->rear = newnode;
	}
}
2.2.5 出队
void QueuePop(Queue* pq)
{
	assert(pq);
	if (QueueEmpty(pq))		return;
	QueueNode* top = pq->front;
	//一个节点的情况
	if (pq->rear == pq->front)
	{
		free(pq->front);
		pq->front = pq->rear = NULL;
	}
	else
	{
		QueueNode* front = pq->front;
		pq->front = pq->front->next;
		free(front);
	}
}
2.2.6 获取队头元素
DataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->front && pq->rear);
	return pq->front->data;
}
2.2.7 获取队尾元素
DataType QueueRear(Queue* pq)
{
	assert(pq);
	assert(pq->front && pq->rear);
	return pq->rear->data;
}
2.2.8 判断队列是否为空
int QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->front == NULL && pq->rear == NULL;
}
2.2.9 获取队列长度
int QueueSize(Queue* pq)
{
	assert(pq);
	int sz = 0;
	QueueNode* front = pq->front;
	while (front)
	{
		sz++;
		front = front->next;
	}
	return sz;
}
2.2.10销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QueueNode* front = pq->front;
	while (front)
	{
		QueueNode* next = front->next;
		free(front);
		front = next;
	}
	pq->front = pq->rear = NULL;
}

3. 栈与队列的应用——互相实现

3.1 用队列来实现栈

leetcode题目地址:225. 用队列实现栈

3.1.1 结构设计

显然单一的队列不容易实现,我们在这用到两个队列。

typedef struct {
  Queue q1;
  Queue q2;
} MyStack;
3.1.2 实现思路(来回倒数据)
3.1.3 创建栈
MyStack* myStackCreate()
{
  MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
  QueueInit(&obj->q1);
  QueueInit(&obj->q2);
  return obj;
}
3.1.4 入栈
void myStackPush(MyStack* obj, int x) 
{
  Queue* emptyqueue=&obj->q1;
  Queue* noemptyqueue=&obj->q2;
  if(QueueEmpty(&obj->q2))
  {
     emptyqueue=&obj->q2;
     noemptyqueue=&obj->q1;
  }
  QueuePush(noemptyqueue,x);
}

这边有个实现值得称道:

3.1.5 出栈
int myStackPop(MyStack* obj) 
{
  Queue* emptyqueue=&obj->q1;
  Queue* noemptyqueue=&obj->q2;
  if(QueueEmpty(&obj->q2))
  {
     emptyqueue=&obj->q2;
     noemptyqueue=&obj->q1;
  }
  int size=QueueSize(noemptyqueue);	
  int ret=QueueRear(noemptyqueue);
  for(int i=0;i<size-1;i++)
  {
     int cur=QueueFront(noemptyqueue);
     QueuePop(noemptyqueue);
     QueuePush(emptyqueue,cur);
  }
  QueuePop(nnemptyqueue);
  return ret;
}
3.1.6 获取栈顶元素

栈顶元素即为非空队列队尾的元素

int myStackTop(MyStack* obj)
{
  Queue* emptyqueue=&obj->q1;
  Queue* noemptyqueue=&obj->q2;
  if(QueueEmpty(&obj->q2))
  {
     emptyqueue=&obj->q2;
     noemptyqueue=&obj->q1;
  }
  return QueueRear(noemptyqueue);
}
3.1.7 判断栈是否为空
bool myStackEmpty(MyStack* obj) {
  return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
3.1.8 销毁栈
void myStackFree(MyStack* obj) {
  QueueDestroy(&obj->q1);
  QueueDestroy(&obj->q2);
  free(obj);
}

3.2 用栈来实现队列

leetcode链接:232. 用栈实现队列

3.1.1 结构设计

同样我们需要两个栈

typedef struct {
  Stack pushstack;
  Stack popstack;
} MyQueue;
3.2.2 实现思路
  1. 设计上一个栈(pushstack)用来放push进来的数据,一个栈(popstack)用来存放要pop的数据。

  2. 入队简单,把数据push进pushstack中即可

  3. 出队时:我们根据定义,先在popstack中找数据,如果popstack空,那就把pushstack中的数据全部出栈再压入popstack中。

    再把popstack中的栈顶数据pop出来并返回即可。

相较于上面的来回倒数据,这样的实现对于两个栈来说各司其职。

3.2.3 创建队列
MyQueue* myQueueCreate()
{
  MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
  StackInit(&obj->pushstack);
  StackInit(&obj->popstack);
  return obj;
}
3.2.4 入队
void myQueuePush(MyQueue* obj, int x)
{
  StackPush(&obj->pushstack,x);
}
3.2.5 出队
int myQueuePop(MyQueue* obj) 
{
  if(StackEmpty(&obj->popstack))
  {
     while(!StackEmpty(&obj->pushstack))
     {
       int top=StackTop(&obj->pushstack);
       StackPush(&obj->popstack,top);
       StackPop(&obj->pushstack);
     }
  }
  int top=StackTop(&obj->popstack);
  StackPop(&obj->popstack);
  return top;
}
3.2.6 获取队头元素
int myQueuePeek(MyQueue* obj) 
{
  if(StackEmpty(&obj->popstack))
  {
     while(!StackEmpty(&obj->pushstack))
     {
       int top=StackTop(&obj->pushstack);
       StackPush(&obj->popstack,top);
       StackPop(&obj->pushstack);
     }
  }
  return StackTop(&obj->popstack);
}
3.2.7判断队列是否为空
bool myQueueEmpty(MyQueue* obj)
{
  return StackEmpty(&obj->pushstack) &&
         StackEmpty(&obj->popstack);
}
3.2.8 销毁队列
void myQueueFree(MyQueue* obj) 
{
  StackDestroy(&obj->pushstack);
  StackDestroy(&obj->popstack);
  free(obj);
}

举报

相关推荐

0 条评论