基础数据结构8:栈和队列

1.栈

#pragma once

typedef int ELEM_TYPE;
#define INIT_SIZE 10

//顺序栈的定义
typedef struct Stack
{
	ELEM_TYPE* base;//数据域 用来保存malloc申请的空间
	int top;//栈顶指针,(1.其中有效数据的个数)(2.用来表示下一个插入位置的下标)
	int stacksize;//用来保存当前stack总共有多少空间(扩容就在这)
}Stack, * PStack;

//顺序栈的操作有哪些?
//增删改查 

//初始化
void Init_stack(PStack ps);

//入栈(或者叫压栈 push)
bool Push(PStack ps, ELEM_TYPE val);

//出栈(或者叫弹栈 pop(获取顶部数据,并且删除))//rtval是一个输出参数(C语言讲到)
bool Pop(PStack ps, ELEM_TYPE* rtval);

//获取顶部元素值 top(获取顶部数据)
bool Top(PStack ps, ELEM_TYPE* rtval);

//获取其有效数据个数
int Get_length(PStack ps);

//判空
bool IsEmpty(PStack ps);

//判满
bool IsFull(PStack ps);

//扩容
static void Inc(PStack ps);

//清空   一间房住了一户人  清空相当于把人赶出去
void Clear(PStack ps);

//销毁  一间房住了一户人  销毁相当于把人赶出去还把房烧了
void Destroy(PStack ps);

//打印
void Show(PStack ps);
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "stack.h"

/*
栈:是先进后出,或者说是后进先出(LIFO)的数据结构,仅限定在表尾进行插入和删除的线性表(受到限制的线性表),因此表尾端称作栈顶,另一端称作栈底,没有数据称作空栈
//例子:弹夹,先压入的子弹反而最后被击发
为啥在表尾:表尾的插入操作以及删除操作时间复杂度都为O(1)

队列:是一种先进先出(FIFO)的数据结构,仅在一端进行插入操作,而在另一端进行删除操作的线性表(受到限制的线性表),没有数据称作空队列
//排队:前面的先被服务,后面的人后被服务
既可以在头部插入,尾部删除,也可以在头部删除,尾部插入

分别用顺序表以及链表都实现一下。
*/

//初始化
void Init_stack(PStack ps)
{
	assert(ps != NULL);
	if (NULL == ps)
	{
		return;
	}

	//2.将成员初始化
	ps->base = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * INIT_SIZE);
	ps->stacksize = INIT_SIZE;
	ps->top = 0;
}

//入栈(或者叫压栈 push)
bool Push(PStack ps, ELEM_TYPE val)
{
	assert(ps != NULL);
	//2.判断栈满了没
	if (IsFull(ps))
	{
		Inc(ps);//3.如果满了 该扩容
	}
	//4.如果没满或者扩容完毕 则插入
	/*ps->base[ps->top] = val;
	ps->top++;*/
	ps->base[ps->top++] = val;

	return true;
}

//出栈(或者叫弹栈 pop(获取顶部数据,并且删除))//rtval是一个输出参数(C语言讲到)
bool Pop(PStack ps, ELEM_TYPE* rtval)
{
	assert(ps != NULL);
	//2.判断栈是否为空
	if (IsEmpty(ps))
	{
		return false;
	}
	//3.出栈
	/**rtval = ps->base[ps->top-1];
	ps->top--;*/
	*rtval = ps->base[--ps->top];

	return true;
}

//获取顶部元素值 top(获取顶部数据)
bool Top(PStack ps, ELEM_TYPE* rtval)
{
	assert(ps != NULL);
	//2.判断栈是否为空
	if (IsEmpty(ps))
	{
		return false;
	}
	//3.获取其顶部数据
	*rtval = ps->base[ps->top - 1];

	return true;
}

//获取其有效数据个数
int Get_length(PStack ps)
{
	assert(ps != NULL);
	return ps->top;
}

//判空
bool IsEmpty(PStack ps)
{
	assert(ps != NULL);
	return ps->top == 0;
}

//判满
bool IsFull(PStack ps)
{
	assert(ps != NULL);
	return ps->top == ps->stacksize;
}

//扩容
static void Inc(PStack ps)
{
	//不需要断言
	ps->base = (ELEM_TYPE*)realloc(ps->base, sizeof(ELEM_TYPE) * ps->stacksize * 2);
	assert(ps->base != NULL);

	ps->stacksize *= 2;
}

//清空   一间房住了一户人  清空相当于把人赶出去
void Clear(PStack ps)
{
	assert(ps != NULL);
	ps->top = 0;
}

