链表是什么
链表是一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一个节点。
接下来我们尝试定义一个链表:
typedef struct ListNode
{
struct ListNode* pNext;
int data;
} ListNode;
然后我们来理解链表中单双链表,有循环无循环和带头链表跟不带头链表的区别
单双链表
单链表表示每一个节点中存储的地址都指向下一个节点,双链表则表示每一个节点中除了记录下一个节点的地址,还会记录上一个节点的地址。相对于单链表来说,只能通过一个节点访问它后面的节点,而双链表既可以访问后面的元素,也可以访问前面的元素,这会使我们对链表的一些操作更容易得做到。图解如下:
循环链表和不循环链表
不循环链表是指链表从一个节点开始向后遍历,总会走到尽头(链表中的尽头我们一般用NULL来表示),而循环链表因为为节点指向了链表自身的某一节点,导致该链表如果遍历的话是无法找到NULL的。如我们前面所知,单链表如果从不是第一个节点开始访问,那么只能一个一个往后访问,该节点前面的内容我们无法获取。相对于不循环链表来说循环链表从任意节点开始都可以访问大链表里的所有节点。这会给一些操作提供更多的便利。循环链表图示如下:
带头链表和不带头链表
无头链表表示第一个节点跟其他节点完全一致,内部也有存储内容。带头链表是指在链表中有一个节点内不存内容,只是作为整个链表的起始位置来使用。即使链表为空,仍然含有一个头结点。下图即为一个有头不循环单链表:
单向,不带头,不循环链表代码实现
本次对链表实现的主要功能有初始化,头插头删,尾插尾删,按值删除第一个,按值删除所有,按位置删除,和按位置插入,数据销毁,数据打印等。
- 头文件如下
#ifndef __SLIST_H__
#define __SLIST_H__
typedef int DataType;
typedef struct SLNode
{
DataType data;
struct SLNode *pNext;
} SList;
void SLInit(SList **ppFirst);//初始化
// ;尾部插入
void SLPushBack(SList** ppFirst, DataType data);
// 头部插入
void SLPushFront(SList **ppFirst, DataType data);
// 尾部删除
void SLPopBack(SList **ppFirst);
// 头部删除
void SLPopFront(SList **ppFirst);
// 给定结点插入,插入到结点前
void SLInsert(SList **ppFirst, SList *pPos, DataType data);
// 给定结点删除
void SLErase(SList **ppFirst, SList *pPos);
// 按值删除,只删遇到的第一个
void SLRemove(SList **ppFirst, DataType data);
// 按值删除,删除所有的
void SLRemoveAll(SList **ppFirst, DataType data);
// 销毁
void SLDestroy(SList **ppFirst);
// 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL
int SLFind(SList *pFirst, DataType data);
void Print(SList *p); //打印函数
#endif//__SLIST_H__
- 具体功能实现如下
#include"slist.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
void SLInit(SList **ppFirst)//初始化
{
*ppFirst = NULL;
}
static SList* BuyNewNode(DataType data)
{
SList *pNewNode = (SList*)malloc(sizeof(SList));
assert(pNewNode);
pNewNode->data = data;
pNewNode->pNext = NULL;
return pNewNode;
}
void SLPushBack(SList** ppFirst, DataType data)// ;尾部插入
{
SList* pNewNode = BuyNewNode(data);
if (*ppFirst == NULL)
{
*ppFirst = pNewNode;
return;
}
SList* pNode = *ppFirst;
for (pNode = *ppFirst; pNode->pNext != NULL; pNode = pNode->pNext)
{
}pNewNode->pNext = pNode->pNext;
pNode->pNext = pNewNode;
}
void SLPushFront(SList **ppFirst, DataType data)//头插
{
SList *pNewNode = BuyNewNode(data);
pNewNode->pNext = *ppFirst;
*ppFirst = pNewNode;
}
void SLPopBack(SList **ppFirst)//尾删
{
if (*ppFirst == NULL)
{
printf("列表为空\n");
return;
}
if ((*ppFirst)->pNext == NULL)
{
free(*ppFirst);
*ppFirst = NULL;
return;
}
SList *pNod = *ppFirst;
for (pNod = *ppFirst; pNod->pNext->pNext != NULL; pNod = pNod->pNext)
{
;
}
SList *pDelete = pNod->pNext;
pNod->pNext = NULL;
free(pDelete);
}
void SLPopFront(SList **ppFirst) //头删
{
if (*ppFirst == NULL)
{
printf("链表为空\n");
return;
}
SList *pDelet = *ppFirst;
SList *pNEXT = pDelet->pNext;
free(pDelet);
*ppFirst = pNEXT;
//SList *pHead = *ppFirst;
//SList *pNext = pHead->pNext;
//free(pHead);
//*ppFirst = pNext;
}
void SLInsert(SList **ppFirst, SList *pPos, DataType data)//插入到给定节点前
{
if (*ppFirst == pPos)
{
SLPushFront(ppFirst, data);
}
SList *pNewNod = BuyNewNode(data);
SList *pNod = *ppFirst;
for (pNod = *ppFirst; pNod->pNext != pPos; pNod = pNod->pNext)
{
;
}
pNewNod->pNext = pPos;
pNod->pNext = pNewNod;
}
void SLErase(SList **ppFirst, SList *pPos)// 给定结点删除
{
if (*ppFirst == NULL)
{
printf("链表为空\n");
return;
}
if (*ppFirst == pPos)
{
SLPopFront(ppFirst);
return;
}
SList *pNode = *ppFirst;
for (pNode = *ppFirst; pNode->pNext != pPos; pNode = pNode->pNext)
{
;
}
pNode->pNext = pPos->pNext;
free(pPos);
pPos = NULL;
}
SList* SLfind(SList *pFirst,DataType data)
{
SList *pNode = pFirst;
for (pNode = pFirst; pNode != NULL; pNode = pNode->pNext)
{
if (pNode->data = data)
{
return pNode;
}
}
printf("要查找的值不存在\n");
return NULL;
}
void SLRemove(SList **ppFirst, DataType data)// 按值删除,只删遇到的第一个
{
SList *pPos = SLfind(*ppFirst, data);
if (pPos != NULL)
{
SLErase(ppFirst, pPos);
}
}
void SLRemoveAll(SList **ppFirst, DataType data)//按值删除所有
{
SList *pDelete = *ppFirst;
SList *pNode = *ppFirst;
while (pNode ->pNext!=NULL)
{
pDelete = pNode->pNext;
if (pDelete->data == data)
{
pNode->pNext = pDelete->pNext;
free(pDelete);
}
else
{
pNode = pNode->pNext;
}
}
if ((*ppFirst)->data == data)
{
SLPopFront(ppFirst);
}
}
void SLDestroy(SList **ppFirst)// 销毁
{
SList *pNode = *ppFirst;
SList *pNEXT = *ppFirst;
for (pNode = *ppFirst; pNode->pNext != NULL; pNode = pNEXT)
{
pNEXT = pNode->pNext;
free(pNode);
}
*ppFirst = NULL;
}
// 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL
void Print(SList *p)//打印
{
assert(p);
SList *pNode = p;
for (pNode=p; pNode != NULL; pNode = pNode->pNext)
{
printf("%2d -> ", pNode->data);
}
printf("NULL\n");
}
双向,循环,带头链表代码实现
由于双向链表与单向链表原理类似,所以在本节中代码演示功能主要有初始化,头插尾插,头删尾删,按位置插入和删除,清空链表留头节点,清空链表不留头节点。
- 头文件如下
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<malloc.h>
typedef int DataType;
typedef struct Dlist
{
DataType data;
struct Dlist *pPrev;
struct Dlist *pNext;
} Dlist;
static Dlist *BuyNewNode(DataType data);//申请新节点
void Init(Dlist **pphead);//初始化
void PushFront(Dlist *phead, DataType data);//头插
void PushBack(Dlist *phead, DataType data);//尾插
void Erase(Dlist *phead, Dlist *pPos);//按位置删除
void Insert(Dlist *phead, Dlist *pPos, DataType data);//按位置插入
void PopFront(Dlist *phead);//头删
void PopBack(Dlist *phead);//尾删
void Clear(Dlist *phead); //清空链表,头还在
void Destroy(Dlist *phead);//清空链表头不在
- 具体功能实现如下
#include"Dlist.h"
static Dlist *BuyNewNode(DataType data)
{
Dlist *pNewNode = (Dlist*)malloc(sizeof(Dlist));
assert(pNewNode);
pNewNode->data = data;
pNewNode->pNext = NULL;
pNewNode->pPrev = NULL;
return pNewNode;
}
void Init(Dlist **pphead)
{
int no_use = 0;
*pphead = BuyNewNode(no_use);
(*pphead)->pPrev = *pphead;
(*pphead)->pNext = *pphead;
}
void PushFront(Dlist *phead,DataType data)
{
Dlist *pNewNode = BuyNewNode(data);
Dlist *pFirst = phead->pNext;
phead->pNext = pNewNode;
pNewNode->pPrev = phead;
pNewNode->pNext = pFirst;
pFirst->pPrev = pNewNode;
}
void PushBack(Dlist *phead, DataType data)
{
Dlist *pNewNode = BuyNewNode(data);
Dlist *pLsat = phead->pPrev;
pLsat->pNext = pNewNode;
pNewNode->pPrev = pLsat;
pNewNode->pNext = phead;
phead->pPrev = pNewNode;
}
void Erase(Dlist *phead, Dlist *pPos)
{
Dlist *pLast = pPos->pPrev;
Dlist *pNext = pPos->pNext;
pLast->pNext = pNext;
pNext->pPrev = pLast;
free(pPos);
}
void Insert(Dlist *phead, Dlist *pPos,DataType data)
{
Dlist *pNewNode = BuyNewNode(data);
Dlist *pPrev = pPos->pPrev;
pPrev->pNext = pNewNode;
pNewNode->pPrev = pPrev;
pNewNode->pNext = pPos;
pPos->pPrev = pNewNode;
}
void PopFront(Dlist *phead)
{
Dlist *pDelete = phead->pNext;
Dlist *pNewFirst = phead->pNext->pNext;
phead->pNext = pNewFirst;
pNewFirst->pPrev = phead;
free(pDelete);
}
void PopBack(Dlist *phead)
{
Dlist *pDelete = phead->pPrev;
Erase(phead, pDelete);
}
void Clear(Dlist *phead) //清空链表,头还在
{
Dlist *pNode = phead->pNext;
Dlist *pNext = pNode;
for (pNode = phead->pNext; pNode != phead; pNode = pNext)
{
pNext = pNode->pNext;
free(pNode);
}
phead->pNext = phead;
phead->pPrev = phead;
}
void Destroy(Dlist *phead)
{
Clear(phead);
free(phead);
phead = NULL;
}