V2.0
C++基础教程<br />——<br />作业及参考答案全部汇总文档<br/>节9队列和栈阶段作业<br/><br/>最新版本V2.0
<br>王道C++团队<br/>COPYRIGHT ⓒ 2021-2024. 王道版权所有基础题篇手动实现链式栈手动实现动态数组循环队列栈的应用:括号匹配问题扩展题篇扩展:手动实现动态数组栈扩展:手动实现链式队列The End
Gn!
下面都是一些基础的语法、概念编程练习题。
Gn!
基于以下头文件,手动实现一个链式栈:
xxxxxxxxxx
33123456
7typedef int ElementType;
8
9// 栈的一个结点栈帧,类型定义
10typedef struct node_s {
11ElementType data;
12struct node_s *next;
13}StackFrame;
14
15typedef struct {
16StackFrame *top; // 栈顶指针
17}LinkedStack;
18
19// 基本操作
20// 创建链式栈
21LinkedStack *stack_create();
22// 销毁链式栈
23void stack_destroy(LinkedStack *stack);
24// 判空
25bool is_empty(LinkedStack *stack);
26// 入栈
27void stack_push(LinkedStack *stack, ElementType data);
28// 出栈并返回栈顶元素
29ElementType stack_pop(LinkedStack *stack);
30// 访问栈顶元素
31ElementType stack_peek(LinkedStack *stack);
32
33// !LINKED_STACK_H
参考代码如下:
参考代码:
以下是实现源文件的代码,即所有函数的实现如下:
xxxxxxxxxx
6012
3// 新建一个空栈
4LinkedStack *stack_create() {
5// callock可以不用手动初始化空指针
6return calloc(1, sizeof(LinkedStack));
7}
8// 对于链式栈而言,销毁栈就是销毁链表
9void stack_destroy(LinkedStack *stack) {
10// 相当于遍历链表(出栈)然后在遍历中逐个free结点
11StackFrame *curr = stack->top;
12while (curr != NULL) {
13StackFrame *tmp = curr->next; // 保存后继结点
14free(curr);
15curr = tmp;
16}
17// 最后free栈结构体
18free(stack);
19}
20
21bool is_empty(LinkedStack *stack) {
22return stack->top == NULL;
23}
24
25// 相当于链表头插法插入结点,栈顶指针相当于链表的头指针
26void stack_push(LinkedStack *stack, ElementType data) {
27// 1.新建一个栈帧结点
28StackFrame *new_frame = malloc(sizeof(StackFrame));
29if (new_frame == NULL) {
30printf("malloc failed in stack_push.\n");
31exit(1);
32}
33// 2.初始化新栈帧
34new_frame->data = data;
35new_frame->next = stack->top;
36// 3.更新栈顶指针
37stack->top = new_frame;
38}
39
40// 相当于链表在头部删除第一个结点,栈顶指针相当于链表的头指针
41ElementType stack_pop(LinkedStack *stack) {
42// 1.栈判空处理
43if (is_empty(stack)) {
44printf("error: stack is empty.\n");
45exit(1);
46}
47// 2.出栈返回栈顶元素,并free结点,更新栈顶指针
48StackFrame *curr = stack->top;
49ElementType data = curr->data;
50stack->top = curr->next;
51free(curr);
52return data;
53}
54ElementType stack_peek(LinkedStack *stack) {
55if (is_empty(stack)) {
56printf("error: stack is empty.\n");
57exit(1);
58}
59return stack->top->data;
60}
测试用例代码如下:
xxxxxxxxxx
35123456
7int main(void) {
8// 创建一个空栈
9LinkedStack *stack = stack_create();
10if (stack == NULL) {
11printf("Failed to create stack.\n");
12return -1;
13}
14
15// 入栈操作
16stack_push(stack, 1);
17stack_push(stack, 2);
18stack_push(stack, 3);
19
20// 查看栈顶元素
21printf("当前栈顶元素是: %d\n", stack_peek(stack)); // 应该输出 3
22
23// 出栈操作,并打印出栈的元素
24printf("依次出栈以下元素:\n");
25while (!is_empty(stack)) {
26printf("%d\n", stack_pop(stack)); // 应该依次输出 3, 2, 1
27}
28
29// 再次尝试查看栈顶元素,预期会触发错误提示
30printf("栈为空时,尝试访问栈顶元素:\n");
31stack_peek(stack); // 应该输出错误信息并退出
32
33// 销毁栈
34stack_destroy(stack); return 0;
35}
以上。
Gn!
基于以下动态数组队列的头文件定义,实现一个动态数组队列:
xxxxxxxxxx
35123
4567
8typedef int ElementType; // 该队列当前存储int元素
9// 数组队列的初始长度是10
10// 超过阈值每次扩容1.5倍扩,否则2倍的扩
11
12// 定义队列结构体
13typedef struct {
14ElementType *data; // 动态数组存储队列元素
15int front; // 标记队头元素的索引
16int rear; // 标记队尾元素下一个位置的索引
17int size; // 当前队列中元素数量
18int capacity; // 队列容量
19} DynamicQueue;
20
21// 队列基本操作函数声明
22// 创建动态数组队列
23DynamicQueue *create_queue();
24// 销毁动态数组队列
25void destroy_queue(DynamicQueue *q);
26// 判空
27bool is_empty(DynamicQueue *q);
28// 判满
29bool is_full(DynamicQueue *q);
30// 入队列
31bool enqueue(DynamicQueue *q, ElementType data);
32// 出队列并且返回队头元素
33ElementType dequeue(DynamicQueue *q);
34
35// DYNAMIC_QUEUE_H
注意:你要实现一个循环队列,而不是一个普通队列。
参考代码如下:
参考代码:
以下是实现源文件的代码,即所有函数的实现如下:
xxxxxxxxxx
9512
3// 创建并初始化队列
4DynamicQueue *create_queue() {
5DynamicQueue *q = calloc(1, sizeof(DynamicQueue));
6if (q == NULL) {
7printf("error: calloc failed in create_queue.\n");
8return NULL;
9}
10// front、rear、size自动初始化0值,无需再手动初始化了
11q->data = calloc(DEFAULT_CAPACITY, sizeof(ElementType)); // 使用calloc避免随机值
12if (q->data == NULL) {
13printf("error: calloc failed in create_queue.\n");
14free(q);
15return NULL;
16}
17q->capacity = DEFAULT_CAPACITY;
18return q;
19}
20
21// 销毁队列
22void destroy_queue(DynamicQueue *q) {
23free(q->data);
24free(q);
25}
26
27// 检查队列是否为空
28bool is_empty(DynamicQueue *q) {
29return q->size == 0;
30}
31
32// 检查队列是否已满
33bool is_full(DynamicQueue *q) {
34return q->size == q->capacity;
35}
36
37/*
38这里采用一种简单粗暴的扩容手段:
39直接分配新容量的内存块
40然后遍历旧内存块中的队列元素,将这些元素全部从头开始复制到新内存块中
41这样的操作在完成后,需要更新front索引和rear索引
42*/
43static bool resize_queue(DynamicQueue *q) {
44int old_capacity = q->capacity;
45int new_capacity = (old_capacity < THRESHOLD) ?
46(old_capacity << 1) :
47(old_capacity + (old_capacity >> 1));
48
49// 重新分配一个新的,更长的动态数组
50ElementType *new_data = malloc(new_capacity * sizeof(ElementType));
51if (new_data == NULL) {
52printf("error: realloc failed in resize_queue.\n");
53return false;
54}
55
56int curr = q->front; // curr索引用于遍历整个队列中的元素
57int index = 0;
58while (index < q->size) {
59new_data[index] = q->data[curr];
60curr = (curr + 1) % q->capacity;
61index++;
62} // while循环结束时,new_data就从头开始包含了队列的所有元素
63free(q->data);
64q->data = new_data;
65q->front = 0;
66q->rear = q->size;
67q->capacity = new_capacity;
68return true;
69}
70
71// 入队操作
72bool enqueue(DynamicQueue *q, ElementType data) {
73if (is_full(q)) {
74if (!resize_queue(q)) {
75printf("error: 扩容失败.\n");
76return false; // 队列扩容失败,入队也同步失败
77}
78}
79q->data[q->rear] = data;
80q->rear = (q->rear + 1) % q->capacity; // 循环队列
81q->size++;
82return true;
83}
84
85// 出队操作
86ElementType dequeue(DynamicQueue *q) {
87if (is_empty(q)) {
88printf("error: 队列为空,无法出队。\n");
89exit(1);
90}
91ElementType item = q->data[q->front];
92q->front = (q->front + 1) % q->capacity; // 循环队列
93q->size--;
94return item;
95}
测试用例代码:
xxxxxxxxxx
35123
4int main(void) {
5// 创建队列
6DynamicQueue *queue = create_queue();
7if (queue == NULL) {
8printf("创建队列失败。\n");
9return 1;
10}
11
12// 入队测试
13for (int i = 0; i < 15; i++) {
14if (enqueue(queue, i)) {
15printf("已入队: %d\n", i);
16}
17else {
18printf("入队失败: %d\n", i);
19}
20}
21
22// 打印队列容量和大小
23printf("队列容量: %d, 队列大小: %d\n", queue->capacity, queue->size);
24
25// 出队测试
26printf("正在出队...\n");
27while (!is_empty(queue)) {
28printf("已出队: %d\n", dequeue(queue));
29}
30
31// 销毁队列
32destroy_queue(queue);
33
34return 0;
35}
以上。
Gn!
在上述已实现的一个链式栈(数组栈也行)的基础上,编写代码完成字符串括号的匹配校验。
括号都是英文字符括号,而且有三类:{}、[]、()
注:
1.若字符串中没有括号,也算做括号匹配成功。
2.思路可以参考文档《栈》
参考代码如下:
参考代码如下:
首先是链式栈的头文件,该文件中需要修改栈中元素的类型为char类型:
x123456
7// 由于栈中要存储字符,所以元素类型要改成char类型
8typedef char ElementType;
9
10// 栈的一个结点栈帧,类型定义
11typedef struct node_s {
12ElementType data;
13struct node_s *next;
14}StackFrame;
15
16typedef struct {
17StackFrame *top; // 栈顶指针
18}LinkedStack;
19
20// 基本操作
21// 创建链式栈
22LinkedStack *stack_create();
23// 销毁链式栈
24void stack_destroy(LinkedStack *stack);
25// 判空
26bool is_empty(LinkedStack *stack);
27// 入栈
28void stack_push(LinkedStack *stack, ElementType data);
29// 出栈并返回栈顶元素
30ElementType stack_pop(LinkedStack *stack);
31// 访问栈顶元素
32ElementType stack_peek(LinkedStack *stack);
33
34// !LINKED_STACK_H
链式栈的实现代码:
x12
3// 新建一个空栈
4LinkedStack *stack_create() {
5// callock可以不用手动初始化空指针
6return calloc(1, sizeof(LinkedStack));
7}
8// 对于链式栈而言,销毁栈就是销毁链表
9void stack_destroy(LinkedStack *stack) {
10// 相当于遍历链表(出栈)然后在遍历中逐个free结点
11StackFrame *curr = stack->top;
12while (curr != NULL) {
13StackFrame *tmp = curr->next; // 保存后继结点
14free(curr);
15curr = tmp;
16}
17// 最后free栈结构体
18free(stack);
19}
20
21bool is_empty(LinkedStack *stack) {
22return stack->top == NULL;
23}
24
25// 相当于链表头插法插入结点,栈顶指针相当于链表的头指针
26void stack_push(LinkedStack *stack, ElementType data) {
27// 1.新建一个栈帧结点
28StackFrame *new_frame = malloc(sizeof(StackFrame));
29if (new_frame == NULL) {
30printf("malloc failed in stack_push.\n");
31exit(1);
32}
33// 2.初始化新栈帧
34new_frame->data = data;
35new_frame->next = stack->top;
36// 3.更新栈顶指针
37stack->top = new_frame;
38}
39
40// 相当于链表在头部删除第一个结点,栈顶指针相当于链表的头指针
41ElementType stack_pop(LinkedStack *stack) {
42// 1.栈判空处理
43if (is_empty(stack)) {
44printf("error: stack is empty.\n");
45exit(1);
46}
47// 2.出栈返回栈顶元素,并free结点,更新栈顶指针
48StackFrame *curr = stack->top;
49ElementType data = curr->data;
50stack->top = curr->next;
51free(curr);
52return data;
53}
54ElementType stack_peek(LinkedStack *stack) {
55if (is_empty(stack)) {
56printf("error: stack is empty.\n");
57exit(1);
58}
59return stack->top->data;
60}
括号匹配函数以及相关的测试代码:
x123456// 已实现的链式栈
7
8char get_right_bracket(char ch) {
9switch (ch) {
10case '{': return '}';
11case '[': return ']';
12case '(': return ')';
13default: return 0;
14}
15}
16bool is_left_bracket(char ch) {
17return ch == '{'
18|| ch == '['
19|| ch == '(';
20}
21
22bool is_right_bracket(char ch) {
23return ch == '}'
24|| ch == ']'
25|| ch == ')';
26}
27
28bool is_matching(char *str, LinkedStack *stack) {
29while (*str) {
30// 排除非括号字符
31if (is_left_bracket(*str)) {
32// 字符是左括号,于是将它对应的右括号入栈
33char right_bracket = get_right_bracket(*str);
34stack_push(stack, right_bracket);
35}
36if (is_right_bracket(*str)) {
37// 如果遇到右括号时栈为空,说明右括号在前面,匹配失败
38// 如果栈不为空就出栈,和当前字符比较,如果不一致就匹配失败
39if (is_empty(stack) || stack_pop(stack) != *str) {
40return false;
41}
42}
43str++;
44}
45// 字符串遍历完成,判断栈是否空
46return is_empty(stack);
47}
48
49int main(void) {
50LinkedStack *stack = stack_create();
51char *str = "1()23{[}";
52if (is_matching(str, stack)) {
53printf("匹配成功!\n");
54}
55else {
56printf("匹配失败!\n");
57}
58stack_destroy(stack);
59return 0;
60}
以上。
Gn!
以下题目都属于扩展题。
Gn!
基于以下头文件,手动实现一个动态数组栈:
x123456
7typedef int ElementType;
8
9typedef struct {
10ElementType *elements; // 指向动态数组首元素的指针
11int size; // 元素的个数
12int capacity; // 数组的容量,也就是栈的容量
13} DynamicStack;
14
15// 创建动态数组栈
16DynamicStack *stack_create();
17// 销毁动态数组栈
18void stack_destroy(DynamicStack *s);
19// 入栈
20void stack_push(DynamicStack *s, ElementType val);
21// 出栈并返回栈顶元素
22ElementType stack_pop(DynamicStack *s);
23// 访问栈顶元素
24ElementType stack_peek(DynamicStack *s);
25// 判空
26bool is_empty(DynamicStack *s);
27
28// !DYNAMIC_ARR_STACK_H
当栈中元素已满时,就需要扩容此数组,扩容策略可以自行拟定。
参考代码如下:
参考代码实现如下:
以下是实现源文件的代码,即所有函数的实现如下:
x12// 动态栈的默认容量
3// 扩容阈值
4
5// 实现扩容机制
6static void grow_capacity(DynamicStack *s) {
7// 扩容策略: 超过阈值则1.5倍的扩容,否则2倍的扩容
8int new_capacity = (s->capacity >= THRESHOLD) ?
9(s->capacity + (s->capacity >> 1)) :
10(s->capacity << 1);
11
12// 使用realloc函数实现扩容,重新分配内存
13ElementType *new_arr = realloc(s->elements, new_capacity * sizeof(ElementType));
14if (new_arr == NULL) {
15printf("Error: realloc failed in grow_capacity\n");
16exit(1);
17}
18// 更新动态数组栈结构体的信息
19s->elements = new_arr;
20s->capacity = new_capacity;
21}
22// 创建一个动态数组栈
23DynamicStack *stack_create() {
24DynamicStack *s = malloc(sizeof(DynamicStack));
25if (s == NULL) {
26printf("malloc failed in stack_create.\n");
27return NULL;
28}
29// 初始化动态数组
30s->size = 0;
31s->capacity = DEFAULT_CAPACITY;
32s->elements = malloc(DEFAULT_CAPACITY * sizeof(ElementType));
33if (s->elements == NULL) {
34free(s);
35printf("malloc failed in stack_create.\n");
36return NULL;
37}
38return s;
39}
40// 销毁一个栈
41void stack_destroy(DynamicStack *s) {
42// 先释放动态数组
43free(s->elements);
44// 再释放栈结构体
45free(s);
46}
47
48// 将数组末尾视为栈顶,将size属性作为新插入结点的下标,这就是入栈
49void stack_push(DynamicStack *s, ElementType val) {
50if (s->capacity == s->size) {
51// 栈满了,需要扩容
52grow_capacity(s);
53}
54
55// 可以直接把size作为入栈出栈的操作索引
56s->elements[s->size] = val;
57s->size++;
58// 上面的两行代码可以合并成一行
59// s->elements[s->size++] = val;
60}
61
62// 将数组末尾视为栈顶,将size属性减1,这就是出栈。元素取值并不用改变
63ElementType stack_pop(DynamicStack *s) {
64if (is_empty(s)) {
65printf("Error: stack is empty\n");
66exit(1);
67}
68s->size--;
69return s->elements[s->size];
70
71// 上面的两行代码可以合并成一行
72// return s->elements[--(s->size)];
73}
74// 访问栈顶元素
75ElementType stack_peek(DynamicStack *s) {
76if (is_empty(s)) {
77printf("Error: stack is empty\n");
78exit(1);
79}
80return s->elements[s->size - 1];
81}
82// 判空
83bool is_empty(DynamicStack *s) {
84return s->size == 0;
85}
测试用例代码如下:
xxxxxxxxxx
2212
3int main(void) {
4DynamicStack *s = stack_create();
5// 入队列1000个元素
6for (int i = 0; i < 1000; i++) {
7stack_push(s, i);
8}
9// 出队列直到队列为空
10while (!is_empty(s)) {
11ElementType ele = stack_peek(s);
12printf("%d ", ele); // 预期会将999-0倒着输出打印
13stack_pop(s);
14}
15printf("\n");
16// 再次尝试出栈, 此时会打印错误提示信息
17stack_pop(s);
18// 销毁队列
19stack_destroy(s);
20
21return 0;
22}
以上。
Gn!
基于以下头文件,手动实现一个链式队列:
xxxxxxxxxx
37123
4567
8// 定义队列中的元素类型
9typedef int ElementType;
10
11// 队列节点的结构
12typedef struct node_s {
13ElementType data;
14struct node_s *next;
15} QueueNode;
16
17// 队列的结构
18typedef struct {
19QueueNode *front; // 队头结点指针
20QueueNode *rear; // 队尾结点指针
21} LinkedListQueue;
22
23// 函数声明
24// 创建链式队列
25LinkedListQueue *create_queue();
26// 销毁链式队列
27void destroy_queue(LinkedListQueue *q);
28// 入队列
29bool enqueue(LinkedListQueue *q, ElementType element);
30// 出队列并返回队头元素
31ElementType dequeue(LinkedListQueue *q);
32// 访问队头元素
33ElementType peek_queue(LinkedListQueue *q);
34// 判空
35bool is_empty(LinkedListQueue *q);
36
37// !LIST_QUEUE_H
注意:链式队列显然不具有满的概念,所以也不需要执行判满操作。
参考代码如下:
参考代码实现如下:
以下是实现源文件的代码,即所有函数的实现如下:
xxxxxxxxxx
8212
3// 函数声明
4LinkedListQueue *create_queue() {
5return calloc(1, sizeof(LinkedListQueue));
6}
7
8void destroy_queue(LinkedListQueue *q) {
9// 从队头开始遍历链表,销毁每一个结点
10QueueNode *current = q->front;
11while (current != NULL) {
12QueueNode *temp = current->next;
13free(current);
14current = temp;
15}
16// 销毁队列结构体
17free(q);
18}
19
20bool is_empty(LinkedListQueue *q) {
21// 队头指针是空指针,即表示空队列
22return q->front == NULL;
23}
24
25// 入队操作: 只能在队尾插入一个结点
26// 由于已存在尾指针,所以这里的操作就是链表尾插
27bool enqueue(LinkedListQueue *q, ElementType element) {
28QueueNode *new_node = malloc(sizeof(QueueNode));
29if (new_node == NULL) {
30printf("Error: malloc failed in enqueue.\n");
31return false;
32}
33// 初始化新结点
34new_node->data = element;
35new_node->next = NULL;
36
37// 开始进行尾插法插入一个结点
38// 分两种情况:如果尾插插入的是第一个结点需要同步更新头指针,否则仅需要更新尾指针
39if (q->front == NULL) {
40// 插入的是第一个结点
41q->front = new_node;
42q->rear = new_node;
43}
44else {
45// 插入的不是第一个结点
46q->rear->next = new_node;
47q->rear = new_node;
48}
49return true;
50}
51
52// 出队,在队头删除一个结点。也就是在删除链表的第一个结点
53ElementType dequeue(LinkedListQueue *q) {
54if (is_empty(q)) {
55printf("Error: queue is empty.\n");
56exit(1);
57}
58
59QueueNode *tmp = q->front;
60// 将出队的结点数据保存
61ElementType remove_data = tmp->data;
62
63// 更新队头指针
64q->front = tmp->next;
65if (q->front == NULL) {
66// 如果队头更新后,队列为空,说明出队的就是最后一个元素
67// 于是同步更新队尾指针
68q->rear = NULL;
69}
70
71free(tmp);
72return remove_data;
73}
74
75// 访问队头元素但不出队
76ElementType peek_queue(LinkedListQueue *q) {
77if (is_empty(q)) {
78printf("Error: queue is empty.\n");
79exit(1);
80}
81return q->front->data;
82}
测试用例代码如下:
xxxxxxxxxx
221234
5int main(void) {
6LinkedListQueue *q = create_queue();
7// 逐一入队列
8enqueue(q, 1);
9enqueue(q, 2);
10enqueue(q, 3);
11enqueue(q, 4);
12enqueue(q, 5);
13enqueue(q, 6);
14// 先进先出,出队列全部元素直到队列为空
15while (!is_empty(q)) {
16int val = peek_queue(q);
17printf("%d ", val); // 预期出队列顺序:1 2 3 4 5 6
18dequeue(q);
19}
20printf("\n");
21return 0;
22}
以上。