嵌入式养成计划-21-数据结构之顺序栈,链栈,顺序循环队列,链式队列

五十三、 顺序栈

53.1 顺序栈的概念

  • 栈就是操作受限的线性表,只允许在栈顶操作的线性结构(只能在一端进行操作的线性表)
  • 后进先出(LIFO)Last Out First In
  • 栈的顺序实现,除了需要一个数组外,还需要一个栈顶元素的下标
    (标记栈顶的元素)
  • 当栈中没有元素时,栈顶指针下标应该是-1;

53.2 顺序栈的结构体

#define MAX 5

typedef int data;

typedef struct seq_stack
{
    //  用于存放栈元素的数组
    data arr[MAX];
    //  用于指向栈顶的位置
    int top;
} my_stack;

53.3 顺序栈实现(接口,功能实现,测试用主函数,Makefile)

接口(head.h)

#ifndef __HEAD_H__
#define __HEAD_H__

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

#define MAX 5
typedef int data;

typedef struct seq_stack
{
    //  用于存放栈元素的数组
    data arr[MAX];
    //  用于指向栈顶的位置
    int top;
} my_stack;

// 创建栈
my_stack *init();
// 栈的判满
int is_full(my_stack *s);
// 栈的判空
int is_empty(my_stack *s);
//  压栈
int push_stack(my_stack *s, data value);
//  出栈
data pop_stack(my_stack *s);
//  遍历输出栈
int output(my_stack *s);
//  栈清空
int clear_stack(my_stack *s);
//  销毁栈
int free_stack(my_stack **s);
#endif

功能实现(my_struct.c)

#include "head.h"

// 创建栈
my_stack *init()
{
    my_stack *s = (my_stack *)malloc(sizeof(my_stack));
    if (NULL == s)
    {
        printf("创建栈失败\n");
        return NULL;
    }
    memset(s, 0, sizeof(my_stack));
    s->top = -1;
    return s;
}

