数据结构篇 --- 栈和队列

简述:栈:“后进先出”                 队列:“先进先出”     

两个栈能实现队列 两个队列能实现栈 

目录

栈:

栈的概念:

栈的实现:

各接口实现:

队列:

队列的概念:

队列的实现:

各接口实现:

栈和队列:

两个栈实现队列:

两个队列实现栈:


栈:

栈的概念:

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端
称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

栈的实现:

在栈的实现上 用数组和链表都是可以的 但是数组更具优势 在后续数据的存入中速度更快(CPU高速缓存命中率更高)

如果要使用单链表实现 就要把链表头当做栈顶 头插头删 否则就要使用双向链表

各接口实现:

这里我们创建数组栈的形式  因为之前有了顺序表各接口实现的基础 这里不再详述

注意的点都在注释

//初始化
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;//当top初始值为0的时候 top指向的是栈顶位置的下一位的
	ps->capacity = 0;
}

//销毁
void StackDestroy(ST* ps)
{
	assert(ps);
	ps->capacity = 0;
	ps->top = 0;
	free(ps->a);
	ps->a = NULL;
}

//压栈
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)//判断是否需要扩容
	{
		int newcapacity = ps->top == 0 ? 4 : ps->capacity * 2;//进行判断
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			printf("realloc fail");
			exit(-1);
		}
		ps->a = tmp;//
		ps->capacity = newcapacity;//容量也切换为新容量
	}
	//填充进去数据
	ps->a[ps->top] = x;
	ps->top++;
}

//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//避免是个空栈
	//一直减下去 top就会变成负值
	ps->top--;
}

//提取栈顶的值
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//避免是个空栈
	return ps->a[(ps->top) - 1];//这里不要使用有副作用的-- 
}

//统计栈中数据的个数
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;//ps->top就把存储的数据个数记录下来了 直接返回就OK
}


//判断栈是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);
	/*if (ps->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return ps->top == 0;//这种表示更简便 但是上面思路更加清晰
}

队列:

队列的概念:

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

队列的实现:

队列与堆不同,不能达到数组和链表形式均可以 出于其先入先出的性质,如果使用数组 效率太低

因此这里只能使用链表来实现队列 

各接口实现:

同样,这里也不再详述  若有不了解 请阅读注释或私信笔者

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}
//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	while (pq->head)
	{
		QueueNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->head = pq->tail= NULL;
}
//入列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		printf("malloc ");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->x = x;
	if (pq->head==NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}
//出列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QueueNode* tmp = pq->head;
	pq->head = pq->head->next;
	free(tmp);
	tmp = NULL;
	//这里值得注意的点是  当pq->head->next为NULL时 此时tail仍指向原位置 此时就会造成野指针的解引用
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}
//返回队头
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->x;
}

//返回末尾值
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//防止为空队列
	return pq->tail->x;
}

//计数
int QueueSize(Queue* pq)//这里如果结构体中设置了sz用来计数 O(1) 直接返回就OK了 如果没有就需要遍历一遍O(N)
{
	assert(pq);
	int sz = 0;
	QueueNode* next = pq->head;
	while (next)
	{
		next = next->next;
		sz++;
	}
	return sz;
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

栈和队列:

两个栈实现队列:

232. 用栈实现队列 - 力扣(Leetcode)

栈的性质是后进先出 队列的性质是先进先出 要围绕着性质进行 

怎样能把后入先出的两个栈实现

整体思路:

假设两个栈 一个栈负责Push 一个栈负责Pop

1.插入数据: 就是正常的压栈操作

2.删除数据: 要先判断popST中是否为空 如果为空 需要从pushST中搬运

  根据题目要求 要创建变量保存一下数据 然后出栈

3.返回开头元素: 如果popST为空 就先进行搬运 如果有数据 根据先进先出原则 存在的栈顶就是队头

4.判空和内存销毁都比较简单  只需要注意是否全部释放内存就OK             

 

 

typedef struct {
    ST pushST;
    ST popST ;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* st = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&st->pushST);
    StackInit(&st->popST);
    return st;
}

void myQueuePush(MyQueue* obj, int x) {
    //向pushST后面压栈
    StackPush(&obj->pushST,x);
}

int myQueuePop(MyQueue* obj) {
      //出的时候要判断popST是否为空
      if(StackEmpty(&obj->popST))//如果为空 需要从pushST向里面推
      {
          while(!StackEmpty(&obj->pushST))
          {
              StackPush(&obj->popST,StackTop(&obj->pushST));
              StackPop(&obj->pushST);
          }
      }
      //创建个变量保存队列开头元素
      //此时位于popST的栈顶
      int tmp = StackTop(&obj->popST);
      StackPop(&obj->popST);
      return tmp;
}

int myQueuePeek(MyQueue* obj) {
      if(StackEmpty(&obj->popST))//如果为空 需要从pushST向里面推
      {
          while(!StackEmpty(&obj->pushST))
          {
              StackPush(&obj->popST,StackTop(&obj->pushST));
              StackPop(&obj->pushST);
          }
      }
      int tmp = StackTop(&obj->popST);
      return tmp;
}

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->pushST)&&StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->pushST);
    StackDestroy(&obj->popST);
    free(obj);
}

两个队列实现栈:

225. 用队列实现栈 - 力扣(Leetcode)

也是围绕着队列和栈的性质进行

整体思路:

假设两个栈 一个存储数据  一个为空(在删除数据之后)  二者不断轮换 

插入数据 : 向非空队列插入数据 

删除数据 : 从非空队列向空队列搬运数据 搬运一个销毁一个 最后只剩下一个 将其pop掉

          根据题目要求 pop之前创建变量  保存数据

 返回栈顶的数据: 只有非空队列有数据 根据其性质 队尾位置即为栈顶数据

判空和销毁比较简单 仍然要主要free完全

typedef struct {
    Queue s1;
    Queue s2;
} MyStack;

MyStack* myStackCreate() {
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));//这里要注意 静态开辟的内存 是局部变量
    //如果建立全局变量可能出问题 所以我们这里选择malloc
    QueueInit(&st->s1);
    QueueInit(&st->s2);
    return st;
}

void myStackPush(MyStack* obj, int x) {
          //向不是空的队列 插入
    if(!QueueEmpty(&obj->s1))
    {
        //向里面插入
        QueuePush(&obj->s1,x);
    }
    else
    {
        QueuePush(&obj->s2,x);
    }
}

int myStackPop(MyStack* obj) {
    //设立一个空队列
    Queue* emptyqueue = &obj->s1;//定义空队列
    Queue* nonemptyqueue = &obj->s2;
    if(!QueueEmpty(&obj->s1))
    {
        emptyqueue = &obj->s2;
        nonemptyqueue = &obj->s1;    
    }
    while(QueueSize(nonemptyqueue)>1)
    {
        QueuePush(emptyqueue,QueueFront(nonemptyqueue));//将非空队列中的前面几个赶到空队列中
        QueuePop(nonemptyqueue);
    }
    int end = QueueFront(nonemptyqueue);//保存栈顶元素
    QueuePop(nonemptyqueue);//移除栈顶元素
    return end;
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->s1))
    {
    return QueueBack(&obj->s1);
    }
    else
    {
    return QueueBack(&obj->s2);
    }
}

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->s1)&&QueueEmpty(&obj->s2);
}

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->s1);//除了obj  队列里面的也要free
    QueueDestroy(&obj->s2);
    free(obj);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值