[数据结构] 2.栈与队列

1.栈

1.1定义

  • 性质:FILO、LIFO

  • 栈顶指针 top 记录栈顶元素的位置,栈空top = -1

  • 弹栈:代表栈顶元素的栈顶指针向下移动一位,逻辑上完成一次出栈操作(类比于Vector删除)


  • 一对 () 可以等价为一个完整事件,(()) 可以看作事件与事件的完全包含关系

  • () 问题是分治思想,可以采用递归实现

  • 递归实现借助的结构是系统栈,栈可以处理具有完全包含关系的问题

  • 栈和队列的应用

1.2顺序栈

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct Stack {
    int *data, size, top;
} Stack;

Stack *initStack(int n) {
    Stack *s = (Stack *)malloc(sizeof(Stack));
    s->data = (int *)malloc(sizeof(int) * n);
    s->size = n;
    s->top = -1;
    return s;
}

//freeStack()置栈区s==NULL
void freeStack(Stack **saddr) { //saddr指向Stack*
    if (!(*saddr)) return ;
    free((*saddr)->data);
    free(*saddr);
    *saddr = NULL; //置NULL
    return ;
}

int expand(Stack *s) {
    int expsize = s->size;
    int *temp;
    while (expsize) {
        temp = (int *)realloc(s->data, sizeof(int) * (s->size + expsize));
        if (temp) break;
        expsize >>= 1;
    }
    if (!temp) return 0;
    s->data = temp;
    s->size += expsize;
    printf("expand successfully, new size is %d\n", s->size);
    return 1;
}

int push(Stack *s, int val) {
    if (!s) return 0;
    if (s->top + 1 == s->size) {
        if (!expand(s))
            return 0;
    }
    s->data[++s->top] = val;
    return 1;
}

//isEmpty()和pop()独立封装,配合调用
int isEmpty(Stack *s) {
    return !(s && s->top != -1); // return s || s->top == -1; 互为逆反
}

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

void showStack(Stack *s) {
    //if (isEmpty(s)) return ; ---> !s/栈空不输出
    if (!s) return ;
    printf("Stack: [");
    for (int i = 0; i <= s->top; i++) {
        i && printf(",");
        printf("%d", s->data[i]);
    }
    printf("]\n");
    return ;
}

int main() {
    srand(time(0));
    Stack *s = initStack(2);
    int cnt = 10;
    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s\n", val, push(s, val) ? "SUC" : "ERR");
                break;
            case 3:
                //配合调用
                isEmpty(s) ? printf("pop nothing~\n") : printf("pop %d\n", pop(s));
                break;
        }
        showStack(s);
    }
    freeStack(&s);
    printf("s = %p\n", s); 
    return 0;
}
  1. 理解指针看到什么?去掉一个*

1.3链式栈

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

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

typedef struct Stack {
    Node *head;
} Stack;

Node *initNode(int val) {
    Node *n = (Node *)malloc(sizeof(Node));
    //if (!n) return NULL;
    n->val = val;
    n->next = NULL;
    return n;
}

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

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

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

// 对于链栈,插入使用头插法,head始终指向栈顶元素
int push(Stack *s, int val) {
    if (!s) return 0;
    Node *n = initNode(val);
    //if (!n) return 0;
    n->next = s->head; // 头插
    s->head = n;
    return 1;
}

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

int pop(Stack *s) {
    Node *k = s->head;
    int tmp = k->val;
    s->head = k->next;
    freeNode(k);
    return tmp;
}

void showStack(Stack *s) {
    if (!s) return ;
    printf("Stack: [");
    Node *p = s->head;
    while (p) {
        printf("%d->", p->val);
        p = p->next;
    }
    printf("NULL]\n");
    return ;
}

int main() {
    srand(time(0));
    Stack *s = initStack();
    int cnt = 10;
    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s\n", val, push(s, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(s) ? printf("pop nothings~\n") : printf("pop %d\n", pop(s));
                break;
        }
        showStack(s);
    }
    freeStack(s);
    return 0;
}
  1. 链栈使用头插法,栈顶指针 head 指向链表头节点,方便入栈、出栈

2.队列