//销毁  一间房住了一户人  销毁相当于把人赶出去还把房烧了
void Destroy(PStack ps)
{
	assert(ps != NULL);
	ps->top = 0;
	free(ps->base);
	ps->base = NULL;
}

//打印
void Show(PStack ps)
{
	assert(ps != NULL);
	for (int i = 0; i < ps->top; i++)
	{
		printf("%d ", ps->base[i]);
	}
	printf("\n");
}

2.链栈


#pragma once


typedef int ELEM_TYPE;
typedef struct LStack
{
	ELEM_TYPE data;//数据域
	LStack* next;//指针域
}LStack, * PLStack;

//链栈的操作有哪些?
//增删改查 

//初始化
void Init_stack(PLStack ps);

//入栈(或者叫压栈 push)
bool Push(PLStack ps, ELEM_TYPE val);

//出栈(或者叫弹栈 pop(获取顶部数据,并且删除))//rtval是一个输出参数(C语言讲到)
bool Pop(PLStack ps, ELEM_TYPE* rtval);

//获取顶部元素值 top(获取顶部数据)
bool Top(PLStack ps, ELEM_TYPE* rtval);

//获取其有效数据个数
int Get_length(PLStack ps);

//判空
bool IsEmpty(PLStack ps);

//判满bool IsFull(PLStack ps);

//扩容static void Inc(PLStack ps);

//清空   一间房住了一户人  清空相当于把人赶出去
void Clear(PLStack ps);

//销毁  一间房住了一户人  销毁相当于把人赶出去还把房烧了
void Destroy(PLStack ps);

//打印
void Show(PLStack ps);


#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Lstack.h"

//初始化
void Init_stack(PLStack ps)
{
	assert(ps != NULL);
	//ps->data; 头结点的数据域不使用,不用初始化
	ps->next = NULL;
}

