赞
踩
在学习单链表之前我们已经学习了顺序表相关的基本操作,顺序表访问元素更加方便,物理地址是连续的;但是也有一些缺点:
1.在头部插入或者从中间插入或删除元素时需要搬移数据,效率较低
2.在插入数据时可能存在空间不足的情况,需要扩容
因此就会出现另一种线性表---链表
1.链表的概念
链表顾名思义就是链式的存储结构,元素的逻辑顺序是由指针来依次连接的。
链表中有多个节点,每一个节点里储存着数据,还有指向下一个节点的指针 。
2.单链表的定义
我们要实现的是不带头结点的非循环单向链表
单链表的结构包括数值域和指针域
- typedef int DataType;
- typedef struct SListNode
- {
- DataType data;
- struct SListNode* next;//指向下一个节点
- }SListNode;
3.单链表节点的创建
- SListNode* BuySListNode(DataType x)
- {
- SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));//这个动态内存空间是用来存放这个结构体类型的
- if (NULL == newNode)
- {
- printf("创建节点失败!!!!");
- exit(0);
- }
- newNode->data = x;
- newNode->next = NULL;
- return newNode;
- }
4.单链表的尾插
- void SListPushBack(SListNode** pplist,DataType x)
- {
- assert(*pplist);
- //如果pplist为空则需要让pplist指向刚刚插入的节点
- if (NULL == *pplist)
- {
- *pplist = BuySListNode(x);
- }
- else
- {
- //此时链表不为空
- //1.构建一个新节点
- SListNode* NewNode = BuySListNode(x);
- //2.找出原链表中的最后一个节点
- SListNode* cur = *pplist;
- while (cur->next)
- {
- //获取cur的下一个节点
- cur = cur->next;
- }
- //3.插入新节点
- cur->next = NewNode;
- }
- }
5.单链表的尾删
- //单链表的尾删
- void SListPopBack(SListNode** pplist)
- {
- assert(pplist);//保证pplist指向实参
- //1.空链表
- if (NULL == *pplist)
- {
- return;
- }
- else if (NULL == (*pplist)->next)
- {
- //2.链表中只有一个节点
- free(*pplist);
- *pplist = NULL;
- }
- else
- {
- //3.链表中有多个节点
- //a.找倒数第一个节点
- SListNode* cur = *pplist;
- SListNode* prev = NULL;//保存cur前一个节点
- while (cur->next)
- {
- prev = cur;
- cur = cur->next;
- }
- prev->next = NULL;
- free(cur);
- //b.找倒数第二个节点
- /*
- SListNode* cur = *pplist;
- while(cur->next->next)
- {
- cur = cur->next;
- }
- free(cur->next);
- cur->next = NULL;
- */
- //c.错误写法
- /*while (cur->next)
- {
- cur = cur->next;
- }
- free(cur);*/
-
- }
- }
6.单链表打印
- //单链表打印
- void SListPrint(SListNode* plist)
- {
- SListNode* cur = plist;
- while (cur)
- {
- printf("&d--->", cur->data);
- cur = cur->next;
- }
- printf("NULL\n");//单链表最后一个节点指向空
- }
7.单链表头插
- // 单链表的头插
- //时间复杂度:O(1)
- void SListPushFront(SListNode** pplist, SLTDataType x)
- {
- //assert(pplist);
- //SListNode* newNode = BuySListNode(x);
- 1.链表为空
- //if (NULL == *pplist)
- //{
- // *pplist = newNode;
- //}
- //else
- //{
- // //2.链表不空
- // newNode->next = *pplist;
- // *pplist = newNode;
- //}
- assert(pplist);
- //表面看上需要分情况讨论,但是在分析之后情况一可以与情况二合并
- SListNode* newNode = BuySListNode(x);
- newNode->next = *pplist;
- *pplist = newNode;
- }
8.单链表头删
- //单链表头删
- void SListPopFront(SListNode** pplist)
- {
- assert(pplist);
- //1.链表为空
- if (NULL == *pplist)
- {
- return;
- }
- //else if (NULL == (*pplist)->next)
- //{
- // //2.链表只有一个节点
- // free(*pplist);
- // *pplist = NULL;
- //}
- else
- {
- //3.链表中有多个节点(分析后可知也包含一个节点的情况)
- SListNode* del = *pplist;//将要删除的节点保存起来
- *pplist = (*pplist)->next;
- free(del);
- }
-
- }
9.单链表删除任意位置pos之后的值
- // 单链表删除pos位置之后的值
- //时间复杂度O(1)
- void SListEraseAfter(SListNode* pos)
- {
- SListNode* delNode = NULL;
- if (NULL == pos)
- {
- return;
- }
- delNode = pos->next;
- if (NULL == delNode)
- {
- return;
- }
- //pos->data = delNode->data;
- pos->next = delNode->next;
- free(delNode);
- }
代码中加一句pos->data = delNode->data就可以实现删除任意位置的值
但是不能删除最后一个节点位置的元素
10.单链表在任意位置pos之后插入
- //单链表在任意位置pos之后插入
- //时间复杂度:O(1)
- void SListInsertAfter(SListNode* pos, SLTDataType x)
- {
- SListNode* newNode = NULL;
- if (NULL == pos)
- {
- return;
- }
- newNode = BuySListNode(x);
- newNode->next = pos->next;//必须先让新节点接入到链表内然后将pos位置的节点指向新节点
- pos->next = newNode;
- }
新节点无法插入到pos之前,因为该方法未提供plist头节点
11.单链表在任意位置pos之前插入
- //单链表在任意位置pos之前插入
- void SListInsertBefore(SListNode** pplist, SListNode* pos, SLTDataType x)
- {
- assert(pplist);
- assert(pos);
- SListNode* newNode = NULL;
- if (pos == *pplist)
- {
- newNode = BuySListNode(x);
- }
- else
- {
- SListNode* cur = *pplist;//将头节点保存起来,**pplist是plist的地址,*plist才是解引用后的指针
- while (cur->next != pos)
- {
- cur = cur->next;
- }
- newNode = BuySListNode(x);
- newNode->next = pos;
- cur->next = newNode;
- }
- }
11.单链表的销毁
- //单链表销毁
- void SListDestroy(SListNode** pplist)
- {
- assert(pplist);
- SListNode* cur = *pplist;
- while (cur)
- {
- *pplist = cur->next;
- free(cur);
- cur = *pplist;
- }
- *pplist = NULL;
- }
单链表的销毁很简单,只需要用cur把链表遍历一遍,每次让pplist头指针指向cur->next,释放cur所指向的当前节点,然后更新pplist指针的值,最后将pplist指向NULL。
12.单链表操作的测试
- void TestSList1()
- {
- SListNode* plist = NULL;//单链表的起始位置保存
-
- //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
- SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
- SListPrint(plist);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
- SListPushBack(&plist, 5);
- SListPrint(plist);
-
- SListPopBack(&plist);//尾删
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
-
- SListPushFront(&plist, 1);//依次头插1,2,3,4,5
- SListPrint(plist);
- SListPushFront(&plist, 2);
- SListPrint(plist);
- SListPushFront(&plist, 3);
- SListPrint(plist);
- SListPushFront(&plist, 4);
- SListPrint(plist);
- SListPushFront(&plist, 5);
- SListPrint(plist);
-
- SListInsertAfter(SListFind(plist, 3), 7);//在3后面插入7
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListInsertAfter(SListFind(plist, 1), 8);//在1后面插入8
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListInsertAfter(SListFind(plist, 2), 9);//在2后面插入9
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
-
- SListEraseAfter(SListFind(plist, 3));//删除3后面的元素
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListEraseAfter(SListFind(plist, 1));//删除1后面的元素
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListEraseAfter(SListFind(plist, 2));//删除2后面的元素
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
-
- SListInsertBefore(&plist, SListFind(plist, 1), 6);//在1之前插入元素6
- SListPrint(plist);
- printf("节点数为 %d\n",SListSize(plist));
-
- }
- void TestSList2()
- {
- SListNode* plist = NULL;//单链表的起始位置保存
-
- //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
- SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
- SListPrint(plist);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
- SListPushBack(&plist, 5);
- SListPrint(plist);
-
- SListPopBack(&plist);//尾删
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
-
- SListPushFront(&plist, 1);//依次头插1,2,3,4,5
- SListPrint(plist);
- SListPushFront(&plist, 2);
- SListPrint(plist);
- SListPushFront(&plist, 3);
- SListPrint(plist);
- SListPushFront(&plist, 4);
- SListPrint(plist);
- SListPushFront(&plist, 5);
- SListPrint(plist);
-
- SListPopFront(&plist);//头删
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- }
本文全部代码:
SList.h
- #pragma once
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- typedef int SLTDataType;
- typedef struct SListNode
- {
- SLTDataType data;
- struct SListNode* next;
- }SListNode;
- //动态申请一个节点
- SListNode* BuySListNode(SLTDataType x);
- //单链表的尾插
- void SListPushBack(SListNode** pplist, SLTDataType x);
- //单链表的尾删
- void SListPopBack(SListNode** pplist);
- //链表打印
- void SListPrint(SListNode* plist);
-
- // 单链表的头插
- void SListPushFront(SListNode** pplist, SLTDataType x);
- // 单链表头删
- void SListPopFront(SListNode** pplist);
- // 单链表查找
- SListNode* SListFind(SListNode* plist, SLTDataType x);
- // 单链表在pos位置之后插入x
- // 分析思考为什么不在pos位置之前插入?
- void SListInsertAfter(SListNode* pos, SLTDataType x);
- // 单链表删除pos位置之后的值
- // 分析思考为什么不删除pos位置?
- void SListEraseAfter(SListNode* pos);
- // 单链表的销毁
- void SListDestroy(SListNode** plist);
SList.c
- #include "SList.h"
- //动态申请一个节点
- SListNode* BuySListNode(SLTDataType x)
- {
- SListNode* newNode = (SListNode *) malloc(sizeof(SListNode));//这个动态内存空间是用来存放这个结构体类型的
- if (NULL == newNode)
- {
- printf("创建节点失败!!!!");
- exit(0);
- }
- newNode->data = x;
- newNode->next = NULL;
- return newNode;
- }
- //单链表的尾插
- void SListPushBack(SListNode** pplist, SLTDataType x)
- {
- //若为空链表则需要让pplist指向刚刚插入的节点
- if (NULL == *pplist)
- {
- *pplist = BuySListNode(x);
- }
- else
- {
- //此时链表不为空
- //1.构建一个新节点
- SListNode* newNode = BuySListNode(x);
- //2.找原链表中最后一个节点
- SListNode* cur = *pplist;
- while (cur->next)
- {
- //获取cur的下一个节点
- cur = cur->next;
- }
- //3.插入新节点
- cur->next = newNode;
- }
- }
- //单链表的尾删
- void SListPopBack(SListNode** pplist)
- {
- assert(pplist);
- //1.空链表
- if (NULL == *pplist)
- {
- return;
- }
- else if(NULL == (*pplist)->next)
- {
- //2.链表中只有一个节点
- free(*pplist);
- *pplist = NULL;
- }
- else
- {
- //3.链表中有多个节点
-
- SListNode* cur = *pplist;//*pplist为第一个节点
- while (cur->next->next)
- {
- cur = cur->next;
- }
- free(cur->next);
- cur->next = NULL;
- //方法2
- /*
- SListNode* cur = *pplist;//*pplist为第一个节点
- SListNode* prev = NULL;
- while (cur->next)
- {
- prve = cur;
- cur = cur->next;
- }
- free(cur);
- prve->next = NULL;
- */
- }
-
-
- }
- //链表打印
- void SListPrint(SListNode* plist)
- {
- SListNode* cur = plist;
- while (cur)
- {
- printf("%d--->", cur->data);
- cur = cur->next;
- }
- printf("NULL\n");
- }
- // 单链表的头插
- //时间复杂度:O(1)
- void SListPushFront(SListNode** pplist, SLTDataType x)
- {
- assert(pplist);
- SListNode* newNode = BuySListNode(x);
- 1.链表为空
- //if (NULL == *pplist)
- //{
- // *pplist = newNode;
- //}
- //else
- //{
- // //2.链表不空
- // newNode->next = *pplist;
- // *pplist = newNode;
- //}
- //表面看上需要分情况讨论,但是在分析之后情况一可以与情况二合并
-
- newNode->next = *pplist;
- *pplist = newNode;
- }
- //单链表查找
- SListNode* SListFind(SListNode* plist, SLTDataType x)
- {
- SListNode* cur = plist;
- while (cur)
- {
- if (cur->data == x)
- {
- return cur;
- }
- cur = cur->next;
- }
- return NULL;
- }
- //单链表中的有效节点数
- int SListSize(SListNode* plist)
- {
- int size = 0;
- SListNode* cur = plist;
- while (cur)
- {
- size++;
- cur = cur->next;
- }
- return size;
- }
- //单链表头删
- void SListPopFront(SListNode** pplist)
- {
- assert(pplist);
- SListNode* del = NULL;
- //1.链表为空
- if (NULL == *pplist)
- {
- return;
- }
- //else if (NULL == (*pplist)->next)
- //{
- // //2.链表只有一个节点
- // free(*pplist);
- // *pplist = NULL;
- //}
- else
- {
- //3.链表中有多个节点(分析后可知也包含一个节点的情况)
- SListNode* del = *pplist;//将要删除的节点保存起来
- *pplist = (*pplist)->next;
- free(del);
- }
-
- }
- //单链表在任意位置pos之后插入
- //新节点无法插入到pos之前,因为该方法未提供plist头节点
- //时间复杂度:O(1)
- void SListInsertAfter(SListNode* pos, SLTDataType x)
- {
- SListNode* newNode = NULL;
- if (NULL == pos)
- {
- return;
- }
- newNode = BuySListNode(x);
- newNode->next = pos->next;//必须先让新节点接入到链表内然后将pos位置的节点指向新节点
- pos->next = newNode;
- }
- //单链表在任意位置pos之前插入
- void SListInsertBefore(SListNode** pplist, SListNode* pos, SLTDataType x)
- {
- assert(pplist);
- assert(pos);
- SListNode* newNode = NULL;
- if (pos == *pplist)
- {
- newNode = BuySListNode(x);
- }
- else
- {
- SListNode* cur = *pplist;//将头节点保存起来,**pplist是plist的地址,*plist才是解引用后的指针
- while (cur->next != pos)
- {
- cur = cur->next;
- }
- newNode = BuySListNode(x);
- newNode->next = pos;
- cur->next = newNode;
- }
- }
- // 单链表删除pos位置之后的值
- //时间复杂度O(1)
- void SListEraseAfter(SListNode* pos)
- {
- SListNode* delNode = NULL;
- if (NULL == pos)
- {
- return;
- }
- delNode = pos->next;
- if (NULL == delNode)
- {
- return;
- }
- //pos->data = delNode->data;
- pos->next = delNode->next;
- free(delNode);
- }
- //单链表销毁
- void SListDestroy(SListNode** pplist)
- {
- assert(pplist);
- SListNode* cur = *pplist;
- while (cur)
- {
- *pplist = cur->next;
- free(cur);
- cur = *pplist;
- }
- *pplist = NULL;
- }
-
-
- void TestSList1()
- {
- SListNode* plist = NULL;//单链表的起始位置保存
-
- //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
- SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
- SListPrint(plist);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
- SListPushBack(&plist, 5);
- SListPrint(plist);
-
- SListPopBack(&plist);//尾删
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
-
- SListPushFront(&plist, 1);//依次头插1,2,3,4,5
- SListPrint(plist);
- SListPushFront(&plist, 2);
- SListPrint(plist);
- SListPushFront(&plist, 3);
- SListPrint(plist);
- SListPushFront(&plist, 4);
- SListPrint(plist);
- SListPushFront(&plist, 5);
- SListPrint(plist);
-
- SListInsertAfter(SListFind(plist, 3), 7);//在3后面插入7
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListInsertAfter(SListFind(plist, 1), 8);//在1后面插入8
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListInsertAfter(SListFind(plist, 2), 9);//在2后面插入9
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
-
- SListEraseAfter(SListFind(plist, 3));//删除3后面的元素
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListEraseAfter(SListFind(plist, 1));//删除1后面的元素
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListEraseAfter(SListFind(plist, 2));//删除2后面的元素
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
-
- SListInsertBefore(&plist, SListFind(plist, 1), 6);//在1之前插入元素6
- SListPrint(plist);
- printf("节点数为 %d\n",SListSize(plist));
-
- }
- void TestSList2()
- {
- SListNode* plist = NULL;//单链表的起始位置保存
-
- //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
- SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
- SListPrint(plist);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
- SListPushBack(&plist, 5);
- SListPrint(plist);
-
- SListPopBack(&plist);//尾删
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
-
- SListPushFront(&plist, 1);//依次头插1,2,3,4,5
- SListPrint(plist);
- SListPushFront(&plist, 2);
- SListPrint(plist);
- SListPushFront(&plist, 3);
- SListPrint(plist);
- SListPushFront(&plist, 4);
- SListPrint(plist);
- SListPushFront(&plist, 5);
- SListPrint(plist);
-
- SListPopFront(&plist);//头删
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- SListPopFront(&plist);
- SListPrint(plist);
- printf("节点数为 %d\n", SListSize(plist));
- }
-
-
-
- int main()
- {
- //TestSList1();
- TestSList2();
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。