数据结构与算法学习笔记 栈和队列Ⅰ

Posted 临风而眠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法学习笔记 栈和队列Ⅰ相关的知识,希望对你有一定的参考价值。

数据结构与算法学习笔记(5) 栈和队列

一.栈和队列的定义和特点

  • 栈和队列是只能在表的“端点”进行插入和删除操作的线性表

    • 栈:后进先出
      • 只能在尾部插入和删除
    • 队列:先进先出
      • 只能在尾部插入,头部删除

    • 栈和队列是线性表的子集
      • 是插入和删除位置受限的线性表
  • 栈的应用

  • 队列的应用

1.栈的定义和特点

相关概念

  • 表尾( a n a_n an端)称为栈顶(Top)

  • 表头( a 1 a_1 a1端)称为栈底(Base)

  • 入栈

    • 插入元素到栈顶(即表尾)的操作
  • 出栈

    • 从栈顶(即表尾)删除最后一个元素的操作>

示意图

不可能得到cab的情况

栈与一般线性表的不同

2.队列的定义和特点

相关概念

二.案例引入

1.栈的典型案例

进制转换

  • 演示图

括号匹配的检验

  • 检验规则

表达式求值

  • 实现

2.队列的典型案例

舞伴问题

三.栈的表示和操作的实现

1.栈的抽象数据类型定义

ADT Stack{
数据对象:
	D = {ai|ai∈ElemSet,i=1,2,...,n,n≥0}
数据关系:
	R1 = {<ai-1,ai>|ai-1,ai∈D,i=2,...,n}
	约定an端为栈顶,a1端为栈底
基本操作: 初始化、进栈、出栈、取栈顶元素等
}ADT Stack

基本操作概述

  • 栈本身就是线性表,所以栈也有顺序存储和链式存储两种实现方式
    • 栈的顺序存储–顺序栈
    • 栈的链式存储–链栈

2.顺序栈的表示和实现

  • 入栈

  • 出栈

  • 顺序栈特点

#define MAXSIZE 100
typedef struct{
	SElemType *base;//栈底指针
	SElemType *top;//栈顶指针
	int stacksize; //栈可用最大容量
}SqStack;
  • 小例

顺序表的初始化
Status InitStack(SqStack &S){	//构造一个空栈
	S.base = new SElemType[MAXSIZE];
	//或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SElemType));
	if (!S.base) exit(OVERFLOW); //存储空间分配失败
	//分配成功
	S.top = S.base;	//栈顶指针等于栈底指针
	S.stacksize = MAXSIZE;
	return OK;
}
判断栈是否为空

Status StackEmpty(SqStack S){
	if(S.top == S.base){
	 	return TRUE;
	}
	else
		return FALSE;
}
求顺序栈的长度

int StackLength(SqStack S){
	return S.top - S.base;
}
清空顺序栈
Status ClearStack(SqStack S){
	if(S.base)		//如果栈不为空 就把top指针指向栈底
		S.top = S.base;
	return OK;
}
销毁顺序栈
Status DestropStack(SqStack &S){
	if(S.base){
		delete S.base;		//释放空间,没有销魂指针
		S.stacksize = 0;
		S.base = S.top = NULL;	//不置空的话会变为野指针
		
	}
	return OK;
}
顺序栈的入栈

要判断是否已经栈满

Status Push(SqStack &S,SElemType e){
	if(S.top-S.base==S.stacksize)//栈满
		return ERROR;
	*S.top = e;
	S.top++;
	//也可以合并为一个语句 *S.top++=e;
	return OK;
}
顺序栈的出栈

要注意栈空的情况

Status Pop(SqStack &S ,SElemType &e){
//若栈不空,则删除S的栈顶元素 
	if(S.top == S.base)	//等价于 if(StackEmpty(S))
		return ERROR;
	--S.top;
	e=*S.top;
	//可合并为e=*--S.top;
	return OK;
}

3.链栈的表示和实现

  • 链栈是运算受限的单链表,只能在链表头部进行操作

  • 类型定义

    typedef struct StackNode{
    	SElemType data;
    	struct StackNode *next;
    }StackNode, *LinkStack;
    LinkStack S; //指向 struct StackNode 的指针型
    

    结构类型 struct StackNode是栈的结点

链栈中的指针和普通单链表的指针方向不同

  • 链栈的特点
    • 链栈的头指针就是栈顶
    • 不需要头结点
    • 基本不存在栈满的情况
    • 空的链栈相当于头指针指向空
    • 插入和删除仅在栈顶处执行
链栈的初始化
void InitStack(LinkStack &S){
	//构造一个空栈,栈顶指针置为空
	S=NULL;
	return OK;
}
判断链栈是否为空
Status StackEmpty(LinkStack S){
	if(S==NULL) return TRUE;
	else return FALSE;
}
链栈的入栈

Status Push(LinkStack &S,SElemType e){
	p = new StackNode;	//生成新结点p
	p->data = e;	//新结点数据域置为e
	p->next = S;	//新结点插入栈顶
	S = p;	//修改栈顶指针
}
链栈的出栈

Status Pop(LinkStack &S,SElemType &e){
	if(S==NULL) return ERROR;
	e = S->data;
	p = S;
	S= S->next;
	delete p;
	return OK;
}
取栈顶元素
SElemType GetTop(LinkStack S){
	if(S!=NULL)
		return S->data;
}

