数据结构知识点3-操作受限的线性表:栈和队列

栈的概念

栈:限定仅在表尾进行插入和删除操作的线性表。
空栈:不含任何元素的栈。
允许插入和删除的一端称为栈顶,另一端称为栈底。

在这里插入图片描述
栈的操作特性:后进先出。

顺序栈

在这里插入图片描述

顺序栈的类型定义

const int MAXSIZE = 100;
typedef char ElemType;
typedef struct
{
	ElemType data[MAXSIZE];
	int top;//top 不是一个指针变量
}SqStack;

顺序栈的实现——初始化

void InitStack(SqStack &S)
{
	S.top = -1;//将top放置在-1的位置,表示表内无元素
}

顺序栈的实现——入栈

void Push(SqStack &S, ElemType e)
{
	if (S.top == MAXSIZE-1)
		{cout<<"栈已满"<<endl; 
		exit(0);}
	S.top ++;
	S.data[S.top] = e;
}

顺序栈的实现——出栈

ElemType Pop(SqStack &S)
{
	if (S.top == -1)
	{cout<<"栈已空"<<endl;
	exit(0);}
	ElemType x = S.data[S.top];
	S.top --;
	return x;
}

顺序栈的实现——取栈顶元素

ElemType GetTop(SqStack &S)
{
	if (S.top == -1)
	{
	cout<<"栈空"<<endl;
	exit(0);}
	return S.data[S.top];
}

顺序栈的实现——判断栈空

bool StackEmpty(SqStack &S)
{
	return S.top == -1;
}

顺序栈的实现——判断栈满

bool StackFull(SqStack &S)
{
	return S.top == MAXSIZE - 1;
}

链栈

栈的链式存储及基本操作

在这里插入图片描述
将哪一端作为栈顶?将链头作为栈顶,方便操作。
链栈需要加头结点吗?链栈不需要附设头结点。

链栈的类型定义

typedef char ElemType;
typedef struct
{
	ElemType data;
	Node *next;
}Node, LinkStack*;

链栈的实现——初始化

void InitStack(LinkStack &L)
{
	L = NULL;
}

链栈的实现——入栈

ElemType Push(LinkStack &L, ElemType e)
{
	LinkStack p = new Node;
	p->data = e;
	p->next = L;
	L = p;
}

链栈的实现——出栈

ElemType Pop(LinkStack &S)
{
	if(S==NULL){
		cout<<"空"<<endl;
		exit(1);}
	ElemType x = S->data;
	LinkStack p = S;
	S = S->next;
	delete p;
	return x;
}

链栈的实现——取栈顶元素

ElemType GetTop(LinkStack &S)
{
	if(S == NULL)
	{
		cout<<"栈空"<<endl;
		exit(1);
	}
	return S->data;
}

链栈的实现——判断栈空

bool StackEmpty(LinkStack &S)
{
	return S == NULL;
}

链栈的实现——销毁

void Destroy(LinkStack &S)
{
	while(S){
	LinkStack p = S;
	S = S->next;
	delete p;}
}

顺序栈和链栈的比较

时间性能:相同,都是常数时间O(1)。
空间性能:

  • 顺序栈:有元素个数的限制和空间浪费的问题。
  • 链栈:没有栈满的问题,只有当内存没有可用空间时才会出现栈满。但是每个元素都需要一个指针域,从而产生了结构性开销。

因此,当栈使用过程中元素变化较大时,用链栈比较合适,反之,应该采用顺序栈。

例题:十进制转八进制

const int MAXSIZE = 100;
typedef struct 
{
	ElemType data[MAXSIZE];
	int top;
}SeqStack;
void conversion(int n)
{
	SqStack S;
	InitStack(S);
	while(n)
	{
		Push(S, n%8);
		n = n/8;
	}
	while(!StackEmpty(S))
	{
		cout<<Pop(S);
	}
	cout<<endl;
}

循环队列

在这里插入图片描述
只允许在一端进行插入操作,而另一端进行删除操作的线性表。
操作特性:先进先出。

