一、 栈——一种数据遵从“后进先出”的线性表
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)