马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
标题:
基于以下头文件,手动实现一条单链表:
- // 头文件保护语法
- #ifndef LINKED_LIST_H
- #define LINKED_LIST_H
- // 包含linked_list.h头文件也会同步包含它包含的其它头文件
- // 根据这一特点, 可以选择将一些共用的头文件包含在此头文件中
- #include <stdbool.h>
- #include <stdlib.h>
- #include <stdio.h>
- typedef int DataType;
- // 定义链表结点结构
- typedef struct node {
- DataType data; // 数据域c
- struct node *next; // 指针域
- } Node;
- // 定义链表结构本身
- typedef struct {
- Node *head; // 头指针
- Node *tail; // 尾结点指针
- int size; // 用于记录链表的长度
- } LinkedList;
- // 创建一个空的链表
- LinkedList *create_linked_list();
- // 销毁链表
- void destroy_linked_list(LinkedList *list);
- // 头插法
- bool add_before_head(LinkedList *list, DataType new_data);
- // 尾插法
- bool add_behind_tail(LinkedList *list, DataType new_data);
- // 根据索引插入一个新结点
- bool add_by_idx(LinkedList *list, int idx, DataType new_data);
- // 根据索引搜索一个结点
- Node *search_by_idx(LinkedList *list, int idx);
- // 根据数据搜索一个结点
- Node *search_by_data(LinkedList *list, DataType data);
- // 根据数据删除一个结点
- bool delete_by_data(LinkedList *list, DataType data);
- // 扩展: 根据索引删除一个结点
- bool delete_by_idx(LinkedList *list, int idx);
- // 打印单链表 格式为:1 -> 2 -> 3 ->...
- void print_list(LinkedList *list);
-
- #endif // !LINKED_LIST_H
- 手动实现单链表是后续一切数据结构学习的前提和基础,所以一定要重视单链表的实现,一定要自己百分百手动将这里面的函数全部实现。
复制代码 关键点
分析:
:
代码
- //实现源文件的代码
- #include "linkedlist.h"
- #include <stdio.h>
- #include <stdlib.h>
- // 创建一个空的链表
- LinkedList *create_linked_list() {
- return calloc(1, sizeof(LinkedList));
- }
- // 销毁链表
- void destroy_linked_list(LinkedList *list) {
- // 遍历整条链表,包括最后一个结点,然后逐一free
- Node *curr = list->head;
- while (curr != NULL) {
- Node *tmp = curr->next;
- free(curr);
- curr = tmp;
- }
- free(list);
- }
- // 头部插入结点
- bool add_before_head(LinkedList *list, DataType data) {
- // 1.分配一个新结点,初始化它
- Node *new_node = malloc(sizeof(Node));
- if (new_node == NULL) {
- printf("error: malloc failed in add_before_head.\n");
- return false;
- }
- new_node->data = data;
- // 2.让新结点指向原本的第一个结点
- new_node->next = list->head;
- // 3.更新头指针
- list->head = new_node;
- // 考虑特殊情况
- //if(list->size == 0)
- if (list->tail == NULL) {
- // 说明头插之前链表为空, 更新尾指针
- list->tail = new_node;
- }
- list->size++;
- return true;
- }
- // 尾部插入结点
- bool add_behind_tail(LinkedList *list, DataType data) {
- // 1.分配一个新结点,初始化它
- Node *new_node = malloc(sizeof(Node));
- if (new_node == NULL) {
- printf("error: malloc failed in add_behind_tail.\n");
- return false;
- }
- new_node->data = data;
- new_node->next = NULL;
- // 2.判断链表插入之前是否为空
- if (list->size == 0) {
- // 尾插之前链表为空
- list->head = new_node;
- list->tail = new_node;
- list->size++;
- return true;
- }
- // 链表尾插之前不为空
- list->tail->next = new_node;
- list->tail = new_node;
- list->size++;
- return true;
- }
- // 按索引插入结点
- /*
- 对于链表来说,第一个结点的索引规定是0,那么链表的结点索引合法范围是[0, size-1]
- 分析:
- 1.参数校验,对错误的参数进行错误的处理
- index在这个函数中,合法的取值范围是[0, size]
- 其中0表示头插, size表示尾插
- 2.处理特殊值,尾插和头插直接调用已实现的函数
- 3.在index合法,且不是头插尾插,即在中间插入结点时
- */
- bool add_by_idx(LinkedList *list, int index, DataType data) {
- if (index < 0 || index > list->size) {
- printf("Illegal Arguments: index = %d\n", index);
- return false;
- }
- if (index == 0) {
- return add_before_head(list, data);
- }
- if (index == list->size) {
- return add_behind_tail(list, data);
- }
- // 处理中间插入结点的情况
- Node *new_node = malloc(sizeof(Node));
- if (new_node == NULL) {
- printf("error: malloc failed in add_index.\n");
- return false;
- }
- new_node->data = data;
- // 遍历链表,找到index-1位置的结点
- Node *prev = list->head;
- for (int i = 0; i < index - 1; i++) {
- prev = prev->next;
- } // for循环结束时,prev指针指向index索引的前驱结点
- // 让新结点指向prev的后继结点
- new_node->next = prev->next;
- // 让prev指向新结点
- prev->next = new_node;
- list->size++;
- return true;
- }
- // 根据索引搜索结点
- Node *search_by_idx(LinkedList *list, int index) {
- if (index < 0 || index > list->size - 1) { // size-1就是最后一个结点
- printf("Illegal Arguments: index = %d\n", index);
- return NULL;
- }
- // 索引合法
- Node *curr = list->head;
- for (size_t i = 0; i < index; i++) // 小于index就表示寻找index索引的结点
- {
- curr = curr->next;
- } // for循环结束时,curr指针指向index索引位置的结点
- return curr;
- }
- // 根据数据搜索结点
- Node *search_by_data(LinkedList *list, DataType data) {
- // 惯用法遍历每一个结点,遍历过程中判断即可
- Node *curr = list->head;
- while (curr != NULL) {
- if (curr->data == data) {
- // 找到了
- return curr;
- }
- curr = curr->next;
- }
- // 遍历结束都没有return 说明没有找到
- return NULL;
- }
- // 根据数据删除结点
- /*
- 对于一条单链表来说, 在一个确定的结点的后面进行删除/新增操作, 它的时间复杂度是O(1)
- 链表的结点删除,哪些是O(1)的呢?
- 1.删除第一个结点是不是? 是,因为这个过程基本只需要改变头指针的指向就可以了
- 2.删除最后一个结点是不是呢? 不是, 因为删除最后一个结点,需要找到倒数第二个结点,这需要遍历数组,时间复杂度是O(n)
- 3.删除中间的结点是不是呢? 肯定不是,因为需要遍历找到这个结点
- 于是将单链表的删除分解成两个步骤:
- 1.假如删除的是第一个结点
- 2.如果删除的是第一个结点外的其它结点
- */
- bool delete_by_data(LinkedList *list, DataType data) {
- // 校验: 如果链表为空,不删除了
- if (list->head == NULL) {
- printf("链表为空,无法删除!\n");
- return false;
- }
- Node *curr = list->head; // 该指针用于遍历整条单链表
- // 1.处理删除的是第一个结点的情况
- if (curr->data == data) {
- list->head = curr->next; // 这步操作不论链表是否只有一个结点 都是要做的
- if (list->head == NULL) {
- // 说明链表仅有一个结点,删除后,要同步更新尾指针
- list->tail = NULL;
- }
- free(curr);
- list->size--;
- return true;
- }
- // 2.处理其他结点的删除情况
- // 已经有一个指针,叫curr,它目前指向第一个结点
- Node *prev = curr;
- curr = curr->next;
- while (curr != NULL) { // 从第二个结点开始,遍历完整条单链表
- if (curr->data == data) {
- // 找到了待删除目标结点,curr指向它,此时prev指向它的前驱
- prev->next = curr->next;
- if (curr->next == NULL) {
- // 待删除结点是尾结点
- list->tail = prev;
- }
- free(curr);
- list->size--;
- return true;
- }
- prev = curr;
- curr = curr->next;
- } // while循环结束时,curr指针指向NULL,prev指向链表的最后一个结点
- // while循环结束都没有return, 说明没找到目标data的结点,删除失败
- return false;
- }
- // 根据索引删除结点
- bool delete_by_idx(LinkedList *list, int idx) {
- if (list->head == NULL) {
- printf("链表为空,无法删除!\n");
- return false;
- }
- // 检查链表是否为空或索引是否越界
- if (idx < 0 || idx >= list->size) {
- printf("Illegal Arguments: index = %d\n", idx);
- return false;
- }
- Node *curr = list->head;
- Node *prev = NULL;
- // 如果要删除的是第一个结点
- if (idx == 0) {
- list->head = curr->next; // 更新头指针
- // 若删除的结点是唯一的结点
- if (list->size == 1) {
- list->tail = NULL; // 更新尾指针
- }
- free(curr);
- list->size--;
- return true;
- }
- // 如果删除的结点不是第一个结点
- for (int i = 0; i < idx; i++) {
- prev = curr;
- curr = curr->next;
- }
- // 让前驱结点指向后继结点
- prev->next = curr->next;
- // 如果要删除的是尾结点, 则需要更新尾指针
- if (idx == list->size - 1) {
- list->tail = prev;
- }
- free(curr);
- list->size--;
- return true;
- }
- // 打印单链表 格式为:1 -> 2 -> 3 ->...
- void print_list(LinkedList *list) {
- Node *curr = list->head;
- // 遍历此单链表
- while (curr != NULL) {
- printf("%d", curr->data);
- // 如果不是链表的最后一个结点,就打印箭头
- if (curr->next != NULL) {
- printf(" -> ");
- }
- curr = curr->next;
- }
- printf("\n");
- }
-
复制代码- //
- // 实现尾插法构建链表
- void insert_tail(Node **head, int new_data) {
- // 1.分配新结点,初始化它
- Node *new_node = malloc(sizeof(Node));
- if (new_node == NULL) {
- printf("error: malloc failed in insert_tail.\n");
- exit(1);
- }
- new_node->data = new_data;
- new_node->next = NULL;
- // 3.链表非空时,让原本最后一个结点指向新结点
- if (*head != NULL) {
- // 2.遍历找到最后一个结点
- Node *end = *head;
- while (end->next != NULL) {
- end = end->next;
- } // while循环结束时, end指向最后一个结点
- end->next = new_node;
- return;
- }
- // 链表尾插之前是空的,那就直接更新头指针就行了
- *head = new_node;
- }
- ```c
- ```c
- 在这里插入代码片
复制代码 [code][/code] 办理方案总结:
:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |