1. 静态链表和动态链表的区别:
(1)静态链表放在栈区
(2)动态链表放在堆区,堆区数据必须要手工开辟,手工释放
2. 单链表定义
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct LinkNode
{
int num; //数据域
struct LinkNode * next; //指针域
}
3. 静态链表的创建方法及使用
struct LinkNode node1 = {10, NULL};
struct LinkNode node2 = {20, NULL};
node1.next = &node2; //建立结点之间的关系
struct LinkNode * pCurrent = &node1;
while(pCurrent != NULL) //遍历链表
{
printf("%d\n", pCurrent->num); //打印结点中的数据
pCurrent = pCurrent->next;
}
4. 动态链表的创建方法及使用
struct LinkNode * node1 = malloc(sizeof(struct LinkNode));
struct LinkNode * node2 = malloc(sizeof(struct LinkNode));
node1->next =node2; //建立结点之间的关系
struct LinkNode * pCurrent = node1;
while(pCurrent != NULL) //遍历链表
{
printf("%d\n", pCurrent->num); //打印结点中的数据
pCurrent = pCurrent->next;
}
free(node1);
free(node2);
5. 常用约定:头结点不保存数据域,只保存指针域的一个空结点。
设定空头结点的好处:空头结点便于头插,头部插入数据方便,头结点永远不会变!
6. 动态链表的相关操作(函数)
(1)初始化链表,让用户拿到一个链表的头结点
struct LinkNode * initLinkList()
{
//创建头结点
struct LinkNode * pHeader = malloc(sizeof(struct LinkNode));
if(pHeader != NULL)
{
return NULL;
}
pHeader->next = NULL; //初始化头结点
// 让用户初始化链表
//专门用于维护当前链表中的尾节点,好处在于添加数据方便
struct LinkNode * pTail = pHeader; //这句很重要!!!!
int val = -1;
while(1)
{
printf("请输入链表中的数据,-1代表输入结果\n");
scanf("%d",&val);
if(val == -1)
{
break;
}
//创建新结点
struct LinkNode * newNode = malloc(sizeof(struct LinkNode));
newNode->num = val;
newNode->next = NULL;
//更改指针的指向
pTail->next = newNode; //相当于当前链表的最后一个结点的指针指向了当前新创建的结点
pTail = newNode;//使pTail指向当先新建的结点。即永远指向最后一个结点
}
return pHeader;
}
(2)动态链表的遍历
void foreachList(struct LinkNode * pHeader)
{
if(pHeader == NULL)
{
return;
}
struct LinkNode * pCurrent = pCurrent->next;
while(pCurrent != NULL) //遍历链表
{
printf("%d\n", pCurrent->num); //打印结点中的数据
pCurrent = pCurrent->next;
}
}
(3)动态链表插入结点
void insertList(struct LinkNode * pHeader, int oldVal, int newVal)
{
//在oldVal后面插入newVal
if(pHeader == NULL)
{
return;
}
//创建两个临时辅助指针
struct LinkNode * pPrev = pHeader;
struct LinkNode * pCurrent = pHeader->next;
while(pCurrent != NULL)
{
//用户传入的数据和遍历的链表中的数据一样
if(pCurrent->num == oldVal) //找到了
{
break;
}
//没找到,两个指针往后移
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
//创建新结点
struct LinkNode * newNode = malloc(sizeof(struct LinkNode));
newNode->num = newVal;
newNode->next = NULL;
//更改指针的指向
newNode->next = pCurrent;
pPrev->next = newNode;
}
(4)删除动态链表中一个结点
void deleteList(struct LinkNode * pHeader, int Val)
{
if(pHeader == NULL)
{
return;
}
//创建两个临时辅助指针
struct LinkNode * pPrev = pHeader;
struct LinkNode * pCurrent = pHeader->next;
while(pCurrent != NULL)
{
//用户传入的数据和遍历的链表中的数据一样
if(pCurrent->num == Val) //找到了
{
break;
}
//没找到,两个辅助指针往后移
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
//如果链表中没有用户传入的Val值,直接return
if(pCurrent == NULL)
{
return;
}
//链表中有用户传入的Val值且已经找到,更改指针的指向
pPrev->next = pCurrent->next;
//删除结点并释放堆区数据
free(pCurrent);
pCurrent = NULL; //严谨的写法,防止野指针出现
}
(5)清空一个动态链表
注意:清空一个动态链表 是 把所有有数据的结点释放掉,还留着一个头结点,并没有将链表销毁掉,还可以从头插入数据。
void clearList(struct LinkNode * pHeader)
{
if(pHeader == NULL)
{
return;
}
//创建1个临时辅助指针
struct LinkNode * pCurrent = pHeader->next; //指向第一个有效数据的结点
while(pCurrent != NULL)
{
//先保存住下一个结点的位置
struct LinkNode * nextNode = pCurrent->next;
//删除当前结点并释放堆区数据
free(pCurrent);
pCurrent = nextNode;
}
pHeader->next = NULL;
}
(6)销毁链表
注意:销毁链表 是 将链表销毁掉,头结点也没了。
void destroyList(struct LinkNode * pHeader)
{
if(pHeader == NULL)
{
return;
}
//先清空链表
clearList(pHeader);
//将头结点也释放掉
free(pHeader);
pHeader = NULL;
}
(7)翻转链表
void reverList(struct LinkNode * pHeader)
{
if(pHeader == NULL)
{
return;
}
//创建三个临时辅助指针
struct LinkNode * pPrev = NULL;
struct LinkNode * pCurrent = pHeader->next;
struct LinkNode * pNext = NULL;
while(pCurrent != NULL)
{
pNext = pCurrent->next; //pNext存储当前结点的下一个结点
pCurrent->next = pPrev;
//移动辅助指针
pPrev =pCurrent; //pPrev永远指向翻转后链表的第一个结点
pCurrent = pNext; //pCurrent永远指向剩下没有翻转的链表的第一个结点
}
pHeader->next = pPrev;
}