// 栈的判满
int is_full(my_stack *s)
{
    if (NULL == s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    my_stack *p = s;
    if (p->top == MAX - 1)
    {
        return 1;
    }
    return 0;
}

// 栈的判空
int is_empty(my_stack *s)
{
    if (NULL == s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    my_stack *p = s;
    if (p->top == -1)
    {
        return 1;
    }
    return 0;
}

//  压栈
int push_stack(my_stack *s, data value)
{
    if (NULL == s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    my_stack *p = s;
    if (!is_full(p))
    {
        //  栈顶指针先+1,再进行入栈,因为栈顶指针是从-1开始
        p->arr[++(p->top)] = value;
        return 0;
    }
    printf("此栈已满,不可再压栈\n");
    return -1;
}

//  弹栈
data pop_stack(my_stack *s)
{
    if (NULL == s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    my_stack *p = s;
    if (!is_empty(p))
    {
        return p->arr[p->top--];
    }
    printf("此栈已空,不可再弹栈\n");

    return -1;
}

//  遍历输出栈
int output(my_stack *s)
{
    if (NULL == s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    my_stack *p = s;
    if (!is_empty(p))
    {
        int i = p->top;
        printf("从栈顶到栈底元素依次为:\n");
        for (int j = 0; i >= 0; i--, j++)
        {
            printf("第 %d 位为: %d\n", j + 1, p->arr[i]);
        }
        return 0;
    }
    printf("此栈已空,无元素可输出\n");
    return -1;
}

//  栈清空
int clear_stack(my_stack *s)
{
    if (NULL == s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    my_stack *p = s;
    if (!is_empty(p))
    {
        memset(p->arr, 0, sizeof(p->arr));
        p->top = -1;
        printf("此栈已清空\n");
        return 0;
    }
    printf("此栈已空,不必再清空\n");
    return -1;
}

//  销毁栈
int free_stack(my_stack **s)
{
    if (NULL == *s)
    {
        printf("传入参数为空\n");
        return -1;
    }
    free(*s);
    *s = NULL;
    return 0;
}

测试用主函数(main.c)

#include "head.h"
int main(int argc, char const *argv[])
{
    my_stack *s = init();
    push_stack(s, 12);
    output(s);

    push_stack(s, 21);
    output(s);
    push_stack(s, 32);
    output(s);
    push_stack(s, 23);
    output(s);
    push_stack(s, 13);
    output(s);
    push_stack(s, 31);
    output(s);
    push_stack(s, 33);
    output(s);
    pop_stack(s);
    output(s);
    pop_stack(s);
    output(s);
    clear_stack(s);
    output(s);
    pop_stack(s);
    output(s);
    push_stack(s, 99);
    output(s);
    return 0;
}

Makefile

EXE=mystruct
CC=gcc
CFLAGs=-c
OBJs+=my_struct.o
OBJs+=main.o

all:$(EXE)

$(EXE):$(OBJs)
	$(CC) $^ -o $@

%.o:%.c
	$(CC) $(CFLAGs) $^ -o $@

clean:
	rm *.o mystruct

五十四、 链栈

54.1 链栈的概念

  • 由于栈是一个操作受限的线性表,可以有顺序结构也可以由链式存储实现
  • 如果想要实现链栈,其实就是链表,只能在一端操作
  • 由于链表的尾插和尾删需要遍历整条链表,所以实现链栈时,通过头插和头删

54.2 链栈的结构体

栈元素结点:

//  栈元素结点
typedef struct linked_stack
{
    //  元素结点的值
    data value;
    //  指向下一结点
    struct linked_stack *next;
} linked_stack;

栈顶指针:

//  栈顶指针
typedef struct top_stack
{
    //  记录栈内有多少个元素
    int len;
    //  指向栈顶元素结点
    struct linked_stack *top;
} top_st;

54.3 压栈----链表的头插

在这里插入图片描述
压栈:

//  压栈
int push_stack(top_st *t, data value)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  创建栈结点
    linked_stack *lt = (linked_stack *)malloc(sizeof(linked_stack));
    if (NULL == lt)
    {
        printf("创建栈结点失败\n");
        return -1;
    }
    //  栈结点填充值
    lt->value = value;
    //  当前栈结点接入到之前栈结点的前面
    lt->next = t->top;
    //  栈顶指针指向当前栈结点
    t->top = lt;
    ++t->len;
    return 0;
}

54.4 弹栈----链栈的头删

在这里插入图片描述
弹栈:

//  弹栈
data pop_stack(top_st *t)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(t))
    {
        linked_stack *p = t->top;
        data value = t->top->value;
        t->top = t->top->next;
        free(p);
        --t->len;
        return 0;
    }
    printf("此栈已空,不可再弹栈\n");

    return -1;
}

54.5 链栈的代码(接口,功能实现,测试用主函数,Makefile)

接口(head.h)

#ifndef __HEAD_H__
#define __HEAD_H__

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

typedef int data;

//  栈元素结点
typedef struct linked_stack
{
    data value;
    struct linked_stack *next;
    // struct linked_stack *top;
} linked_stack;

//  栈顶指针
typedef struct top_stack
{
    int len;
    struct linked_stack *top;
} top_st;

//  创建栈顶指针
top_st *create_top();
// 栈的判空
int is_empty(top_st *s);
//  压栈(头插)
int push_stack(top_st *s, data value);
//  出栈(头删)
data pop_stack(top_st *s);
//  遍历输出栈
int output(top_st *s);
//  栈清空
int clear_stack(top_st *s);
//  销毁栈
int free_stack(top_st **s);
#endif

功能实现(my_linked_struct.c)

#include "head.h"

//  创建栈顶指针
top_st *create_top()
{
    top_st *t = (top_st *)malloc(sizeof(top_st));
    if (NULL == t)
    {
        printf("创建栈顶指针失败\n");
        return NULL;
    }
    t->len = 0;
    t->top = NULL;
    return t;
}

// 栈的判空
int is_empty(top_st *t)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }
    top_st *p = t;
    if (NULL == p->top)
    {
        return 1;
    }
    return 0;
}

//  压栈
int push_stack(top_st *t, data value)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  创建栈结点
    linked_stack *lt = (linked_stack *)malloc(sizeof(linked_stack));
    if (NULL == lt)
    {
        printf("创建栈结点失败\n");
        return -1;
    }
    //  栈结点填充值
    lt->value = value;
    //  当前栈结点接入到之前栈结点的前面
    lt->next = t->top;
    //  栈顶指针指向当前栈结点
    t->top = lt;
    ++t->len;
    return 0;
}