2.1定义

  1. head指向队首,tail指向队尾+1

  2. 循环队列实际容量q->size - 1,为了避免队满时,与队空条件q->head == q->tail冲突

  3. 循环队列
    取余运算%实现循环,解决普通队列假溢出问题

  4. 2个基本操作:push、pop

  5. 出队判空&入队判满
    初始化q->head = q->tail = 0
    队空 q->head == q->tail
    队满(q->tail + 1) % q->size == q->head

  6. 为什么tail指向队尾的后一个元素?
    判断队空还是队满的问题导致的
    head指向队首,tail指向队尾
    初始:head = tail = -1(初始值为0有歧义,元素为0个还是1个?)
    队空: h e a d = = t a i l head == tail head==tail
    出队到只剩1个元素时, h e a d = = t a i l head==tail head==tail发生了歧义

2.2顺序队列(循环队列)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

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

Queue *initQueue(int n) {
    Queue *q = (Queue *)malloc(sizeof(Queue));
    q->data = (int *)malloc(sizeof(int) * n);
    q->size = n; //实际容量n-1
    q->head = q->tail = 0; //初始化-1无法判断队满
    return q;
}

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

int expand(Queue *q) {
    int expsize = q->size;
    int *tmp;
    while (expsize) { 
        tmp = (int *)malloc(sizeof(int) * (q->size + expsize));
        if (tmp) break;
        expsize >>= 1;
    }
    if (!tmp) return 0;
    int i, j;
    for (i=q->head, j=0; i!=q->tail; i=(i+1)%q->size, j++) {
        tmp[j] = q->data[i];
    }
    free(q->data);
    q->data = tmp;
    q->head = 0, q->tail = j; //tail利用循环变量j赋值
    q->size += expsize;
    printf("expand successfully~, new size is %d\n", q->size);
    return 1;
}

//head->队首 tail->队尾+1
int push(Queue *q, int val) {
    if (!q) return 0;
    if ((q->tail + 1) % q->size == q->head) { //队满
        if (!expand(q))
            return 0;
    }
    q->data[q->tail] = val;
    q->tail = (q->tail + 1) % q->size;
    return 1;
}

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

int pop(Queue *q) {
    int tmp = q->data[q->head];
    q->head = (q->head + 1) % q->size;
    return tmp;
}

void showQueue(Queue *q) {
    if (!q) return ;
    printf("Queue: [");
    for (int i = q->head; i != q->tail; i = (i + 1) % q->size) {
        i != q->head && printf(",");
        printf("%d", q->data[i]);
    }
    printf("]\n");
    return ;
}

int main() {
    srand(time(0));
    Queue *q = initQueue(1); //容量0,队满
    int cnt = 10;
    while(cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch(opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s\n", val, push(q, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(q) ? printf("pop nothings~\n") : printf("pop %d\n", pop(q));
                break;
        }
        showQueue(q);
    }
    freeQueue(q);
    return 0;
}

2.3链式队列

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

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

typedef struct Queue {
    Node *head, *tail; //链队尾插头删,因为删除需要尾节点前一个节点的地址,tail无法满足
} Queue;

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

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

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

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

int push(Queue *q, int val) {
    if (!q) return 0;
    Node *n = initNode(val);
    //空栈与否
    if (q->tail) {
        q->tail->next = n;
        q->tail = n;
    } else {
        q->head = n;
        q->tail = n;
    }
    return 1;
}

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

int pop(Queue *q) {
    Node *k = q->head;
    int tmp = k->val;
    q->head = k->next;
    freeNode(k);
    if (!q->head) q->tail = NULL; // freeNode后队空
    return tmp;
}

void showQueue(Queue *q) {
    if (!q) return ;
    printf("Queue: [");
    Node *p = q->head;
    while (p) {
        printf("%d->", p->val);
        p = p->next;
    }
    printf("NULL]\n");
    return ;
}

int main() {
    srand(time(0));
    Queue *q = initQueue();
    int cnt = 10;
    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s\n", val, push(q, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(q) ? printf("pop nothings~\n") : printf("pop %d\n", pop(q));
                break;
        }
        showQueue(q);
        putchar(10);
    }
    freeQueue(q);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值