通常对于链表,我们可以定义为一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一个结点。
首先,我定义了一个有关链表结点的结构体,其中包括一个储存于当前链表结点的元素data和一个指向下一个结点的结构体指针next。代码如下:
typedef int DataType;
typedef struct ListNode{
DataType data;
struct ListNode *next;
} ListNode;
对于链表中结点的创建,链表的初始化和销毁的定义,代码如下:
//创建结点
static ListNode *CreateNode(DataType data)
{
ListNode *newNode = (ListNode*)malloc(sizeof(ListNode));
assert(newNode);
newNode->data = data;
newNode->next = NULL;
return newNode;
}
//初始化
void ListInit(ListNode **ppfirst)
{
assert(ppfirst != NULL);
*ppfirst = NULL;
}
//销毁
void ListDestory(ListNode **ppfirst)
{
*ppfirst = NULL;
}
完成这些操作后,下面将叙述有关链表的插入,删除和查找等相关知识。首先叙述有关链表的查找。
对于一个链表的查找,应当循环遍历整个链表,如果链表中有结点匹配到到查找的元素data,将其返回,否则返回NULL。代码如下:
//查找
ListNode *ListFind(ListNode *first, DataType data)
{
for (ListNode *cur = first; cur != NULL; cur = cur->next)
{
if (data == cur->data)
return cur;
}
return NULL;
}
接下来叙述对于有关链表的的插入,链表的插入基本可以分为头插,尾插,指定结点前插入三种情况,下面将一一列举:
1.头插,代码如下:
//头插
void ListPushFront(ListNode **ppfirst, DataType data)
{
assert(ppfirst != NULL);
//正常情况
//指针ppfirst指向头结点,从堆上申请空间
ListNode *newNode = CreateNode(data);
newNode->next = *ppfirst;
*ppfirst = newNode;
}
2.尾插,当链表为空时,直接插入。如果链表不为空,应当循环遍历整个链表找到最后一个结点,然后在其尾部做插入即可。代码如下:
//尾插
void ListPushBack(ListNode **ppfirst, DataType data)
{
ListNode *newNode = CreateNode(data);
//特殊情况,找到倒数第一个(至少有一个,特殊情况是链表为空)
if (*ppfirst == NULL)
{
*ppfirst = newNode;
return;
}
//通常情况
ListNode *cur = *ppfirst;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = newNode;
}
3.在一个结点前插入,首先定义一个指针pos标记指定结点。如果pos是头结点,直接做头插即可。否则,循环遍历整个链表并找到pos前一个结点,然后在它们中间插入即可。相关代码如下:
//在结点前做插入(结点pos肯定在链表中&&pos不是空)
void ListInsert(ListNode **ppfirst, ListNode *pos, DataType data)
{
//头插
if (*ppfirst==pos)
{
ListPushFront(ppfirst, data);
return;
}
ListNode *cur = *ppfirst;
//找到pos前一个结点
while (cur->next != pos)
{
cur = cur->next;
}
//插入新结点
ListNode *newNode = CreateNode(data);
newNode->next = cur-> next;
cur->next = newNode;
}
同样对于一个链表的的删除,也基本可以分为头删,尾删,删除指定结点三种情况,下面也将一一列举:
1.头删,首先应确保指向链表头结点的指针变量地址有效,并且链表不为空的情况下,才能做删除。相关代码如下:
//头删
void ListPopFront(ListNode **ppfirst)
{
assert(ppfirst != NULL);//变量地址不为NULL
assert(*ppfirst != NULL);//不能是空链表
ListNode *del = *ppfirst;
*ppfirst = del->next;
//重点
free(del);
}
2.尾删,同样得确保确保指向链表头结点的指针变量地址有效,并且链表不为空。当链表中有只有一个结点,直接删除。否则,循环遍历整个链表找到并找到最后一个节点,然后删除。代码如下:
//尾删
void ListPopBack(ListNode **ppfirst)
{
assert(ppfirst != NULL);//变量地址不为NULL
assert(*ppfirst != NULL);//不能是空链表
//链表中只有一个结点
if ((*ppfirst)->next == NULL)
{
free(*ppfirst);
*ppfirst = NULL;
return;
}
//正常情况
ListNode *del;
ListNode *cur = *ppfirst;
while (cur->next->next != NULL)
{
cur = cur->next;
}
del = cur->next;
cur->next = NULL;
free(del);
}
3.删除指定的结点,与指定结点前插入一样,应当定义一个指针pos标记指定结点。当pos是头结点,直接做头删。否则,循环遍历整个链表找到pos这个结点,然后删除即可。代码如下:
//删除指定结点(结点pos肯定在链表中&&pos不是空)
void ListErase(ListNode **ppfirst, ListNode *pos)
{
//头删
if (*ppfirst == pos)
{
ListPopFront(ppfirst);
return;
}
ListNode *cur = *ppfirst;
//找到pos前一个结点
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
}