//  弹栈
data pop_stack(top_st *t)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(t))
    {
        linked_stack *p = t->top;
        data value = t->top->value;
        t->top = t->top->next;
        free(p);
        --t->len;
        return 0;
    }
    printf("此栈已空,不可再弹栈\n");

    return -1;
}

//  遍历输出栈
int output(top_st *t)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }

    printf("当前栈内元素个数为 : %d\n", t->len);

    linked_stack *p = t->top;
    if (!is_empty(t))
    {
        int i = t->len;
        printf("从栈顶到栈底元素依次为:\n");
        for (int j = 0; i > 0 && NULL != p; i--, j++, p = p->next)
        {
            printf("第 %d 位为: %d\n", j + 1, p->value);
        }
        return 0;
    }
    printf("此栈已空,无元素可输出\n");
    return -1;
}

//  栈清空
int clear_stack(top_st *t)
{
    if (NULL == t)
    {
        printf("传入参数为空\n");
        return -1;
    }

    if (!is_empty(t))
    {
        linked_stack *p = t->top;
        while (NULL != p)
        {
            t->top = p->next;
            free(p);
            --t->len;
            p = t->top;
        }
        printf("此栈已清空\n");
        return 0;
    }
    printf("此栈已空,不必再清空\n");
    return -1;
}

//  销毁栈
int free_stack(top_st **t)
{
    if (NULL == *t)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(*t))
    {
        linked_stack *p = (*t)->top;
        while (NULL != p)
        {
            (*t)->top = p->next;
            free(p);
            --(*t)->len;
            p = (*t)->top;
        }
    }
    free(*t);
    *t = NULL;
    printf("此栈已销毁\n");
    return 0;
}

测试用主函数(main.c)

#include "head.h"
int main(int argc, char const *argv[])
{
    top_st *s = create_top();
    printf("\n压栈\n");
    push_stack(s, 12);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 21);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 32);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 23);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 13);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 31);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 33);
    output(s);

    printf("\n弹栈\n");
    pop_stack(s);
    output(s);

    printf("\n弹栈\n");
    pop_stack(s);
    output(s);

    printf("\n清空\n");
    clear_stack(s);
    output(s);

    printf("\n弹栈\n");
    pop_stack(s);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 99);
    output(s);

    puts("");
    free_stack(&s);
    printf("%p\n", s);

    printf("\n弹栈\n");
    pop_stack(s);
    output(s);

    printf("\n压栈\n");
    push_stack(s, 99);
    output(s);

    return 0;
}

Makefile

EXE=linked_struct
CC=gcc
CFLAGs=-c
OBJs+=my_linked_struct.o
OBJs+=main.o

all:$(EXE)

$(EXE):$(OBJs)
	$(CC) $^ -o $@

%.o:%.c
	$(CC) $(CFLAGs) $^ -o $@

clean:
	rm *.o linked_struct

五十五、 顺序循环队列

55.1 队列的概念

  • 一端入,一端出的,操作受限的线性表
  • 只允许一端入队,另一端出队。
  • 先进先出(FIFO)First in first out

55.2 循环队列的结构体

#define MAX 6
typedef int data;

//  队列元素结点
typedef struct my_queue
{
    data arr[MAX];
    //  队头
    int rear;
    //  队尾
    int front;
} my_queue;

55.3 循环队列的判空

头的位置与尾的位置相同
front == rear

55.4 循环队列的判满

头的下一个位置与尾的位置相同
(front+1)%MAX == rear
为了防止因队头不断增加导致溢出,故每次增加时都要对栈的最大值MAX取模,判满同理
在这里插入图片描述

55.5 入队(尾插)

//  入队(尾插)
int in_queue(my_queue *q, data value)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_full(q))
    {
        q->arr[q->rear] = value;
        q->rear = (q->rear + 1) % MAX;
        return 0;
    }
    printf("队列已满,无法入队\n");
    return -1;
}

55.6 出队(头删)

//  出队(头删)
data out_queue(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(q))
    {
        data value= q->arr[q->front];
        q->front = (q->front + 1) % MAX;
        return value;
    }
    printf("队列为空,出队失败\n");
    return -1;
}

