入门小白学数据结构与算法(3)——栈和队列

一、 栈——一种数据遵从“后进先出”的线性表

1.1 基本概念:

1.仅限定在表尾(栈顶)进行插入和删除操作
2.把允许插入和删除的一端(表尾)称为栈顶,另一端叫做栈底,不含任何元素的叫做空栈
3.先进的数据放在底,栈的插入又叫入栈,栈的删除叫做出栈。
理解:其实栈就是一种特殊的线性表(顺序表,链表),只是数据的操作只允许在表尾(栈顶)

1.2 存储结构

分为顺序存储和链式存储,常用的为顺序存储

1.3栈的相关代码操作——栈顶栈底指针形式

栈的定义

typedef int Elemtype;
typedef struct
{
	Elemtype *base;//存储栈底元素的地址
	Elemtype *top;//存储栈顶元素的地址
	int stacksize;//当前栈可用的最大容量
}sqstack;

栈的创建(顺序存储结构)——初始时刻,栈顶和栈尾在一起,随着数据的插入,栈顶和栈尾逐渐分开

#define STACK_INIT_SIZE 100
Status Initstack(sqstack* s)
{
	s->base=(Elemtype)malloc(STACK_INIT_SIZE * sizeof(Elemtype));
	if(!(s->base))
		return ERROR;
	s->top=s->base;
	s->stacksize=STACK_INIT_SIZE;
	return OK;
}

入栈——先判断栈是否放满,再将数据放在栈顶,先移动栈顶的地址,再把数据放在这个地址里面

#define stackincreasesize 10
Status pushstack(sqstack* s, Elemtype e)
{
	if(s->top-s->base>=s->stacksize)//判断栈是否满
	{
		s->base=(Elemtype)realloc(s->base,(s->stacksize+stackincreasesize)*sizeof(Elemtype));
		if(!(s->base))
			return Error;
		s->stacksize+=stackincreasesize;
		s->top=s->base+s->stacksize;
	}
	s->top+=1;
	*(s->top)=e;
	return OK;
} 

出栈——从栈顶取,先把数据拿出来,再将地址减1

Status pop(sqstack* s, Elemtype* e)
{
	if(s->top == s->base)
		return ERROR;
	(*e)=*(s->top);
	s->top--;
}

1.4栈的相关代码操作——数组形式

栈的定义

typedef struct stack
{
	
	Elemtype data[MAXSIZE];
	int top=-1;

}stack;

入栈

status pushstack(stack* s, Elemtype e)
{
	if (s->top == MAXSIZE - 1)
		return Error;
	s->top++;
	s->data[s->top] = e;
	
	return OK;
}

出栈

status popstack(stack* s, Elemtype* e)
{
	if (s->top == -1)
		return Error;
	(*e) = s->data[s->top];
	s->top--;
	return OK;
}

1.5两栈共享内存

定义:在一个数组中的初始位置和末尾位置创建两个栈,两个栈共用同一个数组空间。初始时刻,数组的初始位置0可作为第一个栈的栈顶,当第一个栈不断扩大时,栈顶位置向数组后面移动,而0位置则作为栈底,数组的末尾位置可作为第二个栈的栈顶,当第二个栈数据不断扩充时,栈顶向前方移动,而数组末尾位置MAXSIZE-1则作为栈顶,如下图所示
在这里插入图片描述
另外,栈1的空栈判断条件为栈顶位置为-1,栈2的为MAXSIZE。栈满的时候,栈1与栈2挨着,所以栈2的栈顶-栈1的栈顶=1

双栈的结构代码

/*同一个数组创建两个栈,两栈共享同一个数组内存*/
typedef struct
{
	Elemtype data[MAXSIZE];
	int top1=-1;
	int top2=MAXSIZE;
}doublestack;

双栈的入栈

/*先判断栈是否满了,再选择要插入的栈,再插入数据*/
status doublestackpush(doublestack* s, Elemtype e, int stacknumber)
{
	if (s->top2 - s->top1 == 1)
		return Error;
	if (stacknumber == 1)
	{
		s->top1 += 1;
		s->data [s->top1]= e;
	}
	else if (stacknumber == 2)
	{
		s->top2 -= 1;
		s->data[s->top2] = e;
	}
	return OK;
}

双栈的出栈

