赞
踩
目录
单向链表是一种常见的线性数据结构,它由一系列节点组成,每个节点包含两部分:数据和指向下一个节点的指针。每个节点只能访问它后面的节点,而不能访问前面的节点。
单向链表的特点:
单向链表相对于数组来说,具有一些优点和缺点:
由于单向链表的特点,它常常被用于需要频繁插入和删除元素的场景,或者在事先无法确定数据大小和数量的情况下使用。
优点:
空间连续、支持随机访问
缺点:
缺点:
以节点为单位存储,不支持随机访问
优点:
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结
构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。
定义三个文件:
- //打印链表
- void SListPrint(SLTNode* phead);
-
- //创建新链表元素(动态申请一个节点)
- SLTNode* BuySListNode(SLTDataType x);
-
- //尾插
- void SListPushBack(SLTNode** pphead, SLTDataType x);
-
- //头插
- void SListPushFront(SLTNode** pphead, SLTDataType x);
-
- //尾删
- void SListPopBack(SLTNode** pphead);
-
- //头删
- void SListPopFront(SLTNode** pphead);
-
- //查找->可在查找的基础上进行修改SListAlter
- SLTNode* SListFind(SLTNode* phead,SLTDataType x);
-
- //改
- void SListAlter(SLTNode* phead, SLTDataType x,SLTDataType y);
-
- //pos位置之前插入
- void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
-
- //删除pos位置的值
- void SListErase(SLTNode** pphead, SLTNode* pos);

定义链表基本结构
- typedef struct SListNode
- {
- int data;
- struct SListNode* next;
- }SLTNode;
创建新元素用于插入原链表
- SLTNode* BuySListNode(SLTDataType x)
- {
- //开辟空间
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- assert(newnode);
-
- newnode->data = x;
- newnode->next = NULL;
-
- return newnode;
- }
- void SListPushBack(SLTNode** pphead, SLTDataType x)
- {
- //开辟空间
- SLTNode* newnode = BuySListNode(x);
-
- if (*pphead == NULL)
- {
- //防止开始时节点为空
- *pphead = newnode;
- }
- else
- {
- //找尾节点
- SLTNode* tail = *pphead;//找到链表首元素
- while (tail->next != NULL)
- {
- //检索到未节点
- tail = tail->next;
- }
- //插入
- tail->next = newnode;
- }
- }

- void SListPushFront(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = BuySListNode(x);
-
- newnode->next = *pphead;
- *pphead = newnode;//地址传给pphead
- //*pphead=&plist
-
- /*
- 头插无需检查是否为空
- */
- }
- void SListPopBack(SLTNode** pphead)
- {
- assert(*pphead);
- if ((*pphead)->next==NULL)
- {
- //1,只有一个节点
-
- free(*pphead);
- *pphead = NULL;
- }
- else
- {
- //2,有多个节点
-
- //将前一个链元素中存放的地址换为NULL,防止野指针
- /* 写法一 */
- SLTNode* tailPrev = NULL;
- SLTNode* tail = *pphead;
- while (tail->next!=NULL)
- {
- tailPrev = tail;//tail的地址
- tail = tail->next;
- }
- free(tail);
- tailPrev->next = NULL;
-
- /* //写法二
- * //tail寻找的是倒数第二个元素
- while (tail->next->next!=NULL)
- {
- tail = tail->next;
- }
- free(tail->next);
- tail->next = NULL;
- */
- }
- }

- void SListPopFront(SLTNode** pphead)
- {
- //防止被删空
- assert(*pphead);
-
- //找到首位链表元素
- SLTNode* next = (*pphead)->next;//存储首元素存放下一个元素的地址
- free(*pphead);//释放首元素
- *pphead = next;//将第二位元素改为首元素
- }
- //无需更改元素,故传一级指针
- SLTNode* SListFind(SLTNode* phead, SLTDataType x)
- {
- SLTNode* cur = phead;
- while (cur)
- {
- if (cur->data==x)
- return cur;
- cur = cur->next;
- }
- //未找到指定元素,返回NULL
- return NULL;
- }
改元素是建立再查找基础之上进行更改
- void SListAlter(SLTNode* phead, SLTDataType x, SLTDataType y)
- {
- printf("修改成功:\n");
- //先找到相应元素,再进行更改
- SLTNode* ret = SListFind(phead, y);
- ret->data = x;
- }
任意位置之前插入
- void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- assert(pphead);
- assert(pos);
-
- //头插
- if (pos == *pphead)
- SListPushFront(pphead, x);
- else
- {
- //任意位置之前插入
- SLTNode* prev = *pphead;
- while (prev->next!=pos)//找到pos的位置
- {
- prev = prev->next;//prev存放pos的地址
- }
- //找到位置
- SLTNode* newnode = BuySListNode(x);//创建新链表元素,并赋值
- prev->next = newnode;//给前一个元素赋上下一元素地址
- newnode->next = pos;//给插入元素存放下一个元素的地址
- }
- }