55.7 遍历输出队列

//  遍历输出队列
int output(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(q))
    {
        int i=q->front;
        for (int i=q->front; i != q->rear; i=(i+1)%MAX)
        {
            printf("%d\n",q->arr[i]);
        }
        return 0;
    }
    printf("队列为空,没有输出\n");
    return -1;
}

55.8 队列清空

//  队列清空
int clear_queue(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    memset(q,0,sizeof(my_queue));
    return 0;
}

55.9 销毁队列

//  销毁队列
int free_queue(my_queue **q)
{
    if (NULL == *q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    free(*q);
    *q=NULL;
    return 0;
}

55.10 顺序循环队列的代码(接口,功能实现,测试用主函数,Makefile)

接口(head.h)

#ifndef __HEAD_H__
#define __HEAD_H__

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

#define MAX 6
typedef int data;

//  队列元素结点
typedef struct my_queue
{
    data arr[MAX];
    //  队头
    int rear;
    //  队尾
    int front;
} my_queue;

//  创建栈顶指针
my_queue *init();

// 队列的判空
int is_empty(my_queue *q);
// 队列的判满
int is_full(my_queue *q);
//  入队(头插)
int in_queue(my_queue *q, data value);
//  出队(尾删)
data out_queue(my_queue *q);
//  遍历输出队列
int output(my_queue *q);
//  队列清空
int clear_queue(my_queue *q);
//  销毁队列
int free_queue(my_queue **q);
#endif

功能实现(my_queue.c)

#include "head.h"

//  创建栈顶指针
my_queue *init()
{
    my_queue *q = (my_queue *)malloc(sizeof(my_queue));
    if (NULL == q)
    {
        printf("队列创建失败\n");
        return NULL;
    }
    memset(q, 0, sizeof(my_queue));
    return q;
}

// 队列的判空
int is_empty(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (q->front == q->rear)
    {
        printf("是个空队列\n");
        return 1;
    }
    return 0;
}

// 队列的判满
int is_full(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (((q->rear) + 1) % MAX == q->front)
    {
        printf("是个满队列\n");
        return 1;
    }
}

//  入队(尾插)
int in_queue(my_queue *q, data value)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_full(q))
    {
        q->arr[q->rear] = value;
        q->rear = (q->rear + 1) % MAX;
        return 0;
    }
    printf("队列已满,无法入队\n");
    return -1;
}

//  出队(头删)
data out_queue(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(q))
    {
        data value= q->arr[q->front];
        q->front = (q->front + 1) % MAX;
        return value;
    }
    printf("队列为空,出队失败\n");
    return -1;
}

//  遍历输出队列
int output(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (!is_empty(q))
    {
        int i=q->front;
        for (int i=q->front; i != q->rear; i=(i+1)%MAX)
        {
            printf("%d\n",q->arr[i]);
        }
        return 0;
    }
    printf("队列为空,没有输出\n");
    return -1;
}

//  队列清空
int clear_queue(my_queue *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    memset(q,0,sizeof(my_queue));
    return 0;
}

