1. 什么是链表,链表的分类?
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序是实现的
链表的结构有很多,以下情况组合起来就有8种链表结构:
单向、双向;带头、不带头;循环、非循环
我们在实际中最常用的有两种结构:无头单向非循环链表(结构简单)和带头双向循环链表(结构最复杂)
2. 链表带头结点和不带头结点有什么区别?
数据结构中,链表分为有头结点和无头节点
头结点:在单链表的第一个节点之前设置一个节点,它没有直接前驱,被称为头结点。 头结点的数据域不存储任何信息,指针域指向链表的第一个节点
头指针:头指针是指向链表中第一个节点的指针,链表中可以没有头结点,但是一定不能没有头指针。在有头节点的链表中,头指针指向的是头结点
链表有头结点和无头节点就直接导致了对链表的基本操作的不同
因为有了头节点,不管链表是否为空,链表(包括空表)的头指针一定是非空的,这就使对链表中第一个位置的操作与其他位置的操作一致,更加方便。
3. 单链表的基本操作(有头节点)
1.链表的节点
typedef int SDataType;
// 链表的节点
typedef struct SListNode
{
SDataType _data;
struct SListNode* _pNext;
}Node, *PNode;
2.链表的结构
给一个头指针保存链表第一个节点的地址
typedef struct SList
{
PNode _pHead; // 指向链表中的第一个节点
}SList, *PSList;
3. 链表的初始化
void SListInit(SList* s)
{
assert(s);
s->_pHead = NULL;
}
4.链表的尾插
//在链表s最后一个节点后插入一个值为data的新节点
void SListPushBack(SList* s, SDataType data)
{
assert(s);
PNode pNewNode = BuySListNode(data);
if (NULL == s->_pHead)
{
// 空链表
s->_pHead = pNewNode;
}
else
{
// 链表非空
// 找链表中最后一个节点
PNode pCur = s->_pHead;
while (pCur->_pNext)
pCur = pCur->_pNext;
pCur->_pNext = pNewNode;
}
}
5.链表的尾删
//删除链表s最后一个节点
void SListPopBack(SList* s)
{
assert(s);
if (NULL == s->_pHead) // 空链表
return;
else if (NULL == s->_pHead->_pNext) // 只有一个节点
{
free(s->_pHead);
s->_pHead = NULL;
}
else
{
// 多个节点
PNode pPre = NULL;
PNode pCur = s->_pHead;
while (pCur->_pNext)
{
pPre = pCur;
pCur = pCur->_pNext;
}
free(pCur);
pPre->_pNext = NULL;
}
}
6.链表的头插
//在链表s第一个节点前插入值为data的节点
void SListPushFront(SList* s, SDataType data)
{
assert(s);
PNode pNewNode = BuySListNode(data);
pNewNode->_pNext = s->_pHead;
s->_pHead = pNewNode;
}
7.链表的头删
//删除链表s的第一个节点
void SListPopFront(SList* s)
{
PNode pDelNode = NULL;
assert(s);
if (NULL == s->_pHead)
return;
// 找到待删除的节点
pDelNode = s->_pHead;
s->_pHead = pDelNode->_pNext;
free(pDelNode);
}
8.在链表的任意位置插入节点
//在链表的pos位置后插入值为data的节点
void SListInsert(PNode pos, SDataType data)
{
if (NULL == pos)
return;
PNode pNewNode = NULL;
pNewNode = BuySListNode(data);
pNewNode->_pNext = pos->_pNext;
pos->_pNext = pNewNode;
}
9. 删除链表s中pos位置的节点
void SListEarse(SList* s, PNode pos) {
assert(s);
if (NULL == pos || NULL == s->_pHead) {
return;
}
if (pos == s->_pHead) {
s->_pHead = s->_pHead->_pNext;
}
else{
PNode pPrePos = s->_pHead;
while (pPrePos && pPrePos->_pNext != pos)
pPrePos = pPrePos->_pNext;
if (pPrePos)
pPrePos->_pNext = pos->_pNext;
}
free(pos);
}
10.在链表中查找值为data的节点,找到返回该节点的地址,否则返回NULL
PNode SListFind(SList* s, SDataType data) {
assert(s);
PNode pCur = s->_pHead;
while (pCur) {
if (pCur->_data == data) {
return pCur;
}
pCur = pCur->_pNext;
}
return NULL;
}
11. 获取链表中有效节点的个数
int SListSize(SList* s)
{
assert(s);
PNode pCur = s->_pHead;
size_t count = 0;
while (pCur)
{
count++;
pCur = pCur->_pNext;
}
return count;
}
12.检测链表是否为空
int SListEmpty(SList* s)
{
assert(s);
return NULL == s->_pHead;
}
13.将链表中有效节点清空
// 将链表中有效节点清空
void SListClear(SList* s) {
assert(s);
while (SListSize(s)) {
SListPopBack(s);
}
free(s->_pHead);
}
14.销毁链表
void SListDestroy(SList* s) {
assert(s);
while (SListSize(s)) {
SListPopBack(s);
}
free(s->_pHead);
}