/*先选择要在哪个栈里取数据,接着再判断该栈是否为空栈,若不为空栈再取数据*/
status doublestackpop(doublestack* s, Elemtype *e, int stacknumber)
{
	
	if (stacknumber == 1)
	{
		if (s->top1 == -1)
			return Error;
		(*e) = s->data[s->top1];
		s ->top1 -= 1;
	}
	else if (stacknumber == 2)
	{
		if (s->top2 == MAXSIZE)
			return Error;
		(*e) = s->data[s->top2];
		s->top1 += 1;
	}
	return OK;
}

二、 队列——一种数据遵从“先进先出”的线性表

2.2 基本概念

1.只允许在一端进行插入操作,另一端进行删除操作的线性表
2.因为队列也是线性表,所以实现队列同样需要顺序表或者链表作为实现的前提
3.队尾用来插入数据,对头用来删除(输出)数据
与栈相反,队列常用链式存储结构来实现,简称为链队列

2.3 队列相关代码操作——链式存储结构(链队列)

链队列的结构

typedef struct Qnode//定义队列中的结点
{
	Elemtype data;
	struct Qnode* next;
}Qnode, *Qnodeptr;

typedef struct//定义一个队列
{
	Qnodeptr front, tail;//链表的头结点和队尾
}LinkQ;

队列初始化——一定要先给Q->front和Q->rear分配内存,令二者相等

Q->front = Q->rear = (nodeptr)malloc(sizeof(Qnode));
	if (!Q->front) {
		printf("无内存空间可分配\n");
	}
	Q->front->next = NULL;
	printf("初始化成功\n");
	/*若直接使用Q->front=Q->rear,则会报错,因为没有分配地址,rear地址是系统随机分配的*/

入队操作——从链表尾部(队尾)插入数据

status qnodepush(LinkQ* Q, Elemtype e)
{
	Qnodeptr s=(Qnodeptr)malloc(sizeof(Qnode))
	if(!s)
		return Error;
	s->data=e;
	s->next=NULL;
	Q->tail->next=s;
	Q->tail=s
	return OK;
}

出队操作——从链表头部(队头)插入数据
建立的队列Q中,Q->front是链表的头结点,Q->front->next是队头,如下图。
在这里插入图片描述

status qnodepop(LinkQ* Q, Elemtype* e)
{
	Qnodeptr p;
	if(Q->frnot==Q->tail)
		return Error;
	p=Q->front->next;
	(*e)=p->data;
	Q->front->next=p->next;
	if(Q->rear == p)//如果队头是队尾,则删除p后,将rear指向头结点
		Q->rear = Q->front;
	free(q);
	return OK;
}

2.4 队列相关代码操作——顺序存储结构(循环队列)

循环队列结构

typedef struct
{
	Elemtype data[MAXSIZE];
	int front;//头指针
	int rear;//尾指针,若队列不为空,则指向队尾元素的下一个元素
}SqQueue;

循环队列初始化

status Initqueue(SqQueue* Q)
{
	Q->front=0;
	Q->rear=0;
	return OK;
}

求取循环队列长度

int Queuelength(SqQueue* Q)
{
	return (Q->rear-Q->front+MAXSIZE)%MAXSIZE;
}

循环队列入队操作

status queuepush(SqQueue* Q, Elemtype e)
{
	if((Q->rear+1)%MAXSIZE == Q->front)//判断循环队列是否满的标准公式
		return Error;
	Q->data[Q->rear]=e;
	Q->reat=(Q->rear+1)%MAXSIZE;//尾指针向后移动一步
	return OK;
}

循环队列出队操作

status queuepop(SqQueue* Q, Elemtype* e)
{
	if(Q->rear == Q->front)
		return Error;
	(*e)=Q->data[Q->front];
	Q->front = (Q->front+1)%MAXSIZE;//头指针向后移动一步
	return ok;
}

三、 总结

1.栈是一种“先进后出”的线性表,只允许在栈顶(表尾)进行数据的插入和删除操作
2.队列是一种“先进先出”,只允许在队尾(表尾)进行数据的插入,数据的删除只允许在队头(表头)
3.两种数据结构都有链式存储和顺序存储两种方式。对于栈来说,常用顺序存储方式,并可实现在同一个数组中定义两个栈,提高数组的利用效率;对于队列来说,由于在数据的两头分别进行插入和删除操作,所以常用链式存储结构来提高效率,另外,队列也可以使用顺序存储结构。即用循环队列的方式进行插入和删除,不需要移动数据,只需要移动头尾部指针即可,将原本的插入删除算法复杂度由O(n)变为 O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值