学会单链表的创建, 添加, 插入, 删除.
学习指导:帆神的代码
- 抄代码.
- 进行 insertElement 等函数进一步的测试.
- 挑本贴 bug 并留言.
- 写 CSDN 博客. 必须有图示, 可以用工具画, 或者手绘拍照.
学习成果目录
2022.4.28
1.准备工作
1.1链表的结构体
/** * 先把链表定义好 */ typedef struct LinkNode { char data; struct LinkNode *next; } LNode, *LinkList, *NodePtr;
1.2创建头节点并初始化时间复杂度O(1)
/** * @brief 初始化链表头节点 * * @return tempHeader */ LinkList initLinkList() { NodePtr tempHeader = (NodePtr)malloc(sizeof(LNode)); tempHeader->data = '\0'; tempHeader->next = NULL; return tempHeader; }
1.3打印链表时间复杂度O(n)
/** * @brief 打印链表 * * @param paraHeader */ void printList(NodePtr paraHeader) { NodePtr p = paraHeader->next; while (p != NULL) { printf("%C", p->data); p = p->next; } printf("\n"); }
1.4尾插法插入元素时间复杂度O(n)
/** * @brief 尾插法添加一个节点 * * @param paraChar * @param paraHeader */ void appendElement(NodePtr paraHeader, char paraChar) { NodePtr p, q; //1创建一个新节点 q = (NodePtr)malloc(sizeof(LNode)); q->data = paraChar; q->next = NULL; //2找到尾节点 p = paraHeader; while (p->next != NULL) { p = p->next; } //3把新节点插入尾节点后面 p->next = q; }
2.插入操作 时间复杂度O(n)
/** * @brief 在指定位置插入一个节点 * * @param paraChar * @param paraHeader * @param paraPosition */ void insertElement(NodePtr paraHeader, char paraChar, int paraPosition) { NodePtr p, q; //1创建新节点 q = (NodePtr)malloc(sizeof(LNode)); q->data = paraChar; //2找到插入节点位置 p = paraHeader; for (int i = 0; i < paraPosition; i++) { p = p->next; if (p == NULL) { //未找到节点位置,报错 printf("位置%d超出了链表长度范围", paraPosition); return ; } } //3把新建节点插入链表 //为什么要打印个linking?????? printf("插入节点%c成功\r\n",paraChar); q->next = p->next; p->next = q; }
3.删除操作 时间复杂度O(n)
/** * @brief 根据元素的数据删除链表节点 * * @param paraChar * @param paraHeader */ void deleteElement(NodePtr paraHeader, char paraChar) { NodePtr p, q; //1找到要删除节点的前一个节点 p = paraHeader; while ((p->next != NULL) && (p->next->data != paraChar)) { p = p->next; } if (p->next == NULL) { printf("无法找到所需要删除的数据%c\r\n", paraChar); return; } //释放所需要删除节点 //把删除节点的前一个节点指向删除节点后一个节点 q = p->next; p->next = p->next->next; free(q); }
4.两个测试类及测试结果
4.1增删测试类
/** * @brief 增删测试类 */ void appendInsertDeleteTest() { printf("----appendInsertDeleteTest开始测试-----\n"); //1初始化空链表 LinkList tempList = initLinkList(); printList(tempList); //2加入一些节点 appendElement(tempList, 'H'); appendElement(tempList, 'e'); appendElement(tempList, 'l'); appendElement(tempList, 'l'); appendElement(tempList, 'o'); appendElement(tempList, '!'); printList(tempList); //3删除节点 deleteElement(tempList, 'e'); deleteElement(tempList, 'a'); deleteElement(tempList, 'o'); printList(tempList); //4在位置1初插入一个o insertElement(tempList, 'o', 1); printList(tempList); printf("--------------**********-------------\n\n"); }
4.2地址分配观察测试类
/** * @brief 地址分配观察测试类 */ void basicAddressTest() { printf("------basicAddressTest开始测试-------\n"); LNode tempNode1, tempNode2; tempNode1.data = 4; tempNode1.next = NULL; tempNode2.data = 6; tempNode2.next = NULL; printf("The first node: %d, %d, %d\r\n", &tempNode1, &tempNode1.data, &tempNode1.next); printf("The second node: %d, %d, %d\r\n", &tempNode2, &tempNode2.data, &tempNode2.next); tempNode1.next = &tempNode2; printf("--------------**********-------------\n\n"); }
4.3测试结果
----appendInsertDeleteTest开始测试----- Hello! 无法找到所需要删除的数据a Hll! 插入节点o成功 Holl! --------------**********------------- ------basicAddressTest开始测试------- The first node: -2080376352, -2080376352, -2080376344 The second node: -2080376368, -2080376368, -2080376360 --------------**********------------- -------------------------------- Process exited after 0.03241 seconds with return value 0 Press ANY key to continue...
5.全部代码
#include <stdio.h> #include <malloc.h> /** * 先把链表定义好 */ typedef struct LinkNode { char data; struct LinkNode *next; } LNode, *LinkList, *NodePtr; /** * @brief 初始化链表头节点 * * @return tempHeader */ LinkList initLinkList() { NodePtr tempHeader = (NodePtr)malloc(sizeof(LNode)); tempHeader->data = '\0'; tempHeader->next = NULL; return tempHeader; } /** * @brief 打印链表 * * @param paraHeader */ void printList(NodePtr paraHeader) { NodePtr p = paraHeader->next; while (p != NULL) { printf("%C", p->data); p = p->next; } printf("\n"); } /** * @brief 尾插法添加一个节点 * * @param paraChar * @param paraHeader */ void appendElement(NodePtr paraHeader, char paraChar) { NodePtr p, q; //1创建一个新节点 q = (NodePtr)malloc(sizeof(LNode)); q->data = paraChar; q->next = NULL; //2找到尾节点 p = paraHeader; while (p->next != NULL) { p = p->next; } //3把新节点插入尾节点后面 p->next = q; } /** * @brief 在指定位置插入一个节点 * * @param paraChar * @param paraHeader * @param paraPosition */ void insertElement(NodePtr paraHeader, char paraChar, int paraPosition) { NodePtr p, q; //1创建新节点 q = (NodePtr)malloc(sizeof(LNode)); q->data = paraChar; //2找到插入节点位置 p = paraHeader; for (int i = 0; i < paraPosition; i++) { p = p->next; if (p == NULL) { //未找到节点位置,报错 printf("位置%d超出了链表长度范围", paraPosition); return ; } } //3把新建节点插入链表 //为什么要打印个linking?????? printf("插入节点%c成功\r\n",paraChar); q->next = p->next; p->next = q; } /** * @brief 根据元素的数据删除链表节点 * * @param paraChar * @param paraHeader */ void deleteElement(NodePtr paraHeader, char paraChar) { NodePtr p, q; //1找到要删除节点的前一个节点 p = paraHeader; while ((p->next != NULL) && (p->next->data != paraChar)) { p = p->next; } if (p->next == NULL) { printf("无法找到所需要删除的数据%c\r\n", paraChar); return; } //释放所需要删除节点 //把删除节点的前一个节点指向删除节点后一个节点 q = p->next; p->next = p->next->next; free(q); } /** * @brief 增删测试类 */ void appendInsertDeleteTest() { printf("----appendInsertDeleteTest开始测试-----\n"); //1初始化空链表 LinkList tempList = initLinkList(); printList(tempList); //2加入一些节点 appendElement(tempList, 'H'); appendElement(tempList, 'e'); appendElement(tempList, 'l'); appendElement(tempList, 'l'); appendElement(tempList, 'o'); appendElement(tempList, '!'); printList(tempList); //3删除节点 deleteElement(tempList, 'e'); deleteElement(tempList, 'a'); deleteElement(tempList, 'o'); printList(tempList); //4在位置1初插入一个o insertElement(tempList, 'o', 1); printList(tempList); printf("--------------**********-------------\n\n"); } /** * @brief 地址分配观察测试类 */ void basicAddressTest() { printf("------basicAddressTest开始测试-------\n"); LNode tempNode1, tempNode2; tempNode1.data = 4; tempNode1.next = NULL; tempNode2.data = 6; tempNode2.next = NULL; printf("The first node: %d, %d, %d\r\n", &tempNode1, &tempNode1.data, &tempNode1.next); printf("The second node: %d, %d, %d\r\n", &tempNode2, &tempNode2.data, &tempNode2.next); tempNode1.next = &tempNode2; printf("--------------**********-------------\n\n"); } int main() { appendInsertDeleteTest(); basicAddressTest(); return 0; }
摘要:
只需要定义一个节点类型, 空间只受内存堆空间的限制.
抓住了头节点, 就抓住了整个世界.
可以根据循环语句, 分析时间复杂度.
这里必须用图示来分析 append, insert, delete 操作.
basicAddressTest 用于观察地址分配. 多写几个这样的函数, 就更理解操作系统的机制.