数据结构学习笔记(二)栈和队列

栈和队列

数据结构的线性结构是一种很基础也很重要的结构,通常说的栈和队列只是线性结构中的两种特殊类型,最大的特征在于插入和删除室友规则的,且只能在一端进行。

栈(先进后出)
Insert(S,n+1,e)只能从尾端插入
Delete(S,n)只能从尾端删除

队列(先进先出)
Insert(Q,n+1,x)只能从尾端插入
Delete(Q,1)只能从头部删除

除此之外,同样的两者都可以分别用数组和链表来表示,以下的实现方式也分别用数组和链表完成。

顺序栈

顺序栈的结构定义为

typedef struct Stack
{
    ElementType *base;//栈底指针
    ElementType *top;//栈顶指针
    //指针也可以用int型,表示元素的下标
    int StackSize;//数组最大容量
}Stack;

由于栈先进后出的特性,所以常常会画成这样

 

由此可见,顺序栈的状态可以表示为
栈满:top-base=stacksize
栈空:top=base

相关操作如下

1、初始化操作

Status InitStack(Stack &S){
    S.base = new ElementType[MaxSize];//初始大小
//    S.base = (ElementType*)malloc(MaxSize*sizeof(ElementType));
    if(!S.base)exit ERROR;//分配失败
    S.top  = S.base;
    S.StackSize = MaxSize;
    return OK;
}

2、判断是否为空,求长度,对应两种特殊状态

//判断栈是否为空
Status StackEmpty(Stack S){
    if(S.top == S.bsae)
        return TRUE;
    else
        FALSE;
}

//求长度
int StackLength(Stack S){
    reutrn S.top-S.base;
}

3、销毁与清空
销毁是完全释放出空间;清空置为空表

