栈和队列详解(含代码演示)

1.栈

栈是一种只能在一段进行插入和删除的线性数据结构,通常使用线性表来实现。栈满足 **后进先出(LIFO/ last in first out)**的特性

由于栈不能像线性表一样随意进行插入删除,因此可以将栈视为一种功能受限的线性表。

栈的管理结点和线性表的管理结点相同:

顺序表实现栈:数据域(data)、栈的总容量(total)、栈的大小(len)

链表实现栈:栈顶结点(头结点)的指针(*head)、栈的大小(len)

(1)顺序栈的增删改查

【顺序栈也可以使用和顺序表一样的扩容方式】

#include
#include 
#include 

typedef struct Stack{
    int *data;
    int total;
    int len;
} Stack;

Stack *initStack(int total){
    Stack *s = (Stack *) malloc(sizeof(Stack));
    s->data = (int *) malloc(sizeof(int) * total);
    s->len = 0;
    s->total = total;
    return s;
}

void freeStack(Stack *s){
    if(!s) return ;
    free(s->data);
    free(s);
    return ;
}

void push(Stack *s, int val){
    if(!s) return ;
    if(s->len == s->total) return ;
    s->data[s->len] = val; //相当于顺序表的尾插法
    ++ s->len;
    return ;
}

int isEmpty(Stack *s){
    return !s || s->len == 0;
}

int top(Stack *s){
    return s->data[s->len - 1];
}

int pop(Stack *s){
    int val = top(s);
    -- s->len;
    return val;
}

int main(){
    Stack *s = initStack(5);
    for(int i = 0; i < 5; i++){
        push(s, i * 2); // 0 2 4 6 8
    }
    while(!isEmpty(s)){
        printf("%d ", pop(s)); // 8 6 4 2 0
    }
    printf("\n");

    return 0;
}

(2)链栈的增删改查

#include
#include 
#include 

typedef struct Node{
    int val;
    struct Node *next;
} Node;

Node *initNode(int val){
    Node *node = (Node *) malloc (sizeof(Node));
    node->val = val;
    node->next = NULL;
    return node;
}

void freeNode(Node *node){
    if(!node) return ;
    free(node);
}

typedef struct Stack{
    struct Node *head;
    int len;
} Stack;

Stack *initStack(){
    Stack *s = (Stack *) malloc (sizeof(Stack));
    s->head = NULL;
    s->len = 0;
    return s;
}

void freeStack(Stack *s){
    if(!s) return ;
    Node *p = s->head;
    while(p){
        Node *q = p;
        p = p->next;
        freeNode(q);
    }
    free(s);
}

void push(Stack *s, int val){
    if(!s) return ;
    Node *node = initNode(val);
    node->next = s->head;
    s->head = node;
    ++ s->len;
}

int top(Stack *s){
    return s->head->val;
}

int pop(Stack *s){
    int val = top(s);
    Node *node = s->head;
    s->head = s->head->next;
    freeNode(node);
    -- s->len;
    return val;
}

int isEmpty(Stack *s){
    return !s || !s->head;
}

int main(){
    Stack *s = initStack();
    for(int i = 0; i < 10; i++) push(s, i);
    while(!isEmpty(s)) printf("%d ", pop(s));
    printf("\n");
    

    return 0;
}

2.队列

只能在一段进行插入,另一端进行删除的先行数据结构,通常用线性表来实现。

队列满足 **先进先出(FIFO / first in first out)**的特性。与栈类似,队列也可以被看作是一种功能受限的线性表。

队列的管理结点:

顺序表实现的队列:数据域(data)、队首下标(head)、队尾下标(tail)、队列的总容量(total)

链表实现的队列:队首结点的指针(*head)、队尾结点的指针(*tail)、队列的长度(len)

一般来说队列只能查看队首元素,队尾入队首出

(1) 顺序队列

①普通顺序队列的代码演示

每次出栈头指针都会往后移一位,会造成栈顶空间浪费的情况

#include
#include 
#include 

typedef struct Queue{
    int *data;
    int head, tail, total;
} Queue;

Queue *initQueue(int total){
    Queue *q = (Queue *) malloc (sizeof(Queue));
    q->data = (int *) malloc(sizeof(int) * total);
    q->head = 0;
    q->tail = 0;
    q->total = total;
    return q;
}


void freeQueue(Queue *q){
    if(!q) return ;
    free(q->data);
    free(q);
    return ;
}

void push(Queue *q, int val){
    if(!q) return ;
    if(q->tail == q->total) return ;
    q->data[q->tail] = val;
    ++ q->tail;
}

int front(Queue *q){
    return q->data[q->head];
}

int pop(Queue *q){
    int val = front(q);
    ++ q->head;
    return val;
}

int isEmpty(Queue *q){
    return !q || q->head == q->tail;
}

int main(){
    Queue *q = initQueue(10);

    for(int i = 0; i < 9; i++){
        push(q, i);
    }
    while(!isEmpty(q)){
        printf("%d ", pop(q));
    }
    printf("\n");


    return 0;
}