//入栈(或者叫压栈 push) 头插
bool Push(PLStack ps, ELEM_TYPE val)
{
	assert(ps != NULL && val > 0);
	//申请新节点
	LStack* pnewnode = (LStack*)malloc(sizeof(LStack) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;
	//头插
	pnewnode->next = ps->next;
	ps->next = pnewnode;
	return true;
}

//出栈 头删(或者叫弹栈 pop(获取顶部数据,并且删除))//rtval是一个输出参数(C语言讲到)
bool Pop(PLStack ps, ELEM_TYPE* rtval)
{
	assert(ps != NULL);
	if (IsEmpty(ps)) return false;
	
	*rtval = ps->next->data;

	LStack* p = ps->next;
	ps->next = p->next;
	free(p);
	p = NULL;
	return true;
}

//获取顶部元素值 top(获取顶部数据)
bool Top(PLStack ps, ELEM_TYPE* rtval)
{
	assert(ps != NULL);
	if (IsEmpty(ps)) return false;

	*rtval = ps->next->data;
	return true;
}

//获取其有效数据个数
int Get_length(PLStack ps)
{
	assert(ps != NULL);
	int count = 0;
	for (LStack* p = ps->next; p != NULL; p = p->next)
	{
		count++;
	}
	return count;
}

//判空
bool IsEmpty(PLStack ps)
{
	return ps->next == NULL;
}

//不需要判满bool IsFull(PLStack ps)


//扩容static void Inc(PLStack ps)


//清空   一间房住了一户人  清空相当于把人赶出去
void Clear(PLStack ps)
{
	Destroy(ps);
}

//销毁  一间房住了一户人  销毁相当于把人赶出去还把房烧了
void Destroy(PLStack ps)//头删
{
	while (ps->next != NULL)
	{
		LStack* p = ps->next;
		ps->next = p->next;
		free(p);
		p = NULL;
	}
}
void Destroy(PLStack ps)//不需要头节点,需要两个指针
{
	LStack* p = ps->next;
	LStack* q;

	while (p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
	ps->next = NULL;
}

//打印
void Show(PLStack ps)
{
	for (LStack* p = ps->next; p != NULL; p = p->next)
	{
		printf("%d ", p->data);
	}
	printf("\n");
}

3.队列:循环队列

#pragma once

typedef int ELEM_TYPE;

#define MAXQSIZE 100
typedef struct Queue
{
	ELEM_TYPE* base;//数据域 指向malloc申请来的动态内存
	int front;//头指针,当队列不空的时间,保存是队头,指向的第一个元素的下标
	int rear;//尾指针,当队列不空的时间,保存的是队尾,指向的下一个元素入队的下标

	//int length;//有效元素个数
}Queue, * PQueue;

//初始化
void Init_Queue(PQueue pq);

//入队 push
bool Push(PQueue pq, ELEM_TYPE val);

//出队 pop 需要删除操作
bool Pop(PQueue pq, ELEM_TYPE* rtval);

//top  获取队头元素值, 不需要删除操作
bool Top(PQueue pq, ELEM_TYPE* rtval);

//获取其有效元素个数
int Get_length(PQueue pq);

//判空
bool IsEmpty(PQueue pq);

//判满
bool IsFull(PQueue pq);

//清空
void Clear(PQueue pq);

//销毁
void Destroy(PQueue pq);

void Show(PQueue pq);
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include "queue.h"

/*
队列:一种先进先出FIFO(后进后出)的线性表,类比现实生活中的排队
//一端入,另一端出,入队的一端叫做队尾,出队的一端叫做队头。
//当队列中没有一个元素时,叫做空队。

难点:1.如何将队列的入队以及出队时间复杂度都降低为O(1)?
//火车:售货员怎么卖东西? 推车小车,放着瓜子花生矿泉水。
//模仿一下:我们数据不需要挪动,而是是让队头指针以及队尾指针来回走动,得把它臆想成一个环状
2.怎么将判空和判满操作区分开?
//2.1:浪费一个尾部空间,当做标记位,当(rear+1)%MAXSIZE == front;  我们就认为已满
//2.2:在结构体里多申请一个变量,当做标记位,保存的是队列的有效元素个数
3.怎么获取其队列有效元素个数?求队列的有效元素个数的公式是啥?
//  (rear - front + MAXQSIZE) % MAXQSIZE

这节课讲的是循环队列:
//一共需要知道三个要点:
第一个:为了使队列的入队和出队的时间复杂度都为O(1),所以我们需要将其臆想成一个环形
第二个:判空和判满操作的判断依据出现了冲突,都是rear==front; 所以我们需要浪费一个尾部节点,
		当做标记位,当尾指针再向后跑一步就遇到了头指针,此时我们就认为队列满了
第三个:求队列有效元素个数的公式为:(rear - front + MAXQSIZE) % MAXQSIZE,
		加上一个MAXQSIZE的作用是为了防止(rear-front)出现负数,后边的%MAXQSIZE的作用是防止多加了

*/

//初始化
void Init_Queue(PQueue pq)
{
	assert(pq != NULL);
	pq->base = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * MAXQSIZE);
	assert(pq->base != NULL);

	pq->front = 0;
	pq->rear = 0;
	//pq->front = pq->rear = 0;
}

//入队 push
bool Push(PQueue pq, ELEM_TYPE val)
{
	assert(pq != NULL);
	//如果队列已满 则入队不进去
	if (IsFull(pq)) return false;
	pq->base[pq->rear] = val;

	//把它臆想成了一个环,所以rear向后跑的代码会有不同
	//pq->rear++;//ok? error
	pq->rear = (pq->rear + 1) % MAXQSIZE;

	return true;
}

//出队 pop 需要删除操作
bool Pop(PQueue pq, ELEM_TYPE* rtval)
{
	assert(pq != NULL);
	//如果队列为空  则不需要出队
	if (IsEmpty(pq))
		return false;

	*rtval = pq->base[pq->front];
	//把它臆想成了一个环,所以front向后跑的代码会有不同
	//pq->front++;//error
	pq->front = (pq->front + 1) % MAXQSIZE;

	return true;

}

//top  获取队头元素值, 不需要删除操作
bool Top(PQueue pq, ELEM_TYPE* rtval)
{
	//assert
	//如果队列为空  则不需要出队
	if (IsEmpty(pq))
		return false;

	*rtval = pq->base[pq->front];
	//不需要将front指针改变

	return true;
}

//获取其有效元素个数
int Get_length(PQueue pq)
{
	//assert
	int length = (pq->rear - pq->front + MAXQSIZE) % MAXQSIZE;
	return length;
}

//判空
bool IsEmpty(PQueue pq)
{
	return pq->front == pq->rear;
}

//判满
bool IsFull(PQueue pq)
{
	return (pq->rear + 1) % MAXQSIZE == pq->front;
}

//清空
void Clear(PQueue pq)
{
	//assert
	pq->front = pq->rear = 0;
}

//销毁
void Destroy(PQueue pq)
{
	//assert

	free(pq->base);
	pq->base = NULL;
	pq->front = pq->rear = 0;
}

void Show(PQueue pq)
{
	for (int i = pq->front; i != pq->rear; i = (i + 1) % MAXQSIZE)
	{
		printf("%d ", pq->base[i]);
	}
	printf("\n");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值