概述
在学习过程中, 以掌握不带头结点的单链表与带头节点的双向循环链表为主.
初次接触链表, 总会被头结点所吸引, 其实在链表的代码实现上, 一些链表甚至不需要头结点
原来, 链表实际是被头指针所控制!!!
- 不带头结点的单链表
- 记住指针传入必须传二级指针, 因为要改变pHead的指向, pHead最为指针传入函数后会被复制, 然 后函数将改变形参的内容, 而不影响实参pHead保存的地址.
- 故通过封装 SList, 封装二级指针
- 单链表的插入只插向当前位置后面由于单向性决定, 而双链表一半会插到前面
- 单链表的清空与销毁一样
- 题目中若未说明, 则指的是不带头结点的链表
- 带头结点的双向循环链表
- 除Init, Destroy其他函数不用穿二级指针, 应为pHead指向永远为头结点, 指针指向不变, 改变的是头结点的指向
- 不建议头结点存放链表的信息, 如链表的有效结点的个数, 因为若数据类型为char, 则只能保存256个个数
谨记为什么不带头的链表要传二级指针, 而带头的链表只用传
- 举个例子:
void func(int* pa1, int* pb2)
{
int* tmp = pa1;
pa1 = pb2;
pb2 = tmp;
}
// 想通过形参改变外部实参的话, 必须传外部实参的地址
// 这里交换了指针, pa1是pa的拷贝, 内容变成pb的内容但是并未影响实参
主函数:
int a = 5, b = 6;
int* pa = &a;
int* pb = &b;
func(pa, pb);
printf("%d \n", *pa);// 结果任为5
结论: 改变指针的指向必须传二级指针
- 无头链表的头指针直接指向第一个节点, 若改变头指针的指向必须传二级指针
- 带头的链表头指针指向头结点, 其指向不会改变, 变的是头结点的指向
无头结点单链表实现代码:
- 头文件
#include <stdio.h>
typedef int SDataType;
typedef struct SListNode
{
SDataType _data;
struct SListNode* _pNext;
}Node, *PNode;
typedef struct SList
{
PNode _pHead;
}SList;
// 一个单链表的结构体, 传来传去比较爽
// 用于保存链表, 但不是头结点!也不是第一个节点!
// _pHead指向第一个节点
// 封装了二级指针, 否则要传送二级指针
void SListInit(SList* s);
void SListDestory(SList* s);
void SListPushBack(SList* s, SDataType data);
void SListPopBack(SList* s);
void SListPushFront(SList* s, SDataType data);
void SListPopFront(SList* s);
void SListInsert(PNode pos, SDataType data);
void SListErase(SList* s, PNode pos);
PNode SListFind(SList* s, SDataType data);
void SListRemove(SList* s, SDataType data); // 移除链表中第一个内容为data的元素
void SListRemoveAll(SList* s, SDataType data); // 移除链表中所有内容为data的元素
size_t SListSize(SList* s);
int SListEmpty(SList* s);
void PrintSList(SList* s);
- 实现
#include "SList.h"
#include <assert.h>
#include <stdio.h>
#include <malloc.h>
void SListInit(SList* s)
{
assert(s);
s->_pHead = NULL;
}
void SListDestory(SList* s)
{
assert(s);
PNode pCur = s->_pHead;
while (pCur)
{
SListPopFront(s);
pCur = s->_pHead;
}
}
// 用于创建一个新节点
PNode BuySListNode(SDataType data)
{
PNode pCur = (PNode)malloc(sizeof(Node));
pCur->_data = data;
pCur->_pNext = NULL;
return pCur;
}
void SListPushBack(SList* s, SDataType data)
{
assert(s);
PNode pNewPNode = BuySListNode(data);
if (NULL == s->_pHead)
{
// 空链表
s->_pHead = pNewPNode;
}
else
{
// 链表非空
// 找链表中最后一个节点
PNode pCur = s->_pHead;
while (pCur->_pNext)
{
pCur = pCur->_pNext;
}
pCur->_pNext = pNewPNode;
}
}
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;
}
}
void SListPushFront(SList* s, SDataType data)
{
assert(s);
PNode pNewNode = BuySListNode(data);
pNewNode->_pNext = s->_pHead;
s->_pHead = pNewNode;
}
void SListPopFront(SList* s)
{
assert(s);
if (NULL == s->_pHead)
{
return;
}
PNode pDelNode = s->_pHead;
s->_pHead = pDelNode->_pNext;
free(pDelNode);
}
void SListInsert(PNode pos, SDataType data)
{
if (NULL == pos)
{
return;
}
PNode pNewNode = BuySListNode(data);
pNewNode->_pNext = pos->_pNext;
pos->_pNext = pNewNode;
}
void SListErase(SList* s, PNode pos)
{
// 删除Pos指向, 必须找到前一个
assert(s);
if (NULL == pos || NULL == s->_pHead)
{
return;
}
if (pos == s->_pHead)
{
s->_pHead = pos->_pNext;
}
else
{
PNode pPrePos = s->_pHead;
while (pPrePos && pPrePos->_pNext != pos) // 找到pos前一个
{ // 判断pPrePos是否存在, 为了防止pos可能与s不是一个链表
pPrePos = pPrePos->_pNext;
}
if (pPrePos)
{
pPrePos->_pNext = pos->_pNext;
}
}
free(pos);
}
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;
}
// 移除链表中第一个值为data的元素
void SListRemove(SList* s, SDataType data)
{
assert(s);
if (NULL == s->_pHead)
{
return;
}
PNode pCur = s->_pHead;
PNode pPre = NULL;
while (pCur)
{
if (pCur->_data == data)
{
if (pCur == s->_pHead)
{
s->_pHead->_pNext = pCur->_pNext;
}
else
{
pPre->_pNext = pCur->_pNext;
}
free(pCur);
return;
}
else
{
pPre = pCur;
pCur = pCur->_pNext;
}
}
}
void SListRemoveAll(SList* s, SDataType data)
{
assert(s);
if (NULL == s->_pHead)
{
return;
}
PNode pCur = s->_pHead;
PNode pPre = NULL;
while (pCur)
{
if (pCur->_data == data)
{
if (NULL == pPre)
{
s->_pHead->_pNext = pCur->_pNext;
free(pCur);
pCur = s->_pHead;
}
else
{
pPre->_pNext = pCur->_pNext;
free(pCur);
pCur = pPre->_pNext;
}
}
else
{
pPre = pCur;
pCur = pCur->_pNext;
}
}
}
size_t SListSize(SList* s)
{
assert(s);
size_t count = 0;
PNode pCur = s->_pHead;
while (pCur)
{
count++;
pCur = pCur->_pNext;
}
return count;
}
int SListEmpty(SList* s)
{
assert(s);
return NULL == s->_pHead;
}
void PrintSList(SList* s)
{
assert(s);
PNode pCur = s->_pHead;
while (pCur)
{
printf("%d--->", pCur->_data);
pCur = pCur->_pNext;
}
printf("NULL\n");
}
有头结点双向循环链表实现代码:
- 头文件
typedef int DLDataType;
typedef struct DListNode
{
struct DListNode* _pNext;
struct DListNode* _pPre;
DLDataType _data;
}DLNode, *PDLNode;
void DListInit(PDLNode* pHead);
void DListDestroy(PDLNode* pHead);
PDLNode BuyDListNode(DLDataType data);
void DListPushBack(PDLNode pHead, DLDataType data);
void DListPopBack(PDLNode pHead);
void DListPushFront(PDLNode pHead, DLDataType data);
void DListPopFront(PDLNode pHead);
void DListInsert(PDLNode pos, DLDataType data);
void DListErase(PDLNode pos);
void DListClear(PDLNode pHead);
- 实现
#include "DList.h"
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
void DListInit(PDLNode* pHead)
{
assert(pHead);
*pHead = (PDLNode)malloc(sizeof(DLNode));
if (NULL == *pHead)
{
assert(0);
return;
}
(*pHead)->_pNext = *pHead;
(*pHead)->_pPre = *pHead;
}
void DListDestroy(PDLNode* pHead)
{
// 改变指向, 二级指针
DListClear(*pHead);
free(*pHead);
(*pHead) = NULL;
}
PDLNode BuyDListNode(DLDataType data)
{
PDLNode pNewNode = (PDLNode)malloc(sizeof(DLNode));
if (NULL == pNewNode)
{
assert(0);
return NULL;
}
pNewNode->_data = data;
pNewNode->_pNext = NULL;
pNewNode->_pPre = NULL;
return pNewNode;
}
void DListPushBack(PDLNode pHead, DLDataType data)
{
assert(pHead);
PDLNode pNewNode = BuyDListNode(data);
pNewNode->_pPre = pHead->_pPre;
pNewNode->_pNext = pHead;
pHead->_pPre = pNewNode;
pNewNode->_pPre->_pNext = pNewNode;
}
void DListPopBack(PDLNode pHead)
{
assert(pHead);
if (pHead->_pNext == pHead)
{
return;
}
PDLNode pDelNode = pHead->_pPre;
pDelNode->_pPre->_pNext = pDelNode->_pNext;
pDelNode->_pNext->_pPre = pDelNode->_pPre;
free(pDelNode);
}
void DListPushFront(PDLNode pHead, DLDataType data)
{
assert(pHead);
PDLNode pNewNode = BuyDListNode(data);
pNewNode->_pPre = pHead;
pNewNode->_pNext = pHead->_pNext;
pHead->_pNext = pNewNode;
pNewNode->_pNext->_pPre = pNewNode;
}
void DListPopFront(PDLNode pHead)
{
assert(pHead);
if (pHead->_pNext == pHead)
{
return;
}
PDLNode pDelNode = pHead->_pNext;
pDelNode->_pNext->_pPre = pHead;
pHead->_pNext = pDelNode->_pNext;
free(pDelNode);
}
void DListInsert(PDLNode pos, DLDataType data)
{
if (NULL == pos)
{
return;
}
PDLNode pNewNode = BuyDListNode(data);
pNewNode->_pPre = pos->_pPre;
pNewNode->_pNext = pos;
pNewNode->_pPre->_pNext = pNewNode;
pNewNode->_pNext->_pPre = pNewNode;
}
void DListErase(PDLNode pos)
{
if (NULL == pos)
{
return;
}
pos->_pPre->_pNext = pos->_pNext;
pos->_pNext->_pPre = pos->_pPre;
free(pos);
}
void DListClear(PDLNode pHead)
{
// 清空, 不删除头结点
assert(pHead);
PDLNode pCur = pHead->_pNext;
while (pCur != pHead)
{
pHead->_pNext = pCur->_pNext;
free(pCur);
pCur = pHead->_pNext;
}
pHead->_pNext = pHead;
pHead->_pPre = pHead;
}
void PrintDList(PDLNode pHead)
{
PDLNode pCur = pHead;
while (pCur->_pNext && pCur->_pNext != pHead)
{
printf("%d ", pCur->_pNext->_data);
pCur = pCur->_pNext;
}
}