实现方法:

  • 顺序存储:用一维数组 用顺序存储队列是因为数组是静态申请的,有长度限制,也就有了假上溢问题。
  • 链式存储:用链表 节点时动态申请的,没有长度限制,于是没有假上溢问题;同时因为队列要遵循进队和出队的规则,于是链表的插入删除的优势没有发挥。

在这里插入图片描述
其中,

  1. 两个指针有指示作用
    • front指向队头存放元素的前一个位置;
    • rear指向当前的队尾元素位置。
  2. 入队:rear+1,写入数据;
    出队:front+1,读出数据。
  3. 如果rear追到front说明队满,如果front追到rear说明队空,但此时如果状态都是front==rear,就存在二义性:牺牲一个存储单元。
    在这里插入图片描述

三种解决方案:

  1. 令队列中的一个单元闲置,使得队列非空时,rear和front之间至少间隔一个空闲单元,但是也浪费了一个元素空间。
  2. 设置一个辅助标志flag,有元素出队列时,flag=false,入队列时,flag=true。则当rear=front && flagtrue时,队满;当rear=front && flagfalse时,队空。
  3. 使用一个计数器num记录队列中的元素个数:num==0表示空队列,每次进队num++,每次出队num–,当num=MAXSIZE时为满队。

在这里插入图片描述

  • 队满:当队列添加元素到rear的下面第二个元素时front时,转圈子要碰头了,队满 (rear+1)%MAXSIZE == front
  • 队空:当队列删除元素到front == rear时,队空。

循环队列——构建

const int MAXSIZE = 100;
typedef char ElemType;
typedef struct 
{
	ElemType data[MAXSIZE];
	int front, rear;
}SqQueue

循环队列——初始化

void InitQueue(SqQueue &Q)
{
	Q.rear = Q.front = 0;
}

循环队列——入队

ElemType Push(SqQueue &Q, ElemType e)
{
	if((Q.rear+1) % MAXSIZE == Q.front)
		{
			cout<<"队满"<<endl;
			exit(1);
		}
	Q.rear = (Q.rear + 1) % MAXSIZE;
	Q.data[rear] = e;
}

循环队列——出队

ElemType Pop(SqQueue &Q)
{
	if(Q.rear == Q.front)
	{
		cout<<"队空"<<endl;
		exit(1);
	}
	Q.front = (Q.front + 1)%MAXSIZE;
	ElemType x = Q.data[front];
	return x;
}

循环队列——判断是否为空

bool QueueEmpty(SqQueue &Q)
{
	return Q.rear == Q.front;
}

循环队列——判断是否为满

bool QueueFull(SqQueue &Q)
{
	return (Q.rear + 1)%MAXSIZE == Q.front;
}

链队列

在这里插入图片描述
跟单链表一样,队头指针为链表的头指针,不装数据(和链栈不同)。
在这里插入图片描述

链队列——构造

typedef char ElemType;
typedef struct Node
{
	ElemType data;
	struct Node *next;
}Node;
typedef struct 
{
	Node *front;
	Node *rear;
}LinkQueue;

链队列——初始化

void InitQueue(LinkQueue &Q)
{
	Q.rear = Q.front;
}

链队列——入队

ElemType EnQueue(LinkQueue &Q, ElemType e)
{
	Node *eNode = new Node;
	eNode->data = e;
	eNode->next = NULL;
	Q.rear->next = eNode;
	Q.rear = eNode;
}

链队列——出队

ElemType DeQueue(LinkQueue &Q)
{
	if(Q.front == Q.rear)
	{
		cout<<"空"<<endl;
		exit(1);
	}
	Node *p = Q.front->next;
	ElemType x = p->data;
	Q.front->next = p->next;
	if(Q.rear == p)
		Q.rear = Q.front;
	delete p;
	return x;
}

链队列——判断队空

bool QueueEmpty(LinkQueue &Q)
{
	return Q.rear == Q.front;
}

链队列——销毁

void Destroy(LinkQueue &Q)
{
	while(Q.front){
	Q.rear = Q.front->next;
	delete Q.front;
	Q.front = Q.rear;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值