- void SListErase(SLTNode** pphead, SLTNode* pos)
- {
- assert(pphead);
- assert(pos);
-
- if (*pphead == pos)
- SListPopFront(pphead);//头删
- else
- {
- SLTNode* prev = *pphead;
- while (prev->next != pos)//找到pos的位置
- {
- prev = prev->next;//prev存放pos的地址
- //移到pos前一位,next存放的是pos的地址
- }
- //将prev存放的地址改为pos后一个元素的地址
- prev->next = pos->next;
- //释放pos
- free(pos);
- pos = NULL;
- }
- }

- #pragma once
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <assert.h>
-
- typedef int SLTDataType;
-
- typedef struct SListNode
- {
- int data;
- struct SListNode* next;
- }SLTNode;
-
- //打印链表
- void SListPrint(SLTNode* phead);
-
- //创建新链表元素(动态申请一个节点)
- SLTNode* BuySListNode(SLTDataType x);
-
- //尾插
- void SListPushBack(SLTNode** pphead, SLTDataType x);
-
- //头插
- void SListPushFront(SLTNode** pphead, SLTDataType x);
-
- //尾删
- void SListPopBack(SLTNode** pphead);
-
- //头删
- void SListPopFront(SLTNode** pphead);
-
- //查找->可在查找的基础上进行修改SListAlter
- SLTNode* SListFind(SLTNode* phead,SLTDataType x);
-
- void SListAlter(SLTNode* phead, SLTDataType x,SLTDataType y);
-
- //pos位置之前插入
- void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
-
- //删除pos位置的值
- void SListErase(SLTNode** pphead, SLTNode* pos);

- #include "SList.h"
-
- //打印
- void SListPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur!=NULL)
- {
- printf("%d->", cur->data);
- cur = cur->next;//存放下一个元素的地址
- }
- printf("NULL\n");
- }
-
- //创建新链表元素
- SLTNode* BuySListNode(SLTDataType x)
- {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- assert(newnode);
-
- newnode->data = x;
- newnode->next = NULL;
-
- return newnode;
- }
-
- //尾插
- void SListPushBack(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead);
-
- SLTNode* newnode = BuySListNode(x);//data
-
- if (*pphead == NULL)
- {
- //防止开始时节点为空
- *pphead = newnode;
- }
- else
- {
- //找尾节点
- SLTNode* tail = *pphead;
- while (tail->next != NULL)
- {
- //存放新节点地址
- tail = tail->next;
- }
- tail->next = newnode;
- }
- }
-
- //头插
- void SListPushFront(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead);
-
- SLTNode* newnode = BuySListNode(x);
-
- newnode->next = *pphead;
- *pphead = newnode;//地址传给pphead
- //*pphead=&plist
-
- /*
- 头插无需检查是否为空
- */
- }
-
- //尾删
- void SListPopBack(SLTNode** pphead)
- {
- assert(*pphead);
-
- if ((*pphead)->next==NULL)
- {
- //1,只有一个节点
-
- free(*pphead);
- *pphead = NULL;
- }
- else
- {
- //2,有多个节点
-
- //将前一个链元素中存放的地址换为NULL,防止野指针
- /* 写法一 */
- SLTNode* tailPrev = NULL;
- SLTNode* tail = *pphead;
- while (tail->next!=NULL)
- {
- tailPrev = tail;//tail的地址
- tail = tail->next;
- }
- free(tail);
- tailPrev->next = NULL;
-
- /* //写法二
- * //tail寻找的是倒数第二个元素
- while (tail->next->next!=NULL)
- {
- tail = tail->next;
- }
- free(tail->next);
- tail->next = NULL;
- */
- }
- }
-
- //头删
- void SListPopFront(SLTNode** pphead)
- {
- //防止被删空
- assert(*pphead);
-
- SLTNode* next = (*pphead)->next;
- free(*pphead);
- *pphead = next;
- }
-
- //查找-给一个节点的指针
- SLTNode* SListFind(SLTNode* phead, SLTDataType x)
- {
- SLTNode* cur = phead;
- while (cur)
- {
- if (cur->data==x)
- return cur;
- cur = cur->next;
- }
- return NULL;
- }
-
- //改
- void SListAlter(SLTNode* phead, SLTDataType x, SLTDataType y)
- {
- assert(phead);
- printf("修改成功:\n");
- SLTNode* ret = SListFind(phead, y);
- ret->data = x;
- }
- //pos位置之前插入
- void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- assert(pphead);
- assert(pos);
-
- //头插
- if (pos == *pphead)
- SListPushFront(pphead, x);
- else
- {
- SLTNode* prev = *pphead;
- while (prev->next!=pos)//找到pos的位置
- {
- prev = prev->next;//prev存放pos的地址
- }
- //找到位置
- SLTNode* newnode = BuySListNode(x);//创建新链表元素,并赋值
- prev->next = newnode;//给前一个元素赋上下一元素地址
- newnode->next = pos;//给插入元素存放下一个元素的地址
- }
- }
-
- //删除pos位置的值
- void SListErase(SLTNode** pphead, SLTNode* pos)
- {
- assert(pphead);
- assert(pos);
-
- if (*pphead == pos)
- SListPopFront(pphead);//头删
- else
- {
- SLTNode* prev = *pphead;
- while (prev->next != pos)//找到pos的位置
- {
- prev = prev->next;//prev存放pos的地址
- //移到pos前一位,next存放的是pos的地址
- }
- //将prev存放的地址改为pos后一个元素的地址
- prev->next = pos->next;
- //释放pos
- free(pos);
- pos = NULL;
- }
- }

