数据结构初阶之栈和队列

栈的概念

栈是一种t的线性表,只允许在固定的一段进行插入和删除操作,进行插入、删除操作的叫特殊栈顶,另一端叫做栈底。栈的操作遵守后进先出的原则(last in first out)。

压栈:也就是输数据插入也叫做压栈,入栈、进栈,数据的操作在栈顶。

出栈:也就是数据删除也叫做出栈,数据的操作也在栈顶。

在实际的内存中,栈是向下生长的。

 

栈的实现

线性表有两种结构,顺序表和链表,具体该选用哪一种作为栈呢。

栈的特性是数据的操作只在栈顶,不需要任意部分的插入和删除,所以顺序表更合适,因为顺序表在尾部的操作相率很高,而且缓存命中率高。

定义一个栈

typedef int STDataType;

typedef struct ST
{
	STDataType * st; //数据存储的内容
	int top;         //已经存入的数据
	int capacity;    //空间容量
}ST;

栈的初始化

void StackInit(ST * pst)
{
	assert(pst);
	pst->st = NULL;
	pst->top = pst->capacity = 0;
	//初始化时可以给st开辟空间也可以不开辟。
}

栈的释放

void StackDestory(ST * pst)
{
	assert(pst);
	free(pst->st);
	pst->st = NULL;
	pst->top = pst->capacity = 0;
}

压栈

void StackPush(ST * pst,STDataType x)
{
	assert(pst);
	//开辟空间
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType * newStack = (STDataType *)realloc(pst->st, newcapacity*sizeof(STDataType));
		if (newStack == NULL)
		{
			perror("STInit realloc is error");
			exit(-1);
		}
		pst->st = newStack;
		pst->capacity = newcapacity;
	}
	pst->st[pst->top] = x;
	pst->top++;
}

出栈

出栈并不是真的修改顺序表中的数据,只是将top前移一位。

void StackPop(ST * pst)
{
	assert(pst);
	//修改之前,需要对栈进行判空,当栈中没有元素时,不需要弹出元素。
	assert(!StackEmpty(pst));
	pst->top--;
}

栈的打印

//打印,注意打印栈实际上就是在弹出栈,当栈不为0时,从栈顶开始不断弹出元素
void StackPrint(ST * pst)
{
	assert(pst);
	while (pst->top)
	{
		pst->top--;
		printf("%d->", pst->st[pst->top]);//先打印栈顶元素。
	}
	printf("\n");
}

返回栈顶元素

STDataType StackTop(ST * pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	return pst->st[pst->top - 1];
}

判断栈是否为空

//判空
//判空需要返回两个值,为空,不为空,所以bool类型最合适。
//为空时返回true 不为空返回false
bool StackEmpty(ST * pst)
{
	assert(pst);
	return pst->top==0;
}

栈中元素个数

int StackSize(ST * pst)
{
	assert(pst);
	return pst->top;
}

队列

队列的概念

队列与栈不同,队列是从一段插入数据,另一端删除数据,队列遵守的是先进先出的顺序。

入队:插入数据的一端是队尾。

出队:删除数据的一端是队头。

队列的实现

队列定义

队列应该用什么实现呢?

队列的特性要求在两端进行操作,如果使用顺序表,在数组前操作是时间复杂度为O(n),使用链表在插入删除时会非常简单。

需要注意,为了让队列更节省时间,减少对队列的遍历,需要定义两个结构体,一个结构体是队列的节点,另一个结构体是队列包含两个结构体变量,指向队头和队尾的指针。

typedef int QDataType;

typedef struct Node
{
	QDataType val;
	struct Node* next;
}Node;

typedef struct Queue
{
	Node *head;
	Node *tail;
	int size;
}Queue;

队列初始化

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail=NULL;
	pq->size = 0;
}

队列释放

void QueueDestory(Queue* pq)
{
	assert(pq);
	Node* cur = pq->head;
	while (cur)
	{
		Node* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

入队

void QueuePush(Queue* pq,QDataType val)
{
	assert(pq);
	Node * newnode = (Queue*)malloc(sizeof(Node));
	if (newnode==NULL)
	{
		perror("Node malloc fail\n");
		exit(-1);
	}
	else
	{
		newnode->val = val;
		newnode->next = NULL;
	}
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}

入队时需要对队头队尾进行判断,当队头队尾为NULL时,说明队列中没有节点,队头与队尾指向新节点,如果不 为空说明队列中已经有元素,只改变队尾指针即可。

出队

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//判空
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		Node* del = pq->head;
		pq->head = pq->head->next;
		free(del);
		del = NULL;
	}
	pq->size--;

}

出队需要对队列进行判空,如果队头队尾都为空,不能出队,出队有个特殊情况,当队头地址与队尾地址相等时,需要将队尾队头指针置空,其余情况改变队头指针即可。 

打印

void QueuePrint(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	Node *cur = pq->head;
	while (cur)
	{
		printf("%d->", cur->val);
		Node * del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
	pq->head = pq->tail=NULL;
	printf("\n");
}

队头数据

//队头数据
QDataType QueueFrontData(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->val;
}

队尾数据

//队尾数据
QDataType QueueBackData(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->val;
}

判空

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return (pq->head == NULL&&pq->tail == NULL);
}

队列长度

//队列长度
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

总结 

队列
数据结构多使用顺序表链表
进出方向后进先出先进先出
效率栈顶数据操作效率高队头队尾指针确定可以直接操作效率高
空间开辟空间消耗大,存在浪费不存在空间概念,用多少开多少,但是数据量大时,总体比栈更浪费

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值