一、概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表是一种有序的列表。链表的内容通常存储与内存中分散的位置上。链表由节点组成,每一个结点的结构都相同。节点分为数据域或链域,数据域顾名思义就是存放节点的内容,链域存放的是下一个节点的指针,也就是螳螂捕蝉黄雀在后的道理。
- 链表结构
1、单向、双向
2、带头、不带头
3、循环、非循环
1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向 循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而 简单了,后面我们代码实现了就知道了。
二、传参总结:
- 传值:实参是形参的一份拷贝,不能通过形参改变外部的实参
- 传地址:形参中就是实参的地址,可以通过对形参解引用拿到实参,可以通过修改形参来达到对实参的改变
- 因此:如果想要通过形参来改变实参,必须传递实参的地址 如果实参是普通类型的变量
- 比如:inta---->&a—>一级指针
- 如果实参是指针类型–>int* pa =&a;在函数中想要修改实参pa的指向,必须要传递pa的地址,即int**二级指针
三、复杂链表的复制
1.在原链表每个节点之后插入值相等的新节点
2.给新插入节点的随机指针域进行赋值
3、将新插入的节点从原链表中拆下来
四、接口实现
1、无头节点无循环的单向链表
typedef int SLDataType;
typedef struct SListNode
{
struct SListNode* next;//指向下一个节点的地址
SLDataType data;//存储该节点中的数据
}SListNode;
//申请节点
SListNode* BuySListNode(SLDataType data)
{
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
if (NULL == newNode)
{
assert(0);
return NULL;
}
newNode->next = NULL;
newNode->data = data;
return newNode;
}
/*
注意:
如果想要在函数中改变头指针的指向,形参必须为二级指针
如果不需要在函数中改变头指针的指向,只需传递一级直线即可,比如:SListFind
*/
void SListPushBack(SListNode** head, SLDataType data)
{
assert(head);//链表不存在
//链表为空
SListNode* newNode = BuySListNode(data);
if (NULL == *head)
{
*head = newNode;
}
else
{
SListNode* cur = *head;
while (cur->next)
{
cur = cur->next;
}
cur->next = newNode;
//newNode->next = NULL;
}
}
void SListPopBack(SListNode** head)
{
assert(head);//检测非法情况
if (NULL == *head)
{
return;
}
else if (NULL == (*head)->next)
{
free(*head);
*head = NULL;
}
else
{
SList