数据结构入门——栈和队列

1.栈

1.1栈的概念及结构

        栈是一种特殊的线性表,只允许在一端进行插入和删除元素的操作。进行数据插入和删除的一端称为栈顶,另一端称为栈底。进行插入的操作称为压栈。进行删除的操作称为出栈。元素遵循后进先出原则,即入数据在栈顶,出数据也在栈顶。

        这就相当于一个桶,在不扰动的情况下往里面放东西,一定会放在最上层,想要取东西时,也是先取最上层。只是咱们把桶底称为栈底,桶顶称为栈顶,放东西到桶顶称为压栈(压:像是放东西到桶里的时候压一压,腾出更多空间),把桶顶的东西拿走就叫出栈。

1.2栈的实现

        栈的实现一般可以用数组或者链表来实现,但是在尾部插入数据这一方面更好实现,之前也有提过顺序表像是数组的上位版本,那么这次咱将基于动态顺序表的结构来实现动态增长的栈。

        先定义一个栈的结构体,之后咱再实现基本的接口。

typedef int SLDateType;

typedef struct Stack
{
	SLDateType* _a;
	int _top;
	int _capacity;
}Stack;
  1.2.1栈的初始化

        将创建好的结构体指针进行初始化赋值。值得注意的是,_top表示的是栈顶元素的后一个位置,这里将_top初始化为0其实是有点别扭的 ( 因为栈底就是-1了 ) ,但是这样方便元素的计数,更好和当前的容量进行比较。_top也可以赋值为1,只是要在比较或者是给数组赋值的时候的时候-1。

void STInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = 0;
}
1.2.2入栈
void STPush(Stack* ps, SLDateType x)
{
	assert(ps);
    //判断顶是否到达最大容量,如果是,扩容
	if (ps->_top == ps->_capacity)
	{
		int DoubCapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
		ps->_capacity = DoubCapacity;
		SLDateType* ptr = (SLDateType*)realloc(ps->_a, sizeof(SLDateType) * DoubCapacity);
		if (ptr == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->_a = ptr;
	}
    //入栈
	(ps->_a)[ps->_top] = x;
	ps->_top++;
}
1.2.3出栈

        出栈很简单,只需要改变_top的值,下一个入栈的数将会覆盖原来的数据。

void STPop(Stack* ps)
{
	assert(ps);
	ps->_top--;
}
1.2.4获取栈顶元素

        这时候_top的概念就很重要了,_top是栈顶的下一个,那么获得栈顶的时候需要-1来获取。

SLDateType STTop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > 0);
	return (ps->_a)[ps->_top-1];
}
1.2.5获取栈的元素个数

        这一步,简单的看起来没有必要,因为直接访问_top就够了。但其实有一个隐患,打个比方就是,别人在用咱们的代码的时候怎么知道_top的初始值呢,直接访问_top谁知道这是不是元素个数,所以咱们需要提供一个接口,减少不必要的麻烦。

int STSize(Stack* ps)
{
	assert(ps);
	return ps->_top;
}
1.2.6判空

        判空也就是判断_top的值是否是初始值,如果是的话说明为空返回1,否则返回0。

int STEmpty(Stack* ps)
{
	assert(ps);
	return ps->_top == 0 ? 1 : 0;
}
1.2.7销毁
void STDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
    ps->_a=NULL;
	ps->_capacity = ps->_top = 0;
}

        以上就是栈实现的几个接口。

2.队列

2.1队列的概念及结构

        队列也是一种特殊的线性表,只允许在一端插入,在另一端删除数据。在进行插入的一端称为队尾,在进行删除的一端称为队头,遵循先进先出原则

        就像是排队进入游乐园,先来的人在队头,可以先进去,后来的人只能站队尾,轮到队尾时才能进去。

2.2队列的实现

        队列也可以用数组或者顺序表实现,不过用数组的话需要挪动数据,效率较低,所以用链表的结构实现会更优。

        先定义结构体,用于维护队列。

typedef int QDataType;
//队列节点结构体
typedef struct QListNode
{
	struct QListNode* next;
	QDataType val;

}QListNode;
//创建一个结构体,用于维护队列的队头、队尾、数据个数
typedef struct Queue
{
	QListNode* phead;
	QListNode* ptail;
	int size;
}Queue;
2.2.1初始化队列

        初始时,为空队列,所以不用创建节点,只是先初始化队列的基本数据。

void QueueInit(Queue* q)
{
	assert(q);
	q->phead = NULL;
	q->ptail = NULL;
	q->size = 0;
}
2.2.2入队列
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
    //创建一个节点
	QListNode* newnode = (QListNode*)malloc(sizeof(QListNode));
	newnode->next = NULL;
    //如果尾节点是NULL则新节点变成第一个节点
	if (q->ptail == NULL)
	{
		q->phead = q->ptail = newnode;
		q->size++;
	}
	else//否则尾节点后尾插一个节点
	{
		q->ptail->next = newnode;
		q->ptail = newnode;
		q->size++;
	}
    //赋值
	q->ptail->val = x;
}
2.2.3出队列
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->phead);
    //创建一个指向第二个节点的指针
	QListNode* next = q->phead->next;
	free(q->phead);
	q->phead = next;
    //当队列一共只有一个节点时,ptail会变成野指针
	if (next == NULL)
	{
		q->ptail = NULL;
	}
	q->size--;
}
2.2.4获取队头、尾数据

        获取队头数据只需要访问头节点指针的数据,同理,队尾则只需要访问尾节点指针的数据。

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->phead);
	return q->phead->val;
}
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->phead);
	return q->ptail->val;
}
2.2.5获取队列元素个数

        也是看上去简单的没必要的接口,但是也是必须的,获取栈的元素个数的接口同理。

int QueueSize(Queue* q)
{
	assert(q);
	assert(q->size > 0);
	return q->size;
}
2.2.6判空

        只需要简单访问队列维护个数的变量。

int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0 ? 1 : 0;
}
2.2.7销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	q->size = 0;
	//和链表同理遍历销毁节点
	while (q->phead!=q->ptail)
	{
        //当前指针指向头
		QListNode* cur = q->phead;
        //头指向下一个节点
		q->phead = q->phead->next;
		free(cur);
		cur = NULL;
	}
	free(q->phead);
	q->phead = q->ptail = NULL;
}

        以上就是队列实现的几个接口。

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值