//  销毁队列
int free_queue(my_queue **q)
{
    if (NULL == *q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    free(*q);
    *q=NULL;
    return 0;
}

测试用主函数(main.c)

#include "head.h"
int main(int argc, char const *argv[])
{
    my_queue *q = init();
    printf("\n入队\n");
    in_queue(q, 12);
    output(q);

    printf("\n入队\n");
    in_queue(q, 21);
    output(q);

    printf("\n入队\n");
    in_queue(q, 32);
    output(q);

    printf("\n入队\n");
    in_queue(q, 23);
    output(q);

    printf("\n入队\n");
    in_queue(q, 13);
    output(q);

    printf("\n入队\n");
    in_queue(q, 31);
    output(q);

    printf("\n入队\n");
    in_queue(q, 33);
    output(q);

    printf("\n出队\n");
    out_queue(q);
    output(q);

    printf("\n出队\n");
    out_queue(q);
    output(q);

    printf("\n清空\n");
    clear_queue(q);
    output(q);

    printf("\n出队\n");
    out_queue(q);
    output(q);

    printf("\n入队\n");
    in_queue(q, 99);
    output(q);

    puts("");
    free_queue(&q);
    printf("%p\n", q);

    printf("\n出队\n");
    out_queue(q);
    output(q);

    printf("\n入队\n");
    in_queue(q, 99);
    output(q);

    return 0;
}

Makefile

EXE=my_queue
CC=gcc
CFLAGs=-c
OBJs+=my_queue.o
OBJs+=main.o

all:$(EXE)

$(EXE):$(OBJs)
	$(CC) $^ -o $@

%.o:%.c
	$(CC) $(CFLAGs) $^ -o $@

clean:
	rm *.o my_queue.c

五十六、 链式队列

在这里插入图片描述

56.1 链式队列的结构体

链队元素结点:

//  链队元素结点中值的类型的别名
typedef int data;

//  链队元素结点
typedef struct linked_queue
{
    //  链队的值
    data value;
    //  链队的next指针域
    struct linked_queue *next;
} linked_queue; //  链队结点类型的的别名

链队指针,用于指向队头和队尾:

//  链队指针,用于指向队头和队尾
typedef struct queue_point
{
    //  链队中元素个数
    int len;
    //  队头
    linked_queue *front;
    //  队尾
    linked_queue *rear;
} queue_point;  //  链队指针类型的别名

56.2 创建链队指针

//  创建链队指针
queue_point *create_point()
{
    //  创建链队指针
    queue_point *q = (queue_point *)malloc(sizeof(queue_point));
    //  没创建成功,提示创建失败,并返回NULL
    if (NULL == q)
    {
        printf("创建队列指针失败\n");
        return NULL;
    }
    //  链队指针创建成功,将链队元素结点个数初始化为0
    //  链队的队头指针和队尾指针都指向NULL
    q->len = 0;
    q->rear = NULL;
    q->front = NULL;
    //  返回创建的链队指针地址
    return q;
}

56.3 链队入队(尾插)

//  链队入队(尾插----链表的尾部,不是队列的尾,是队列的头)
int in_queue(queue_point *q, data value)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  创建链队元素结点
    linked_queue *lq = (linked_queue *)malloc(sizeof(linked_queue));
    //  创建失败,结束程序,返回-1表示函数异常结束
    if (NULL == q)
    {
        printf("创建队列结点失败\n");
        return -1;
    }
    //  创建链队元素结点成功,将链队元素结点填充值,并将next指针域置空
    lq->value = value;
    lq->next = NULL;
    //  如果链队是空的,那么队头要指向这个结点
    //  第一个结点,队头队尾都是它
    if (is_empty(q))
    {
        q->front = lq;
        // q->rear = lq;   //  可以现在先不指向,因为后面都会指向
    }
    //  如果链队不是空的,那么链队就存在队头和队尾
    //  所以只需要更新队尾
    //  先让新建的结点跟在旧的队尾后面
    else
    {
        q->rear->next = lq;
    }
    //  让队尾指向新建的这个结点
    q->rear=lq;  
    //  链队的元素个数+1
    ++q->len;
    return 0;
}

56.4 链队出队(头删)

//  链队出队(头删----链表的头部,不是队列的头,是队列的尾)
data out_queue(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  链队为空时提示并退出
    if (is_empty(q))
    {
        printf("链队为空,无元素出队\n");
        return -1;
    }
    //  链队非空时
    //  搞个链队结点,用于指向队头出队的结点
    //  (类比银行排队时队头处理完的那个人会离开队伍)
    linked_queue *lq = q->front;
    //  之前的队头准备走了,让旧队头的下一个成为新队头
    q->front = lq->next;
    //  暂存队头的数据
    data t = lq->value;
    //  释放旧队头
    free(lq);
    //  队列元素个数-1
    --q->len;
    return t;
}

56.5 遍历输出队列

//  遍历输出队列
int output(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (is_empty(q))
    {
        printf("队列为空,无元素可输出\n");
        return -1;
    }
    //  队列不为空时
    //  声明一个结点用于指向要输出的结点,首先从队头开始
    linked_queue *lq = q->front;
    printf("队列中当前元素个数为:%d\n", q->len);
    //  如果指到了队尾的next指针域,则进行退出
    while (NULL != lq)
    {
        printf("%d\n", lq->value);
        //  当前要输出的结点向后挪
        lq = lq->next;
    }
    return 0;
}