test.c仅仅是用于测试代码,本文以弄懂单向链表为主,故不进行菜单制作
但改测试中也包含了对部分链表结构即原理进行了讲解,请耐心看完
- #include "SList.h"
- void TestSList1()
- {
- SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
- assert(n1);
-
- SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
- assert(n2);
-
- SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
- assert(n3);
-
- SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
- assert(n4);
-
- n1->data=1;
- n2->data=2;
- n3->data=3;
- n4->data=4;
-
- n1->next = n2;
- n2->next = n3;
- n3->next = n4;
- n4->next = NULL;
-
- SListPrint(n1);
- }
-
- void TestSList2()
- {
- SLTNode* plist = NULL;
- //传的是plist指针的地址
-
- //如果直接传plist,将导致SLTNode* phead中
- //phead为临时拷贝,不影响实参
- SListPushBack(&plist, 1);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
-
- SListPrint(plist);//不改变无需传二级指针
-
- SListPushFront(&plist,0);
- SListPrint(plist);
-
- SListPopFront(&plist);
- SListPrint(plist);
-
- SListPopBack(&plist);
- /*SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);
- SListPopBack(&plist);
- SListPrint(plist);*/
- /*SListPopBack(&plist);
- SListPrint(plist);*/
- }
-
- void TestSList3()
- {
- SLTNode* plist = NULL;
- //传的是plist指针的地址
-
- //如果直接传plist,将导致SLTNode* phead中
- //phead为临时拷贝,不影响实参
- SListPushBack(&plist, 1);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
-
- SListPrint(plist);//不改变无需传二级指针
-
-
- //查找
- SLTNode* ret = SListFind(plist, 3);
- if (ret)
- {
- //返回值不为空则为找到
- printf("找到了\n");
- }
- SListPrint(plist);
- 修改
- //SListAlter(plist, 20, 2);
- //SListPrint(plist);
-
- //插入
- SLTNode* pos = SListFind(plist, 3);//先要找到再进行更改
- if (pos)
- {
- SListInsert(&plist, pos, 40);
- }
- SListPrint(plist);
-
- //删除
- pos = SListFind(plist, 2);//先要找到再进行删除
- if (pos)
- {
- SListErase(&plist, pos);
- }
- SListPrint(plist);
- }
-
- int main()
- {
- TestSList3();
-
- return 0;
- }

单向链表讲解完毕啦!创作不易,如果喜欢请留下个赞吧,感激不尽
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。