目录
前言
每接触一种数据结构类型,就要从逻辑结构,物理结构,数据的运算这三个方面进行考虑,深入理解定义
一、栈的定义和特点
栈(stack)是只允许在一端进行插入或删除操作的线性表。
- 栈顶(top):允许插入删除的一端(一般是表尾)
- 栈底(bottom):不允许插入删除的一端(一般是表头)
- 空栈:不含任何元素的空表。
- 原则:后进先出 (Last In First Out, LIFO) 。
可以类比叠盘子。
超纲了解:卡特兰数
一个n个元素皆不相同的序列,给定入栈顺序,但出栈与入栈同时进行,出栈的排列总共有(卡特兰数Catalan)。
问题:入栈顺序a->b->c->d->f, 在入栈的同时出栈,哪些出栈顺序是合法的?
二、栈的逻辑结构以及基本操作
2.1 用抽象数据类型来定义栈的数据结构
2.2 顺序栈的定义及其特点
顺序栈是指利用顺序存储结构实现的栈。
即利用一组地址连续的存储单元依次存放自栈底到 栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。
2.3 顺序存储结构对栈基本操作的实现
与顺序表类似,顺序栈类型的表示方法有两种:静态分配和动态分配。静态分配:用静态的“数组”存放数据;动态分配:用指针指向一片连续的区域。
栈顶用从0记的话初始化top为-1;
栈顶用从1记的话初始化top为0;
声明顺序栈类型: 静态分配方式 |
#define Maxsize 10 //定义栈中最多元素 typedef struct { int data[Maxsize]; //存放栈中元素 int top;// 栈顶指针 }SStack; |
静态分配方式,大小固定不变; 顺序栈;栈顶指针类型与数据域类型相同。 |
//初始化 void InitStack(SStack *S){ S.top=-1; } |
// 初始化2 void InitStack(SStack *S){ S.top = 0; } |
由于data[i] (0<=i<=Maxsize-1) 数据都是有效的所以这里初始化top为-1, |
// 清空栈 bool ClearStack(SStack *S){ S.top=-1; return true; } |
// 清空栈 bool ClearStack(SStack *S){ S.top=0; return true; } |
如果top初始化为0,那么top指向的就是下一个可以插入的位置。也就是top[0]可入栈。 |
//进栈 bool Push(SStack *S,int x){ if(S.top==Maxsize-1) return false;// 栈满,报错 S.data[++S.top] = x; return true; } |
bool Push(SStack *S,int x){ // 栈满,报错 if(S.top==Maxsize) return false; S.data[S.top++]=x; return true; } |
|
// 出栈 bool Pop(SStack *S,int &x){ if(S.top==-1) return false;// 栈空,报错 x = S.data[S.top--]; return true; } |
bool Pop(SStack *S,int &x){ // 栈空报错 if(S.top==0) return false; x = S.data[--S.top]; return true; } |
|
// 判断栈空 bool StackEmpty(SStack S){ if(S.top==-1) return true; else return false; } |
// 判断栈空 bool StackEmpty(SStack S){ if(S.top==0) return true; else return false; } |
共享栈:利用栈底位置相对不变的特性,可以让两个顺序栈共享一个数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶项共享空间的中间延伸。
栈满的标志是top0==top1-1;
2.4 链栈的定义及其特点
链栈:采用链式存储方式实现的栈。
链栈的优点是:便于多个栈共享存储空间和提高其效率,且不存在栈满溢出的情况。
链栈的内容与链表相似,但也都有带头结点,与不带头结点的区别,注意区别。
2.5 链式存储结构对栈基本操作的实现
暂略。
三、队列的定义和特点
队列:只允许在一端进行插入,而在另一端进行删除的线性表。
- 队尾(rear):允许插入的一端;
- 队头(front):允许删除的一端;
- 空队列:
- 原则(特点,先进先出):先进先出(First In First Out,FIFO)。
四、队列的逻辑结构以及基本操作
4.1 用抽象数据类型来定义队列的数据结构
4.2 顺序队列的定义及其特点
在队列的顺出存储结构中,除了用一组地址连续的存储单元一次存放从队头到队尾的元素外,还需要附设连个整形变量front和rear分别指示队头元素和队尾元素的位置(后面分别称为头指针和尾指针)。
4.3 顺序存储结构对队列基本操作的实现
声明队列类型: 静态数组形式 |
#define Maxsize 10 typedef struct{ int data[Maxsize]; int front; int rear; }SQ; |
|
初始化队列 |
void InitQ(SQ *Q){ Q->front = 0;// 指向队头 Q->rear = 0;// 队尾指向下一个要插入的位置(下一个应该插入的位置) } |
|
队列是否为空 |
bool Empty(SQ Q){ if(Q.front==Q.rear) return true; else return false; } |
|
入队 |
bool EnQueue(SQ *Q,int x){ if((Q->rear+1)%Maxsize==Q.front) return false; Q->data[Q.rear]=x;//插入队尾 Q->rear=(Q->rear+1)%Maxsize;//队尾指针后移 return true; } 因为这里判断队列是否为满的方法是:当前队尾(下一个要插入的位置)的下一节点是否为队头,容易注意到这里Q->rear是下一个要插入的位置,然而如果我们直接用Q->rear%Maxsize==Q->front去判断是否为满,因为这个判断方式与判断队空是等价的,所以会产生冲突,所以就选择了牺牲循环队列队头的前一个元素的空间,(Q->rear+1)%Maxsize==Q.front,如果这个下一个要插入元素的下一个元素是队头,那么我们就不能入队,也就是下一个的下一个如果是队头,所以实际上最多只能存放9个有效数据。 |
由于队列的特性,可能即使队尾是Maxsize也是非空。 利用取余运算,模运算将无限的整数域映射到有限的整数集合上{0,1,2,...,b-1}上。 {0,1,2,..,Maxsize-1}将存储空间在逻辑上变成了“环状”。 如果Q->rear==9那么(Q->rear+1)%Maxsize==0 即使(Q->rear+1)%Maxsize==0,也不能说明是队满,因为队列的环状结构,让队头的位置可能已经不再是0下标的位置 所以下一个要插入的位置如果是队头Q.front,那么这就满了。 所以队列不像栈一样,将值设为-1,而是将Q.front直接指向第一个结点。再看左边是关键:为什么会浪费一个结点? |
出队 |