数据结构期末复习(第三章 栈与队列)
个人感觉,这一章的内容不算特别难,主要是对上一章的链表之类的思想加以巩固;如果链表什么的没搞懂,学起来还是特别痛苦。不过学着学着也就悟了,不用太焦虑,我也是学着学着才慢慢摸索懂的。 说在前面,知识点总结中的代码一定一定一定要手敲一遍,过程中一定会遇到各种各样的问题,只看不动手很容易眼高手低! 还有,代码都是伪代码,提供思路用的,不要照着抄 原理之类的看不懂,其实敲一遍就理解了,实践出真知。Part 1、知识点总结
栈和队列是两种应用非常广泛的数据结构,它们都来自线性表数据结构,都是“操作受限”的线性表。
栈在计算机的实现有多种方式:
◆ 硬堆栈:利用CPU中的某些寄存器组或类似的硬件或使用内存的特殊区域来实现。这类堆栈容量有限,但速度很快;
◆ 软堆栈:这类堆栈主要在内存中实现。堆栈容量可以达到很大。在实现方式上,又有动态方式和静态方式两种。
1.1.0 栈和队列的基本概念
栈和队列是两种特殊的线性表,它们的逻辑结构和线性表相同,只是其访问规则较线性表有更多的限制,故又称它们为访问受限的线性表
1.1.1 栈的基本概念
- 栈(Stack):是限制在表的一端进行插入和删除操作的线性表。又称为后进先出LIFO (Last In First Out)或先进后出FILO (First In Last Out)线性表。
- 栈顶(Top):允许进行插入、删除操作的一端,又称为表尾。用栈顶指针(top)来指示栈顶元素。
- 栈底(Bottom):是固定端,又称为表头。
- 空栈:当表中没有元素时称为空栈。
1.1.2 栈的顺序存储表示
栈的顺序存储结构简称为顺序栈,和线性表相类似,用一维数组来存储栈。根据数组是否可以根据需要增大,又可分为静态顺序栈和动态顺序栈。
◆ 静态顺序栈实现简单,但不能根据需要增大栈的存储空间;
◆ 动态顺序栈可以根据需要增大栈的存储空间,但实现稍为复杂。
1.1.2.1 栈的动态顺序存储表示
◆ 采用动态一维数组来存储栈。所谓动态,指的是栈的大小可以根据需要增加。
◆ 用bottom表示栈底指针,栈底固定不变的;栈顶则随着进栈和退栈操作而变化。用top(称为栈顶指针)指示当前栈顶位置。
◆ 用top=bottom作为栈空的标记,每次top指向栈顶数组中的下一个存储位置。
◆ 结点进栈:首先将数据元素保存到栈顶(top所指的当前位置),然后执行top加1,使top指向栈顶的下一个存储位置;
◆ 结点出栈:首先执行top减1,使top指向栈顶元素的存储位置,然后将栈顶元素取出
1.1.2.2 基本实现
- 栈的类型定义
#define STACK_SIZE 100 /* 栈初始向量大小 */
#define STACKINCREMENT 10 /* 存储空间分配增量 */
#typedef int ElemType ;
typedef struct sqstack
{ ElemType *bottom; /* 栈不存在时值为NULL */
ElemType *top; /* 栈顶指针 */
int stacksize ; /* 当前已分配空间,以元素为单位 */
}SqStack ;
- 栈的初始化
Status Init_Stack(void)
{ SqStack S ;
S.bottom=(ElemType *)malloc(STACK_SIZE *sizeof(ElemType));
if (! S.bottom) return ERROR;
S.top=S.bottom ; /* 栈空时栈顶和栈底指针相同 */
S. stacksize=STACK_SIZE;
return OK ;
}
- 压栈(元素进栈)
Status push(SqStack S , ElemType e)
{ if (S.top-S.bottom>=S. stacksize-1)
{ S.bottom=(ElemType *)realloc((S. STACKINCREMENT+STACK_SIZE) *sizeof(ElemType)); /* 栈满,追加存储空间 */
if (! S.bottom) return ERROR;
S.top=S.bottom+S. stacksize ;
S. stacksize+=STACKINCREMENT ;
}
*S.top=e; S.top++ ; /* 栈顶指针加1,e成为新的栈顶 */
return OK;
}
- 弹栈(元素出栈)
Status pop( SqStack S, ElemType *e )
/*弹出栈顶元素*/
{ if ( S.top== S.bottom )
return ERROR ; /* 栈空,返回失败标志 */
S.top-- ; e=*S. top ;
return OK ;
}
1.1.2.3 栈的静态顺序存储表示
采用静态一维数组来存储栈。
栈底固定不变的,而栈顶则随着进栈和退栈操作变化的,
◆ 栈底固定不变的;栈顶则随着进栈和退栈操作而变化,用一个整型变量top(称为栈顶指针)来指示当前栈顶位置。
◆ 用top=0表示栈空的初始状态,每次top指向栈顶在数组中的存储位置。
◆ 结点进栈:首先执行top加1,使top指向新的栈顶位置,然后将数据元素保存到栈顶(top所指的当前位置)。
◆ 结点出栈:首先把top指向的栈顶元素取出,然后执行top减1,使top指向新的栈顶位置。
1.1.2.4 基本操作的实现
- 栈的类型定义
# define MAX_STACK_SIZE 100 /* 栈向量大小 */
# typedef int ElemType ;
typedef struct sqstack
{ ElemType stack_array[MAX_STACK_SIZE] ;
int top;
}SqStack ;
- 栈的初始化
SqStack Init_Stack(void)
{ SqStack S ;
S.bottom=S.top=0 ; return(S) ;
}
- 压栈(元素进栈)
Status push(SqStack S , ElemType e)
/* 使数据元素e进栈成为新的栈顶 */
{ if (S.top==MAX_STACK_SIZE-1)
return ERROR; /* 栈满,返回错误标志 */
S.top++ ; /* 栈顶指针加1 */
S.stack_array[S.top]=e ; /* e成为新的栈顶 */
return OK; /* 压栈成功 */
}
- 弹栈(元素出栈)
Status pop( SqStack S, ElemType *e )
/*弹出栈顶元素*/
{ if ( S.top==0 )
return ERROR ; /* 栈空,返回错误标志 */
*e=S.stack_array[S.top] ;
S.top-- ;
return OK ;
}
1.1.2.5 注意
当栈满时做进栈运算必定产生空间溢出,简称“上溢”。上溢是一种出错状态,应设法避免。
当栈空时做退栈运算也将产生溢出,简称“下溢”。下溢则可能是正常现象,因为栈在使用时,其初态或终态都是空栈,所以下溢常用来作为控制转移的条件。
1.2.2 链栈
栈的链式存储结构称为链栈,是运算受限的单链表。其插入和删除操作只能在表头位置上进行。因此,链栈没有必要像单链表那样附加头结点,栈顶指针top就是链表的头指针。
注意!不是偷懒不是偷懒不是偷懒!链栈几乎和之前的单链表一模一样,定义上几乎只是改个名字,操作上也没什么可多说的,会写就行!我这可不是偷懒嗷!不是!
1.3 栈与递归调用的实现
栈的另一个重要应用是在程序设计语言中实现递归调用。
递归调用:一个函数(或过程)直接或间接地调用自己本身,简称递归(Recursive)。
递归是程序设计中的一个强有力的工具。因为递归函数结构清晰,程序易读,正确性很容易得到证明。
为了使递归调用不至于无终止地进行下去,实际上有效的递归调用函数(或过程)应包括两部分:递推规则(方法),终止条件。
为保证递归调用正确执行,系统设立一个“递归工作栈”,作为整个递归调用过程期间使用的数据存储区。
每一层递归包含的信息如:参数、局部变量、上一层的返回地址构成一个“工作记录” 。每进入一层递归,就产生一个新的工作记录压入栈顶;每退出一层递归,就从栈顶弹出一个工作记录。
从被调函数返回调用函数的一般步骤:
- 若栈为空,则执行正常返回。
- 从栈顶弹出一个工作记录。
- 将“工作记录”中的参数值、局部变量值赋给相应的变量;读取返回地址。
- 将函数值赋给相应的变量。
- 转移到返回地址。
2.1.0 队列
只允许在一端插入,在另一端删除的线性表(感受一下什么叫受限的线性表)
2.1.1 队列的基本概念
队列(Queue):也是运算受限的线性表。是一种先进先出(First In First Out ,简称FIFO)的线性表。只允许在表的一端进行插入,而在另一端进行删除。
队首(front) :允许进行删除的一端称为队首。
队尾(rear) :允许进行插入的一端称为队尾。
例如:排队购物。操作系统中的作业排队。先进入队列的成员总是先离开队列。
队列中没有元素时称为空队列。在空队列中依次加入元素a1, a2, …, an之后,a1是队首元素,an是队尾元素。显然退出队列的次序也只能是a1, a2, …, an ,即队列的修改是依先进先出的原则进行的
2.2.2 队列的顺序表示和实现
利用一组连续的存储单元(一维数组) 依次存放从队首到队尾的各个元素,称为顺序队列。
对于队列,和顺序栈相类似,也有动态和静态之分.
类型定义如下:
#define MAX_QUEUE_SIZE 100
typedef struct queue
{ ElemType Queue_array[MAX_QUEUE_SIZE] ;
int front ;
int rear ;
}SqQueue;
设立一个队首指针front ,一个队尾指针rear ,分别指向队首和队尾元素。
◆ 初始化:front=rear=0。
◆ 入队:将新元素插入rear所指的位置,然后rear加1。
◆ 出队:删去front所指的元素,然后加1并返回被删元素。
◆ 队列为空:front=rear。
◆ 队满:rear=MAX_QUEUE_SIZE-1或front=rear。
在非空队列里,队首指针始终指向队头元素,而队尾指针始终指向队尾元素的下一位置。
顺序队列中存在“假溢出”现象。因为在入队和出队操作中,头、尾指针只增加不减小,致使被删除元素的空间永远无法重新利用。因此,尽管队列中实际元素个数可能远远小于数组大小,但可能由于尾指针巳超出向量空间的上界而不能做入队操作。该现象称为假溢出。如图3-6所示是数组大小为5的顺序队列中队首、队尾指针和队列中元素的变化情况。
2.2.3 循环队列
为充分利用向量空间,克服上述“假溢出”现象的方法是:将为队列分配的向量空间看成为一个首尾相接的圆环,并称这种队列为循环队列(Circular Queue)。
在循环队列中进行出队、入队操作时,队首、队尾指针仍要加1,朝前移动。只不过当队首、队尾指针指向向量上界(MAX_QUEUE_SIZE-1)时,其加1操作的结果是指向向量的下界0。
这种循环意义下的加1操作可以描述为:
if (i+1==MAX_QUEUE_SIZE) i=0;
else i++ ;
其中: i代表队首指针(front)或队尾指针(rear)
用模运算可简化为:
i=(i+1)%MAX_QUEUE_SIZE ;
· 显然,为循环队列所分配的空间可以被充分利用,除非向量空间真的被队列元素全部占用,否则不会上溢。因此,真正实用的顺序队列是循环队列。
入队时尾指针向前追赶头指针,出队时头指针向前追赶尾指针,故队空和队满时头尾指针均相等。因此,无法通过front=rear来判断队列“空”还是“满”。解决此问题的方法是:约定入队前,测试尾指针在循环意义下加1后是否等于头指针,若相等则认为队满。
◆ rear所指的单元始终为空。
◆ 循环队列为空:front=rear 。
◆ 循环队列满:(rear+1)%MAX_QUEUE_SIZE =front。
2.2.3.1 循环队列的基本操作
1 循环队列的初始化
SqQueue Init_CirQueue(void)
{ SqQueue Q ;
Q.front=Q.rear=0; return(Q) ;
}
2 入队操作
Status Insert_CirQueue(SqQueue Q , ElemType e)
/* 将数据元素e插入到循环队列Q的队尾 */
{ if ((Q.rear+1)%MAX_QUEUE_SIZE== Q.front)
return ERROR; /* 队满,返回错误标志 */
Q.Queue_array[Q.rear]=e ; /* 元素e入队 */
Q.rear=(Q.rear+1)% MAX_QUEUE_SIZE ;
/* 队尾指针向前移动 */
return OK; /* 入队成功 */
}
3 出队操作
Status Delete_CirQueue(SqQueue Q, ElemType *x )
/* 将循环队列Q的队首元素出队 */
{ if (Q.front+1== Q.rear)
return ERROR ; /* 队空,返回错误标志 */
*x=Q.Queue_array[Q.front] ; /* 取队首元素 */
Q.front=(Q.front+1)% MAX_QUEUE_SIZE ;
/* 队首指针向前移动 */
return OK ;
}
2.2.4 队列的链式表示和实现
队列的链式存储结构简称为链队列,它是限制仅在表头进行删除操作和表尾进行插入操作的单链表。
需要两类不同的结点:数据元素结点,队列的队首指针和队尾指针的结点。
数据元素结点类型定义:
typedef struct Qnode
{ ElemType data ;
struct Qnode *next ;
}QNode ;
指针结点类型定义:
typedef struct link_queue
{ QNode *front , *rear ;
}Link_Queue ;
2.2.4.1 链队运算及指针变化
链队的操作实际上是单链表的操作,只不过是删除在表头进行,插入在表尾进行。插入、删除时分别修改不同的指针。
2.2.4.2 链队列的基本操作
⑴ 链队列的初始化
LinkQueue *Init_LinkQueue(void)
{ LinkQueue *Q ; QNode *p ;
p=(QNode *)malloc(sizeof(QNode)) ; /* 开辟头结点 */
p->next=NULL ;
Q=(LinkQueue *)malloc(sizeof(LinkQueue)) ;
/* 开辟链队的指针结点 */
Q.front=Q.rear=p ;
return(Q) ;
}
⑵ 链队列的入队操作
在已知队列的队尾插入一个元素e ,即修改队尾指针(Q.rear)。
Status Insert_CirQueue(LinkQueue *Q , ElemType e)
/* 将数据元素e插入到链队列Q的队尾 */
{ p=(QNode *)malloc(sizeof(QNode)) ;
if (!p) return ERROR;
/* 申请新结点失败,返回错误标志 */
p->data=e ; p->next=NULL ; /* 形成新结点 */
Q.rear->next=p ; Q.rear=p ; /* 新结点插入到队尾 */
return OK;
}
⑶ 链队列的出队操作
Status Delete_LinkQueue(LinkQueue *Q, ElemType *x)
{ QNode p ;
if (Q.front==Q.rear) return ERROR ; / 队空 /
p=Q.front->next ; / 取队首结点 */
x=p->data ;
Q.front->next=p->next ; / 修改队首指针 /
if (p==Q.rear) Q.rear=Q.front ;
/ 当队列只有一个结点时应防止丢失队尾指针 */
free§ ;
return OK ;
}
⑶ 链队列的出队操作
Status Delete_LinkQueue(LinkQueue *Q, ElemType *x)
{ QNode *p ;
if (Q.front==Q.rear) return ERROR ; /* 队空 */
p=Q.front->next ; /* 取队首结点 */
*x=p->data ;
Q.front->next=p->next ; /* 修改队首指针 */
if (p==Q.rear) Q.rear=Q.front ;
/* 当队列只有一个结点时应防止丢失队尾指针 */
free(p) ;
return OK ;
}
⑷ 链队列的撤消
void Destroy_LinkQueue(LinkQueue *Q )
/* 将链队列Q的队首元素出队 */
{ while (Q.front!=NULL)
{ Q.rear=Q.front->next;
/* 令尾指针指向队列的第一个结点 */
free(Q.front); /* 每次释放一个结点 */
/* 第一次是头结点,以后是元素结点 */
Q.ront=Q.rear;
}
}
3 关键问题
- 栈虽然是一种后进先出的顺序存取结构,但是顺序存取和顺序存储并不一样;顺序存取指只能存或逐个取结构中的元素,数据存储是指利用一个连续的空间相继存放结构中的元素。
PPT课后题
1 设有一个栈,元素进栈的次序为a, b, c。问经过栈操作后可以得到哪些输出序列?
2 循环队列的优点是什么?如何判断它的空和满?
3 设有一个静态顺序队列,向量大小为MAX,判断队列为空的条件是什么?队列满的条件是什么?
4 设有一个静态循环队列,向量大小为MAX,判断队列为空的条件是什么?队列满的条件是什么?
5 利用栈的基本操作,写一个返回栈S中结点个数的算法int StackSize(SeqStack S) ,并说明S为何不作为指针参数的算法?
6 一个双向栈S是在同一向量空间内实现的两个栈,它们的栈底分别设在向量空间的两端。试为此双向栈设计初始化InitStack(S) ,入栈Push(S,i,x),出栈Pop(S,i,x)算法,其中i为0或1 ,用以表示栈号。
7 设Q[0,6]是一个静态顺序队列,初始状态为front=rear=0,请画出做完下列操作后队列的头尾指针的状态变化情况,若不能入对,请指出其元素,并说明理由。
a, b, c, d入队
a, b, c出队
i , j , k , l , m入队
d, i出队
n, o, p, q, r入队
8 假设Q[0,5]是一个循环队列,初始状态为front=rear=0,请画出做完下列操作后队列的头尾指针的状态变化情况,若不能入对,请指出其元素,并说明理由。
d, e, b, g, h入队
d, e出队
i , j , k , l , m入队
b出队
n, o, p, q, r入队
自己康的一点题
- 当利用大小为n的数组顺序存储一个栈时,假定用top == n表示栈空,则向这个栈插入一个元素时,首先应执行 top-- 语句修改top指针。
- 循环队列判断队满条件:Q.front == (Q.rear + 1)% maxsize
- 设循环队列的存储容量为maxsize,队头和队尾指针分别为front和rear。若有一个循环队列Q,可用(Q.rear - Q.front + maxsize)%maxsize 来计算元素个数
- 将递归算法转换成对应的非递归算法时,除了单项递归和尾递归的情况外,通常需要使用栈保存中间结果
- 一个递归算法必须包括结束条件和递归部分
- 栈和队列共同点是允许在端点处进行操作线性表
Part 2、代码
//共享栈
#include "stdio.h"
#define Maxsize 20
#define error -1
#define ok 1
typedef int ElemType;
typedef struct{
ElemType data[Maxsize];
int top0;
int top1;
}shstack;
void InitStack(shstack *s)
{
s->top0=-1;
s->top1=Maxsize;
}
int Push(shstack *s,int i,ElemType x)
{
if(s->top1==s->top0+1)
return error;
if(i==0)
{
s->top0++;
s->data[s->top0]=x;
}
else if(i==1)
{
s->top1--;
s->data[s->top1]=x;
}
else return error;
return ok;
}
int Pop(shstack *s,int i,ElemType x)
{
if(i==0)
{
if(s->top0==-1)
return error;
}
else if(i==1)
{
if(s->top1==Maxsize)
return error;
}
else return error;
if(i==0)
{
x=s->data[s->top0];
s->top0--;
}
else
{
x=s->data[s->top1];
s->top1++;
}
printf("The number popped is %d\n",x);
return ok;
}
int main()
{
shstack s;
int tmp,i,x,boolean;
InitStack(&s);
printf("The stack has been created successfully!\n");
printf("0->End the program.\n");
printf("1->Push new numbers.\n");
printf("2->Pop numbers.\n");
while(1)
{
scanf("%d",&tmp);
if(tmp==0) break;
else if(tmp==1)
{
printf("Please input where you want insert and the number you want to push:\n");
scanf("%d %d",&i,&x);
boolean=Push(&s,i,x);
if(boolean==error) printf("ERROR!\n");
else
printf("Pushed successfully!\n");
}
else if(tmp==2)
{
printf("Please input which stack you want to pop the number:\n");
scanf("%d",&i);
boolean=Pop(&s,i,x);
if(boolean==error) printf("ERROR!\n");
}
else
{
printf("ERROR!\n");
}
}
printf("Thanks for using.\n");
return 0;
}
//括号匹配
#include "stdio.h"
#include "string.h"
#define Maxsize 20
#define error -1
#define ok 1
typedef char ElemType;
typedef struct{
ElemType data[Maxsize];
int top;
}shstack;
void InitStack(shstack *s)
{
s->top=-1;
}
int StackEmpty(shstack *s)
{
if(s->top==-1)
return ok;
else return error;
}
int Push(shstack *s,ElemType x)
{
if(s->top==Maxsize)
return error;
s->top++;
s->data[s->top]=x;
return ok;
}
int Pop(shstack *s,ElemType x)
{
if(s->top==-1)
return error;
x=s->data[s->top];
s->top--;
return x;
}
int check(char string[],int len)
{
shstack s;
InitStack(&s);
int i;
char tmp;
for(i=0;i<len;i++)
{
if(string[i]=='('||string[i]=='['||string[i]=='{'||string[i]=='<')
Push(&s,string[i]);
else
{
if(StackEmpty(&s)==ok) return error;
tmp=Pop(&s,tmp);
if(string[i]==')'&&tmp!='(')
return error;
if(string[i]==']'&&tmp!='[')
return error;
if(string[i]=='}'&&tmp!='{')
return error;
if(string[i]=='>'&&tmp!='<')
return error;
}
}
return ok;
}
int main()
{
char string[100],tmp;
int len,i=0;
while(1)
{
scanf("%c",&tmp);
if(tmp!='{'&&tmp!='}'&&tmp!='['&&tmp!=']'&&tmp!='('&&tmp!=')'&&tmp!='\n'&&tmp!='<'&&tmp!='>')
{
printf("ERROR!\n");
return 0;
}
if(tmp!='\n')
{
string[i]=tmp;
i++;
}
else{
string[i]='\0';
break;
}
}
len=strlen(string);
int answer;
answer=check(string,len);
if(answer==error)
printf("Failed!\n");
else if(answer==ok)
printf("Succeed!\n");
return 0;
}
//求结点个数
#include "stdio.h"
#include "stdlib.h"
#define Maxsize 50
#define error -1
#define ok 1
typedef int ElemType;
typedef struct{
ElemType data[Maxsize];
int top;
}SeqStack;
void InitStack(SeqStack *s)
{
s->top=-1;
}
int Push(SeqStack *s,ElemType x)
{
if(s->top==Maxsize)
return error;
s->top++;
s->data[s->top]=x;
return ok;
}
int StackSize(SeqStack S)
{
int len=0;
len=S.top+1;
return len;
}
int main()
{
SeqStack s;
InitStack(&s);
int tmp,e,len;
printf("0->End the Program.\n");
printf("1->Push new numbers.\n");
printf("2->Get the length of stack.\n");
while(1)
{
scanf("%d",&tmp);
if(tmp==0) break;
else if(tmp==1)
{
scanf("%d",&e);
Push(&s,e);
}
else if(tmp==2)
{
len=StackSize(s);
printf("The length of stack is %d\n",len);
}
else printf("ERROR!");
}
return 0;
}
//队列逆序
#include "stdio.h"
#include "stdlib.h"
#define Maxsize 50
#define error -1
#define ok 1
typedef int ElemType;
typedef struct {
ElemType data[Maxsize];
int top;
}SeqStack;
typedef struct {
ElemType data[Maxsize];
int front, rear, size;
}SqQueue;
void InitStack(SeqStack* s)
{
s->top = -1;
}
int Push(SeqStack* s, ElemType x)
{
if (s->top == Maxsize)
return error;
s->top++;
s->data[s->top] = x;
return ok;
}
int StackSize(SeqStack S)
{
int len = 0;
len = S.top + 1;
return len;
}
int Pop(SeqStack* s, ElemType x)
{
if (s->top == -1)
return error;
x = s->data[s->top];
s->top--;
return x;
}
void create(SqQueue* Q)
{
Q->size = 0;
Q->front = 0;
Q->rear = 0;
}
int EnQueue(SqQueue* Q, ElemType e)
{
if (Q->size == Maxsize) return error;
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % Maxsize;
Q->size++;
return ok;
}
int DeQueue(SqQueue* Q, ElemType e)
{
if (Q->rear == Q->front) return error;
e = Q->data[Q->front];
Q->front = (Q->front + 1) % Maxsize;
return e;
}
void print(SqQueue* Q, int len)
{
int i;
for (i = 0;i < len;i++)
{
printf("%d ", Q->data[Q->front + i]);
}
}
int main()
{
SeqStack s;
InitStack(&s);
SqQueue Q;
create(&Q);
int tmp, len = 0, i;
printf("Please input numbers until '9999' is inputed.\n");
while (1)
{
scanf("%d", &tmp);
if (tmp == 9999) break;
EnQueue(&Q, tmp);
len++;
}
for (i = 0;i < len;i++)
{
tmp = DeQueue(&Q, tmp);
Push(&s, tmp);
}
create(&Q);
for (i = 0;i < len;i++)
{
tmp = Pop(&s, tmp);
EnQueue(&Q, tmp);
}
print(&Q, len);
return 0;
}
Part 3、总结
感觉自己也看了蛮多题的,但是总觉得都比较基础,没必要写到帖子里…到头来就加了几个题进去。这一块的重点还是巩固链表吧,因为不懂链表是不可能独立敲出来链栈的。
最低要求就是能独立敲栈的顺序存储和链式存储,至少得有基本的功能;
队列也要能敲出来带基本功能的。
在此基础上敲一下括号匹配、双向队列之类的综合性的代码,帮助会大一点。
代码都是很早之前敲的,很多地方现在看的也高血压(但是懒得改…能跑没bug就是阔以的),仅供参考,提供思路
知识点里的代码还是算法那一类的,伪代码,仅提供思路。第一遍敲可以参考一下,有bug就debug一下,要成为一个成熟的程序员(雾)
万变不离其宗,栈和队列无非就是在线性表和链表的基础上加一些条件限制,本质上都是一样的。