一.链表的基本概念
单链表 : n个结点链接成一个链式线性表的结构叫做链表,当每个结点中只包含一个指针域时,叫做单链表
?数据结点:链表中代表数据元素的结点,包含指向下一个数据元素的指
?尾结点: 链表中的最后一个数据结点,其下一元素指针为空,表示无后继
二.链表的定义
由于带头的链表操作比较方便,下文介绍的所有操作都是基于都头结点的单链表,
1.链表的定义
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /* 链表节点的定义 */ 2 typedef struct list 3 { 4 int data; //数据域 5 struct list *next; //指针域 6 }linkList;
2.链表的创建
创建一个带头结点的空链表,头结点中的数据成员存储的是当前链表的节点个数
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/* 创建一个带头结点的空链表 */ linkList *linkListCreate() { // 为头结点申请空间 linkList *pHead = (linkList *)malloc(sizeof(linkList)); if (pHead != NULL) { pHead->data = 0; pHead->next = NULL;// 由于创健的链表为空,指向的节点为空 } // 节点个数为0 pHead->data = 0; return pHead; }
3.链表的插入
插入元素到位置pos的算法 ?
1)判断线性表是否合法
2)判断插入位置是否合法
3)由表头开始通过next指针移动pos次后,当前元素的next指针即指向要插入的位置 ?
4)将新元素插入
?5)线性表长度加1
插入元素操作指针的步骤非常重要,一定要先将新节点的指针指向current的next。
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/* 功能:带头节点的单链表中的pos位置插入元素,指定位置大于链表长度则插到链表尾 参数: pHead-头指针 pos-位置 pNode-要插入的节点 返回值: 成功返回1 失败-1 说明:位置大于等于0 */ int linkListInsertNode(linkList *pHead,int pos,linkList *pNode) { // 安全性的检查 if(pHead == NULL ||pNode == NULL || pos < 0) { return -1; } linkList *pCurrent = pHead; for (int i = 0; (i < pos)&&(pCurrent->next != NULL); i++) { pCurrent = pCurrent->next; // 避免成环,如果调用函数者传的参数都是同一节点地址就会成环 if(pCurrent == pNode) { return -1; } } pNode->next = pCurrent->next; pCurrent->next = pNode; // 链表长度记录加1 pHead->data++; return 1; }
3. 链表的删除
链表删除指定位置节点的步骤:
1)链表是否合法
2)链表的位置是否合法
3)获取指定位置的元素
4)将元素下链
5)长度减一
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /* 带头节点的单链表中删除pos位置的元素,指定位置大于链表长度则返回NULL 2 参数: pHead头指针 pos位置 3 返回值: 成功返回被删除的节点 失败NULL 4 说明:位置大于等于0 小于长度 5 */ 6 int linkListDeleteNode(linkList *pHead,int pos) 7 { 8 // 删除的节点位置不能超过当前链表的长度 9 if((pHead == NULL) || (pos < 0) || (pos > pHead->data - 1)) 10 { 11 return -1; 12 } 13 linkList *currentNode = pHead; 14 linkList *deleteNode = NULL; 15 // 定位pos位置currentNode->next即为pos位置节点 16 for (int i = 0; i < pos; i++) 17 { 18 currentNode = currentNode->next; 19 } 20 deleteNode = currentNode->next; 21 currentNode->next = deleteNode->next; 22 // 释放节点内存 23 free(deleteNode); 24 // 长度减一 25 pHead->data--; 26 return 1; 27 }
4. 获取指定位置的元素
获取第pos个元素操作 ?
1)判断线性表是否合法
?2)判断位置是否合法
3 由表头开始通过next指针移动pos次后,当前元素的next指针即指向要获取的元素
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /* 带头节点的单链表中获取pos位置的元素,指定位置大于链表长度则返回NULL 2 参数: pHead头指针 pos位置 3 返回值: 成功返回节点地址 失败NULL 4 说明:位置大于等于0小于当前元素个数 5 */ 6 linkList *linkListGetNode(linkList *pHead,int pos) 7 { 8 // 链表与位置是否合法 9 if(pHead == NULL || pos < 0 || (pos > pHead->data -1)) 10 { 11 return NULL; 12 } 13 linkList *pGetNode = NULL; 14 linkList *pCurrent = pHead; 15 for (int i = 0; i < pos; i++) 16 { 17 pCurrent = pCurrent->next; 18 } 19 pGetNode = pCurrent->next; 20 return pGetNode; 21 }
5. 获取当前链表的元素个数
由于头结点中的数据存储的是链表的节点个数,所以只要将此值返回即可
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int linkListGetLength(linkList *pHead) 2 { 3 if(pHead != NULL) 4 { 5 return pHead->data; 6 } 7 return -1; 8 }
6. 链表节点的创建
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 linkList *linkLisNodeCreate(int val) 2 { 3 // 申请空间 4 linkList *node = (linkList *)malloc(sizeof(linkList)); 5 // 申请成功则初始化 6 if(node != NULL) 7 { 8 node->data = val; 9 node->next = NULL; 10 } 11 return node; 12 }
7.链表的销毁
由于节点的空间都是使用malloc从堆空间分配,所以使用完毕必须释放空间,否则造成内存泄露
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 void freeList(linkList *pHead) 2 { 3 linkList *node = pHead; 4 while (node != NULL) 5 { 6 pHead = pHead->next; 7 free(node); 8 node = pHead; 9 } 10 free(pHead); 11 }
8.测试程序
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
int main() { // 链表的创建 linkList *pHead = linkListCreate(); // 创建四个节点 linkList *node1 = linkLisNodeCreate(1); linkList *node2 = linkLisNodeCreate(2); linkList *node3 = linkLisNodeCreate(3); linkList *node4 = linkLisNodeCreate(4); linkList *node5 = linkLisNodeCreate(5); // 头插法插到链表中先插入的节点会在后面 linkListInsertNode(pHead, 0, node1); linkListInsertNode(pHead, 0, node2); linkListInsertNode(pHead, 0, node3); linkListInsertNode(pHead, 0, node4); /* 获取链表长度 */ printf("lengh %d\n",linkListGetLength(pHead)); /* 遍历链表 */ printWithHeadLinkList(pHead); // 获取第三个位置的元素 linkList *temp = linkListGetNode(pHead,3); printf("temp ->data = %d\n",temp->data); /* 删除链表测试用例 */ // 删除右边界节点(最后一个) linkListDeleteNode(pHead, linkListGetLength(pHead)-1); // 删除左边界节点(第0个) linkListDeleteNode(pHead, 0); // 删除中间的节点 linkListDeleteNode(pHead, 2); // 删除不存在的节点 linkListDeleteNode(pHead, -1); printWithHeadLinkList(pHead); // 销毁链表 freeList(pHead); pHead = NULL; return 0; }