56.6 队列清空、销毁队列

//  队列清空
int clear_queue(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  用于指向待清空的结点,从队头开始
    linked_queue *lq = q->front;
    //  如果待清空的结点为NULL
    //  要么本就是空的队,队头是空的
    //  要么是指到了队尾的next指针域
    while (NULL != lq)
    {
        //  先让新的队头变成旧队头的下一个,即原来的第二个
        q->front = lq->next;
        //  将旧队头释放
        free(lq);
        //  队列结点个数-1
        --q->len;
        //  将新的队头变成准备删除的结点,最多让新队头变成队尾的next指针域
        lq = q->front;
    }
    //  循环结束之后,此时待删除结点指向了队尾的next指针域,即NULL
    //  所以让队头再次指向NULL,貌似这句有点多余
    q->front = lq;
    return 0;
}
//  销毁队列
int free_queue(queue_point **q)
{
    if (NULL == *q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  清理掉链队后,需要将链队指针的空间也释放
    clear_queue(*q);
    free(*q);
    //  让链队指针指向NULL
    *q = NULL;
    return 0;
}

56.7 链式队列的代码(接口,功能实现,测试用主函数,Makefile)

接口(head.h)

#ifndef __HEAD_H__
#define __HEAD_H__

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

//  链队元素结点中值的类型的别名
typedef int data;

//  链队元素结点
typedef struct linked_queue
{
    //  链队的值
    data value;
    //  链队的next指针域
    struct linked_queue *next;
} linked_queue; //  链队结点类型的的别名

//  链队指针,用于指向队头和队尾
typedef struct queue_point
{
    //  链队中元素个数
    int len;
    //  队头
    linked_queue *front;
    //  队尾
    linked_queue *rear;
} queue_point;  //  链队指针类型的别名

//  创建链队指针
queue_point *create_point();
// 链队判空
int is_empty(queue_point *q);
//  链队入队(尾插----链表的尾部,不是队列的尾,是队列的头)
int in_queue(queue_point *q, data value);
//  链队出队(头删----链表的头部,不是队列的头,是队列的尾)
data out_queue(queue_point *q);
//  遍历输出链队
int output(queue_point *q);
//  链队清空
int clear_queue(queue_point *q);
//  销毁链队
int free_queue(queue_point **q);
#endif

功能实现(linked_queue.c)

#include "head.h"

//  创建链队指针
queue_point *create_point()
{
    //  创建链队指针
    queue_point *q = (queue_point *)malloc(sizeof(queue_point));
    //  没创建成功,提示创建失败,并返回NULL
    if (NULL == q)
    {
        printf("创建队列指针失败\n");
        return NULL;
    }
    //  链队指针创建成功,将链队元素结点个数初始化为0
    //  链队的队头指针和队尾指针都指向NULL
    q->len = 0;
    q->rear = NULL;
    q->front = NULL;
    //  返回创建的链队指针地址
    return q;
}

// 链队判空
int is_empty(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  链队指针的队头或者队尾指向NULL时链队为空
    if (NULL == q->front || NULL == q->rear)
    {
        return 1;
    }
    return 0;
}

//  链队入队(尾插----链表的尾部,不是队列的尾,是队列的头)
int in_queue(queue_point *q, data value)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  创建链队元素结点
    linked_queue *lq = (linked_queue *)malloc(sizeof(linked_queue));
    //  创建失败,结束程序,返回-1表示函数异常结束
    if (NULL == q)
    {
        printf("创建队列结点失败\n");
        return -1;
    }
    //  创建链队元素结点成功,将链队元素结点填充值,并将next指针域置空
    lq->value = value;
    lq->next = NULL;
    //  如果链队是空的,那么队头要指向这个结点
    //  第一个结点,队头队尾都是它
    if (is_empty(q))
    {
        q->front = lq;
        q->rear = lq;
    }
    //  如果链队不是空的,那么链队就存在队头和队尾
    //  所以只需要更新队尾
    //  先让新建的结点跟在旧的队尾后面
    else
    {
        q->rear->next = lq;
    }
    //  让队尾指向新建的这个结点
    q->rear=lq;  
    //  链队的元素个数+1
    ++q->len;
    return 0;
}