//销毁
Status DestroyStack(Stack S{
    if(S.base){
        delete S.base;
        S.StackSize = 0;
        S.top = S.base = NULL;
    }
    return OK;
}

//清空
Status ClearStack(Stack &S){
    if(S.base)S.top = S.base;
    return OK;
}

 

 4、出栈与入栈

//
Status Pop(Stack &S,ElementType &e){
    if(S.top==S.base)//栈空
        return ERROR;
    e = *--S.top;
    return OK;
}

//
Status Push(Stack &S,ElementType e){
    if(S.top-S.base==S.StackSize)//栈满
        return ERROR;
    *S.top++=e;
    return OK;
}

链栈

链栈的结构与一般的链表差不多

//链栈
//表示方法
typedef struct StackNode{
    ElementType data;
    struct StackNode *next;
}StackNode,*LinkStack;

由于链表独特的结构特性——找到最后一个需要顺着指针一个一个寻找,对于栈这种一边存取的结构显然是很费时间的。于是采用反向指针

 

 

 相关操作

1、初始化

//初始化
void InitStack(LinkStack &S){
    S = NULL;
    return OK;
}

2、判断是否为空

//判断是否为空
Status StackEmpty(LinkStack S){
    if(S==NULL)return TRUE;
    else return FALSE;
}

3、出栈和入栈

//入栈
Status Push(LinkStack &S,ElementType e){
    p = new StackNode;//新建节点
    p->data = e;//赋值
    p->next = S;//接到栈顶上
    S = p;//S指针上移
    return OK;
}
//出栈
Status Pop(LinkStack &S,ElementType &e){
    if(S==NULL)return ERROR;//判断是否为空
    e = S->data;//把删除的值记录下来
    p = S;
    S = S->next;//S指针下移
    delete p;//释放空间
    return OK;
}

4、取栈顶元素

//取栈顶元素
ElementType GetTop(LinkStack S){
    if(S!=NULL)
        return S->data;
}

队列

队列可分为顺序队列与循环队列

队列的表示方法与栈类似,这里顺序队列为例

typedef struct Queue{
    ElementType *base;//初始空间
    int front;//头指针
    int rear;//尾指针,下标
}Queue;
//头指针和尾指针可以用下标代替,所以直接用的int型

从存取过程可以看出

 

 

 入队的一头会越来越长,出队的一头会越来越短。但是,出队一头的空间按照规定不能用的,这就造成了极大的空间浪费,

为解决这个问题,于是有了循环队列。
当队列入队的那一头满了,就又从另一端开始加

 

但是这样也产生了问题,队空和队列满都是front=rear,怎么判断?
1,另外设置一个标志表示空还是满
2,另外设置一个量表示元素个数
3,少用一个元素空间
三种方法选哪个?
另外front-rear为负数怎么办?

对于这两个问题,一般采用的是,少用一个空间,求余数的办法
综上,队列的状态为
队空:front = rear
队满:(rear+1)%Maxsize == front

顺序队列

上述关于循环队列的引入就是以顺序队列为例子,相关操作为

1、队列的初始化

//初始化
Status InitQueue(Queue &Q){
    Q.base = new ElementType[MaxSize];//分配空间
    if(!Q.base)exit ERROR;//分配失败
    Q.front = Q.rear=0;//头尾指针都为零
    return OK;
}

2、求队列长度

//求队列长度
int QueueLength(Queue Q){
    return (Q.rear-Q.front+MaxSize)%MaxSize;//求余操作,应对出现负数的情况
}

 3、出队和入队

//入队
Status EnQueue(Queue &Q,ElementType e){
    if((Q.rear+1)%MaxSize==Q.front)
        return ERROR;//队列已满,溢出错误
    Q.base[Q.rear] = e;//把e加在队尾
    Q.rear = (Q.rear+1)%MaxSize;//队尾指针加一
    return OK;
}
//出队
Status DeQueue(Queue &Q,ElementType &e){
    if(Q.front==Q.rear)
        return ERROR;//队列为空,出现错误
    e = Q.base[Q.front];//删除的值记录在e中
    Q.front = (Q.front+1)%MaxSize;//队头元素加一
    return OK;
}

4、取出头部元素front

//取出对头元素
ElementType GetHead(Queue Q){
    if(Q.front!=Q.rear)//队列不能为空
        return Q.base[Q.front];
}

链队

无论是栈还是队列,采用链表的一个好处就是,几乎不用担心空间的大小,因为可以动态申请与释放

typedef struct QueueNode{
    ElementType data;
    struct QueueNode *next;    
}QueueNode,*QueuePtr;

由于链表的两端时间复杂度相差太大,所以采用头尾两个指针的形式,带有头节点,rear端插入,front端删除

 
1、初始化
Status InitQueue(LinkQueue &Q){
    Q.front=Q.rear=(QueuePtr)malloc(sizeof(QueueNode));
    if(!Q.front)exit ERROR;//失败
    Q.front->next = NULL;
    return OK;
}

2、销毁队列

Status DestroyQueue(LinkQueue &Q){
    while(Q.front){
        p = Q.front->next;
        free(Q.front);
        Q.front = p;//依次释放
    }
    return OK;
}

3、出队和入队

//出队
Status DeQueue(LinkQueue &Q,ElementType &e){
    if(Q.rear==Q.front) return ERROR;
    p = Q.front->next;
    e = p->data;//记录下删除的值
    Q.front->next = p->next;//头节点指向下下一节点
    if(Q.rear==p)Q.rear=Q.front;//如果头节点后面就是rear节点...
    free(p);//释放空间
    return OK;
}
//入队
Status EnQueue(LinkQueue &Q,ElementType e){
    p = (QueuePtr)malloc(sizeof(QueueNode));
    if(!p)exit ERROR;
    p->data = e;//赋值
    p->next = NULL;
    Q.rear->next = p;//把p接在队尾
    Q.rear = p;//尾指针后移
}

4、取得对头元素

Status GetHead(LinkQueue Q,ElementType &e){
    if(Q.front==Q.rear)retur ERROR;
    e = Q.front->next->data;
}

 

posted @ 2020-09-05 22:06  fallwinddy  阅读(293)  评论(0)    收藏  举报