实验0 课程先导实验报告
文章目录
一. 代码实现部分
1.1 队列节点与队列数据结构
-
修改后代码的数据结构
typedef struct ELE { char *value; struct ELE *next; } list_ele_t; /* 队列结构 */ typedef struct { list_ele_t *head; /* 头指针 */ list_ele_t *tail; /* 尾指针 */ int size; /* 节点数量 */ } queue_t;
list_ele_t 结构和 queue_t 结构一起实现如下图所示的字符串队列。queue_t 结构定义中有一个域 head,存储指向队列的指针。而另一个域tail,则储存队列的尾部的节点,最后添加int型数据size来记录添加节点个数,每个元素用 list_ele_t 结构表示,有 value 和 next 两个域,存储指向一个字符串的指针和指向下一个链表元素的指针,字符用ASCII编码,最后一个元素的next 指针设置为 NULL。
-
数据结构参考图
1.2 q_new的实现
-
完成后代码
queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); if (q == NULL) // 若空则返回NULL return NULL; q->tail = NULL; q->head = NULL; // 初始化各个指针 q->size = 0; // 初始化size return q; }
该部分修改对malloc返回空进行了处理,同时也对新添加的域进行初始化。
1.3 q_free的实现
-
完成后代码
void q_free(queue_t *q) { if (q == NULL) return; list_ele_t *temp; while (q->head != NULL) { temp = q->head; q->head = q->head->next; free(temp->value); temp->value = NULL; free(temp); temp = NULL; } free(q); }
该部分实现思路为从头节点下手,用temp指针充当临时指针来释放头节点,并且使用queue结构的头指针指向其下一个节点以防节点丢失。
-
思路参考图
1.4 q_insert_head的实现
-
完成后代码
bool q_insert_head(queue_t *q, char *s) { if (q == NULL) return false; list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if (newh == NULL) return false; newh->value = malloc(strlen(s) + 1); if (newh->value == NULL) { free(newh); return false; } strcpy(newh->value, s); newh->next = NULL; if (q->head == NULL) q->tail = newh; newh->next = q->head; q->head = newh; q->size++; return true; }
该部分代码修改后考虑到了malloc失败的情况以及第一次插入的情况。malloc失败的情况分两个部分,第一种情况是newh分配失败,那么就可以直接返回false而不必考虑后续情况,第二种情况为newh->value分配失败,这种情况下返回false前需要释放之前成功分配的newh的空间以免发生内存泄漏。第一次插入需要考虑到尾指针的情况,此时因为第一次插入,所以此时的节点即是第一个也是最后一个,因此需要将尾指针指向该节点。当然,每次节点插入成功我们都会使size++用来记录此时节点数量。
-
思路参考图
- 第一次插入参考图(由于与尾插法思虑相似,故尾插法不再展示第一次插入)
1.5 q_insert_tail的实现
-
完成后代码
bool q_insert_tail(queue_t *q, char *s) { if (q == NULL) return false; list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if (newh == NULL) return false; newh->value = malloc(strlen(s) + 1); if (newh->value == NULL) { free(newh); return false; } strcpy(newh->value, s); newh->next = NULL; if (q->tail == NULL) { q->head = newh; q->tail = newh; } q->tail->next = newh; q->tail = newh; q->size++; return true; }
该部分代码整体与头插法相似,故具体思路可参考 1.4 部分头插法代码说明。
-
思路参考图
1.6 q_remove_head的实现
-
完成后代码
bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (q == NULL || q->head == NULL) return false; if (sp != NULL) { strncpy(sp, q->head->value, bufsize - 1); sp[bufsize - 1] = '\0'; } list_ele_t *temp = q->head; q->head = q->head->next; free(temp->value); temp->value = NULL; free(temp); temp = NULL; q->size--; return true; }
该部分代码主要为字符串拷贝部分以及释放内存空间部分。字符串拷贝需要遵守bufsize的大小,否则调试代码的时候会发生溢出,并且’\0’的置入也非常重要,否则显示的sp后面会出现许多 X 。释放空间部分需要注意先释放value后释放节点,否则会丢失指针。
-
思路参考图
该思路参考图可参考 1.3 部分的参考图,去掉第四步即是我们需要的参考图。
1.7 q_size的实现
-
完成后代码
int q_size(queue_t *q) { if (q != NULL && q->head != NULL) return q->size; return 0; }
该部分代码只需注意空队列返回0即可。而size的大小只需访问结构体中记录的size就可以知道队列的大小。
1.8 q_reverse的实现
-
完成后代码
void q_reverse(queue_t *q) { if (q == NULL || q->head == NULL) return; list_ele_t *temp1; list_ele_t *temp2; temp1 = q->head; q->tail->next = q->head; q->head = q->head->next; temp1->next = NULL; while (q->head != q->tail) { temp2 = q->head; q->head = q->head->next; temp2->next = q->tail->next; q->tail->next = temp2; } q->tail = temp1; temp1 = temp2 = NULL; }
该部分代码思路主要分为两个部分:第一个部分主要记录旧头节点的位置并将其移动至尾部,然后将新头指针指向下一个节点;第二部分主要将不断产生的新头节点移至旧尾节点的前面,不断循环至头指针指向旧尾节点,最后将尾指针指向第一部分记录的旧头节点。
-
思路参考图第一部分
- 思路参考图第二部分
二. 代码测试部分
代码测试截图
通过不断调试与修改得到以上结果