//  链队出队(头删----链表的头部,不是队列的头,是队列的尾)
data out_queue(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  链队为空时提示并退出
    if (is_empty(q))
    {
        printf("链队为空,无元素出队\n");
        return -1;
    }
    //  链队非空时
    //  搞个链队结点,用于指向队头出队的结点
    //  (类比银行排队时队头处理完的那个人会离开队伍)
    linked_queue *lq = q->front;
    //  之前的队头准备走了,让旧队头的下一个成为新队头
    q->front = lq->next;
    //  暂存队头的数据
    data t = lq->value;
    //  释放旧队头
    free(lq);
    //  队列元素个数-1
    --q->len;
    return t;
}

//  遍历输出队列
int output(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    if (is_empty(q))
    {
        printf("队列为空,无元素可输出\n");
        return -1;
    }
    //  队列不为空时
    //  声明一个结点用于指向要输出的结点,首先从队头开始
    linked_queue *lq = q->front;
    printf("队列中当前元素个数为:%d\n", q->len);
    //  如果指到了队尾的next指针域,则进行退出
    while (NULL != lq)
    {
        printf("%d\n", lq->value);
        //  当前要输出的结点向后挪
        lq = lq->next;
    }
    return 0;
}

//  队列清空
int clear_queue(queue_point *q)
{
    if (NULL == q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  用于指向待清空的结点,从队头开始
    linked_queue *lq = q->front;
    //  如果待清空的结点为NULL
    //  要么本就是空的队,队头是空的
    //  要么是指到了队尾的next指针域
    while (NULL != lq)
    {
        //  先让新的队头变成旧队头的下一个,即原来的第二个
        q->front = lq->next;
        //  将旧队头释放
        free(lq);
        //  队列结点个数-1
        --q->len;
        //  将新的队头变成准备删除的结点,最多让新队头变成队尾的next指针域
        lq = q->front;
    }
    //  循环结束之后,此时待删除结点指向了队尾的next指针域,即NULL
    //  所以让队头再次指向NULL,貌似这句有点多余
    q->front = lq;
    return 0;
}

//  销毁队列
int free_queue(queue_point **q)
{
    if (NULL == *q)
    {
        printf("传入参数为空\n");
        return -1;
    }
    //  清理掉链队后,需要将链队指针的空间也释放
    clear_queue(*q);
    free(*q);
    //  让链队指针指向NULL
    *q = NULL;
    return 0;
}

测试用主函数(main.c)

#include "head.h"
int main(int argc, char const *argv[])
{
    queue_point *s = create_point();
    printf("\n入队\n");
    in_queue(s, 12);
    output(s);

    printf("\n入队\n");
    in_queue(s, 21);
    output(s);

    printf("\n入队\n");
    in_queue(s, 31);
    output(s);

    printf("\n入队\n");
    in_queue(s, 33);
    output(s);

    printf("\n出队\n");
    out_queue(s);
    output(s);

    printf("\n出队\n");
    out_queue(s);
    output(s);

    printf("\n清空\n");
    clear_queue(s);
    output(s);

    printf("\n出队\n");
    out_queue(s);
    output(s);

    printf("\n入队\n");
    in_queue(s, 99);
    output(s);

    puts("");
    free_queue(&s);
    printf("%p\n", s);

    printf("\n出队\n");
    out_queue(s);
    output(s);

    printf("\n入队\n");
    in_queue(s, 99);
    output(s);

    return 0;
}

Makefile

EXE=linked_queue
CC=gcc
CFLAGs=-c
OBJs+=linked_queue.o
OBJs+=main.o

all:$(EXE)

$(EXE):$(OBJs)
	$(CC) $^ -o $@

%.o:%.c
	$(CC) $(CFLAGs) $^ -o $@

clean:
	rm *.o linked_queue

小作业

使用递归实现程序:输入一个数,输出该数的每一位

在这里插入图片描述

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

int output(int num);

int main(int argc, const char *argv[])
{
    int num;
    printf("请输入一个数:");
    scanf("%d", &num);
    output(num);
    return 0;
}

int output(int num)
{
    if (num < 1)
    {
        return num;
    }
    else
    {
        printf("%d\n", num % 10);
        return output(num/10);
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhk___

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值