②空间的循环利用

当head或tail大于队列总长size时,head或tail会变为0,即 (head + 1) % total 或者 (tail + 1) % total

#include
#include 
#include 

typedef struct Queue{
    int *data;
    int head, tail, total;
} Queue;

Queue *initQueue(int total){
    Queue *q = (Queue *) malloc (sizeof(Queue));
    q->data = (int *) malloc(sizeof(int) * total);
    q->head = 0;
    q->tail = 0;
    q->total = total;
    return q;
}


void freeQueue(Queue *q){
    if(!q) return ;
    free(q->data);
    free(q);
    return ;
}
//循环队列,保证在n个容量大小的情况下能存放n-1个元素

void push(Queue *q, int val){
    if(!q) return ;
    if(q->head == (q->tail + 1) % q->total) return ;
    q->data[q->tail] = val;
    q->tail = (q->tail + 1) % q->total;
}

int front(Queue *q){
    return q->data[q->head];
}

int pop(Queue *q){
    int val = front(q);
    q->head = (q->head + 1) % q->total;
    return val;
}

int isEmpty(Queue *q){
    return !q || q->head == q->tail;
}

int main(){
    Queue *q = initQueue(10);

    for(int i = 0; i < 4; i++){
        push(q, i);
    }
    while(!isEmpty(q)){
        printf("%d ", pop(q));
    }
    printf("\n");


    for(int i = 10; i < 19; i++){
        push(q, i);
    }
    while(!isEmpty(q)){
        printf("%d ", pop(q));
    }
    printf("\n");

    return 0;
}

③顺序循环队列扩容

int expand(Queue *q){
    int exTotal = q->total;
    int *temp;
    while(exTotal){
        //realloc重新分配空间成功后,指针q->data会被回收,不可以再使用
        temp = (int *) realloc (q->data, sizeof(int) * (q->total + exTotal));
        if(temp) break;
        exTotal >>= 1;
    }
    if(!temp) return 0;
    int i = q->head, j = q->head; //j是原来q->data的head,i是新的temp的q->data的head
    while(j != q->tail){
        temp[i] = temp[j]; //不能使用temp[i] = q->data[j]
        i = (i + 1) % (q->total + exTotal);
        j = (j + 1) % q->total;
    }
    q->tail = i;
    q->data = temp;
    q->total += exTotal;
    return 1;
}

//循环队列,保证在n个容量大小的情况下能存放n-1个元素
void push(Queue *q, int val){
    if(!q) return ;
    if(q->head == (q->tail + 1) % q->total) {
        if(!expand(q)) return ;
    }
    q->data[q->tail] = val;
    q->tail = (q->tail + 1) % q->total;
}

(2) 链队列

#include
#include 
#include 

typedef struct Node{
    int val;
    struct Node *next;
} Node;

Node *initNode(int val){
    Node *node = (Node *) malloc (sizeof(Node));
    node->val = val;
    node->next = NULL;
    return node;
}

void freeNode(Node *node){
    if(!node) return ;
    free(node);
}

typedef struct Queue{
    struct Node *head, *tail;
    int len;
} Queue;

Queue *initQueue(){
    Queue *q = (Queue *) malloc (sizeof(Queue));
    q->head = NULL;
    q->tail = NULL;
    q->len = 0;
    return q;
}

void freeQueue(Queue *q){
    if(!q) return ;
    Node *p = q->head;
    while(p){
        Node *temp = p;
        p = p->next;
        free(temp);
    }
    free(q);
}

//队尾插入
void push(Queue *q, int val){
    if(!q) return ;
    Node *node = initNode(val);
    if(q->len == 0){ //对于链表的第一次插入也可以改成NIL(虚头结点)
        q->head = node;
        q->tail = node;
    } else {
        q->tail->next = node;
        q->tail = q->tail->next;
    }
    ++ q->len;
}

int front(Queue *q){
    return q->head->val;
}

//队首弹出
int pop(Queue *q){
    int val = front(q);
    Node *node = q->head;
    if(q->len == 1){
        q->head = NULL;
        q->tail = NULL;
    } else {
        q->head = q->head->next;
    }
    freeNode(node);
    -- q->len;

    return val;
}

int isEmpty(Queue *q){
    return !q || (!q->head && !q->tail);
}

int main(){
    Queue *q = initQueue();
    for(int i = 0; i < 10; i++) push(q, i);
    while(!isEmpty(q)) printf("%d ", pop(q));
    printf("\n");

    return 0;
}

(3) 双向队列

双向队列可以使用 双向链表 实现,与单链表的区别仅有每个结点存放的指针有两个,分别为前驱结点的指针prev和后继结点的指针next

双向队列同时拥有:头插、尾插、头删、尾删四种增删操作

双向队列既可以当作栈使用又可以当作队列使用

要是对您有帮助,点个赞再走吧~ 欢迎评论区讨论~

你可能感兴趣的:(数据结构与算法,算法,数据结构,c算法,链表,c++)