四.队列的表示和操作的实现

1.队列的抽象数据类型定义

  • 队列的两种物理存储方式

    • 队列的物理存储可以用顺序存储结构,也可以用链式存储结构
    • 顺序队列链式队列
  • 队列的顺序表示 :用一维数组base[MAXQSIZE]

    #define MAXQSIZE 100 //最大队列长度
    Typedef struct{
    	QElemType *base; //初始化的动态分配存储空间
        //数据的元素类型QElemType,base指针指向数组的首元素, 之后可以分配空间
    	int front; //用于表示队头元素的下标
    	int rear;  //用于表示队尾元素的下标
    }SqQueue;
    

2.队列的顺序表示和实现(顺序队)

一般顺序队的问题

图中问题:没法入队了

上图中的假溢出就是说,其实数组中还有存储空间,但是却没法入队了,

解决办法:循环队列

区别队空队满

循环队列解决队满时判断方法–少用一个元素空间

循环队列的类型定义
#define MAXQSIZE 100 //最大队列长度
typedef struct{
	QElemType *base; //动态分配存储空间
	int front;	//头指针(不是真的指针,就是表示头元素下标)  若队列不空,指向队列头元素
	int rear;	//尾指针(不是真的指针),若队列不空,指向队列尾元素的下一个位置
	
}SqQueue;
循环队列的操作
队列初始化
Status InitQueue(SqQueue &Q){
	Q.base = new QElemType[MAXQSIZE]	//分配数组空间
	//C语法 Q.base=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
    //Q.base是指针,数组首元素的地址就是指针
	if(!Q.base) exit(OVERFLOW);	//存储分配失败
    Q.front=Q.rear=0;	//分配成功,头指针尾指针置为0,队列为空
    
}
求队列长度

第一个图可以直接用Q.rear-Q.front, 第二张就不能直接减了

int QueueLength (SqQueue Q){
	return((Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}
循环队列入队

牺牲了一个存储空间的情况下(少用一个元素空间解决队空队满判断)

Status EnQueue(SqQueue &Q, QElemType e){
	if((Q.rear+1)%MAXSIZE == Q.front) return ERROR;	//队满
	Q.base[Q.rear]=e;	//新元素加入队尾
    Q.rear=(Q.rear+1)%MAXQSIZE;		//尾指针+1取模
    return OK;
}
循环队列出队

Status DeQueue (SqQueue &Q,QElemType &e){
	if(Q.front == Q.rear)	return ERROR;	//队空
	e = Q.base[front];	//保存队头元素
    Q.front = (Q.front+1)%MAXQSIZE;	//队头指针+1
    return OK;
}
取队头元素
SElemType GetHead(SqQuere Q){
	if(Q.front!=Q.rear)		//队列不为空
		return Q.base[Q.front];	//返回队头指针元素的值,队头指针不变
}

3.队列的链式表示和实现(链队)

链队列的类型定义

#define MAXQSIZE 100 //最大队列长度
typedef struct Qnode{
	QElemType data;
	struct Qnode *next;
}QNode,*QueuePtr;

pointer简写ptr

typedef struct{
	QuenePtr front;	//队头指针
	QuenePtr rear;	//队尾指针
}LinkQueue; //链式队列

链队列运算指针变化状况

链队列的操作

链队列的初始化

Status InitQueue(LinkQueue &Q){
	Q.front = Q.rear =(QueuePtr)malloc(sizeof(QNode));
	if(!Q.front) exit(OVERFLOW);
	Q.front->next=NULL;
	return OK;
}
链队列的销毁
  • 算法思想:

    从队头结点开始,依次释放所有结点

  • 算法描述

    Status DestroyQueue(LinkQueue &Q){
    	while(Q.front){
    		p=Q.front->next;
    		free(Q.front);	//释放结点的那块空间
    		Q.front=p;
            //p那里可以用尾指针来代替
            //Q.rear=Q.front->next;
            //free(Q.front);
            //Q.front=Q.rear;
    	}
    	retrun OK;
    }
    
元素e入队

Status EnQueue(LinkQueue &Q,QElemType e){
	p=(QueuePtr)malloc(sizeof(QNode));
	if(!p) exit(OVERFLOW);
	//空间分配成功
    p->data=e;
    p->next=NULL;
    Q.rear->next=p;
    Q.rear=p;
    return OK;
}
链队列出队

p指向头结点的下一元素

Status DeQueue(LinkQueue &Q,QElemType &e){
	if(Q.front==Q.rear) return ERROR;
	p=Q.front->next;
	e=p->data;
	Q.front->next=p->next;
	if(Q.rear==p) Q.rear=Q.front; //如果删除的是尾结点
	delete p;
	return OK;
}
取链队列的队头元素
Status GetHead(LinkQueue Q,QElemType &e){
	if(Q.front==Q.rear) return ERROR;
	e=Q.front->next->data;
    return OK;
}
if(!p) exit(OVERFLOW);
//空间分配成功
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return OK;

}

以上是关于数据结构与算法学习笔记 栈和队列Ⅰ的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法学习笔记:线性表Ⅰ

博客作业03--栈和队列

浅谈算法和数据结构: 一 栈和队列

《算法导论》读书笔记

表栈和队列

考研数据结构与算法栈和队列