数据结构专项-链表

数据结构的总结
1.定义 一组用来保存一种或者多种特定关系的数据的集合(组织和存储数据) 程序的设计:将现实中大量而复杂的问题以特定的数据类型和特定的存储结构存储在内存中, 并在此基础上实现某个特定的功能的操作;
程序 = 数据结构 + 算法 高内聚,低耦合
2.数据与数据之间的关系
数据的逻辑结构:数据元素与元素之间的关系
集合:关系平等
线性结构:元素之间一对一的关系(表,队列。栈。。。)
树型结构:元素之间一对多的关系(二叉树)
图形结构:元素之间多对多的关系(网状结构)
数据的物理结构:数据的逻辑结构在计算机内存中的存储形式
顺序存储:采用一段连续的内存空间保存元素
优点:空间连续,访问方便
缺点:插入删除需要移动大量的元素 需要预分配内存空间 容易造成存储空间碎片
链式存储:采用一组非连续的内存空间保存元素
缺点:访问元素效率低 优点:插入和删除数据方便
不需要预分配内存
索引存储:通过关键字构建索引表,通过索引表来来找到数据的存储位置
散列存储(哈希存储):将数据元素的存储位置与关键码之间建立确定对 应关系从而实现查找的存储方式
3. 储备知识 指针 结构体 动态内存分配
1,单向链表(熟悉链表的增删改查) 1.创建链表 在头文件中编写结构体 1.标签数据类型 LinkNode *pHead;
节点类型指针 int cLen; 整个链表的长度 2.节点类型 DataType data; 节点中存放的数据类型
struct node *pNext; 指向下一个节点的地址
流程: 申请建立标签数据的空间(malloc),将地址和节点初始化,之后返回标签的指针。
LinkList *CreateLinkList()
{
LinkList *pList =malloc(sizeof(LinkList *pList));
if (NULL == pList)
{
perror(“fail to malloc”);
return NULL;
}
pList->cLen = 0;
pList->pHead = NULL;
return pList;
}
2.“增” 插入链表:
1.头插法:
流程:
1.先申请增加这个节点的空间 LinkNode *pInsertNode =

malloc(sizeof(LinkNode));
2.将准备传入的数据给这个节点的数据域 pInsertNode->data = data;
3.将标签数据指针所指向的位置给这个新节点
pInsertNode->pNext = pList->pHead; 4.将标签指向这个新节点
pList->pHeaed = pInsertNode; 5.将标签所记的节点数加一
pList->cLen++; 2.尾插法
流程:
(1)(2)步和头插一样
(3)将新节点的指针域指向空
(4)判断链表是否为空(本来为空的话直接插入即可)
if (IsEmptyLinkList(pList)) 建议单写一个判空函数
{
pList->pHead =pInsertNode;
}
判空函数思想:用节点数来判断
int IsEmptyLinkList(pList) { return 0 == pList->cLen; } (5)若不是空链表则遍历找到最后一个节点插入
LinkNode *pTmpNode = pList->pHead;
while (pTmpNode-> != NULL)
{pTmpNode = pTmpNode->pNext; } pTmpNode->pNext = pInsertNode; (6)最后将标签的节点数加一
pList->cLen++;
3.“删”删除节点
1.头删法
流程:
(1)首先判断链表是否没有节点 (2)新设一个释放指针指向标签所指向的空间(原来的第一个节点)
LinkNode *pFreeNode = pList->pHead; (3)将这个释放指针所指向的空间(第二个节点)给标签指针域
pList->pHead = pFreeNode->pNext; (4)释放这个地址
free(pFreeNode);
(5)节点数cLen--
2.尾删法
流程:
1.不仅判断是否为空链表
2.还要判断是否只有一个节点(如果只有一个节点,直接调用头删法)
3.先定义一个新的节点指针指向标签指针域 !!找到最后一个节点
LinkNode *pTmpNode = pList->pHead; ! while (pTmpNode->pNext-pNext != NULL) { pTmpNode = pTmpNode->pNext; } 4.释放地址
free(pTmpNode->pNext); pTmpNode->pNext = NULL;
pList->cLen–;
4.“改” “查” 思路一样
遍历链表找到数据与之相同时,将需要改的数据改动
5.链表的销毁
流程:
1.传入指向标签指针的地址
2.判断是否为空链表
3.只要链表不为空循环头删节点
4.释放标签指针
5.将指针指向空(传入地址的原因,改变这个值)
void DestroyLinkList(LinkList **ppList) { while (!IsEmptyLinkList(*ppList)) { DeleteHeadLinkList(*ppList); }
free(*pList); *ppList = NULL; }
单向链表常考的算法
1.寻找中间节点:快慢指针
2.寻找倒数第K个节点:快慢指针
3.链表的倒置
4.循环链表的判断:快慢指针
5.约瑟夫环链表
2.双向链表:在单向链表的前提下加入指向前一个节点的指针
类比于单向链表的增删改查
倘若有很多数据时,数据的插入不再赋值,而是采用内存复制的形式) memcpy(&InsertNode->data, &data, sizeof(DataType));
3.内核链表:
创立内核的链表的原因:
往常单向,双向链表的数据域为固定的, 当创建两个结构体的数据或者不同的数据域时需要再写各个链表的函数。 而内核链表将一个节点(只有pNext和pPre)作为结构体的一部分 不管有哪些数据类型,创造时传入含有节点类型的结构体指针
内核链表有两个宏
1.container_of(ptr, type, member)
功能 通过一个结构体变量中一个成员的地址找到这个结构体变量的首地址 参数 ptr:指针
type:类型
member:成员 2.offsetof(struct s, c1)
功能: 获得结构体成员在内存中的偏移量 参数:
struct s:结构体名 c1:成员名 返回这个结构体成员在内存中的偏移量
通过这两个的作用,获得首地址和偏移量就可以得到节点在数据类型中的位置
而不常用的原因是常常将节点放在结构体的首地址上
内核链表需要注意的点: 在查找某个节点上的数据时,为了使查找函数适应各个信息查找,所以利用回调函数 遍历打印时,也用回调函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值