C:无空头链表
1.链表的结构体以及全局变量声明
#include <stdio.h>
#include <stdlib.h>
// 创建Node结构体
struct Node
{
int a;
struct Node * pNext;
};
//链表头尾指针
struct Node* g_pHead = NULL; //刚开始链表头部为null(头部:内容,该节点的内容存储)
struct Node* g_pEnd = NULL; //刚开始链表尾部为null 头尾都为null,表示空头链表
2.创建链表函数(插尾法和插头法)
①插尾插入法
//创建链表,在链表中增加一个数据,尾添加(g_pEnd发生变化,但是g_pHead不变化,只与第一个传入的Temp有关)
void AddListTail(int a)
{
//创建一个结点
struct Node * pTemp = (struct Node*)malloc(sizeof(struct Node));
//结点数据进行赋值
pTemp->a = a;
pTemp->pNext = NULL;
//链接
if(NULL == g_pHead || NULL == g_pEnd) //也就是刚开始,头尾都是空,其实写一个就可以,头是空,尾就是空
{
g_pHead = pTemp; //此时pTemp的地址既是头也是尾,在之后的调用中,g_pHead始终等于第一次传入的pTemp
//g_pEnd = pTemp; //g_pEnd赋值成为pTemp新结点
}
else//此时链表不再为空
{
g_pEnd->pNext = pTemp; //g_pEnd尾部指向新来的结点
//g_pEnd = pTemp;//指向之后,g_pEnd下移成为pTemp新的结尾结点
}
g_pEnd = pTemp;//在每一次调用函数后,g_pEnd都等于此时传入的pTemp g_pHead g_pEnd 此时由于是尾插入,g_pEnd地址指向尾部新增,并且之后g_pEnd变为最后一个
}
在这里有一点很巧妙,g_pHead = pTemp只是在第一次参数传入的时候赋值,但是在第一次传入参数的最后让g_pEnd也等于pTemp,这样做使在之后,g_pEnd指向下一个值的结构体指针也能被g_pHead使用
同时,如果去打印g_pHead以及g_pEnd的pNext地址可以发现
(1)g_pHead的pNext永远指向首结点的下一个地址
(2)g_pEnd的pNext是变化的,因为g_pEnd结点在变化,pNext指向当前的下一跳结点
②插头插入法
//创建链表,在链表中增加一个数据,头添加(g_pHead发生变化,但是g_pEnd不变化,只与第一个传入的Temp有关)
void AddListHead(int a)
{
//创建一个结点
struct Node * pTemp = (struct Node*)malloc(sizeof(struct Node));
//结点数据进行赋值
pTemp->a = a;
pTemp->pNext = NULL;
//链接
if(NULL == g_pHead || NULL == g_pEnd) //也就是刚开始,头尾都是空,其实写一个就可以,头是空,尾就是空
{
g_pHead = pTemp; //此时pTemp的地址既是头也是尾,在之后的调用中,g_pHead始终等于第一次传入的pTemp
g_pEnd = pTemp; //g_pEnd赋值成为第一个传入的pTemp,之后不再改变
}
else//此时链表不再为空
{
g_pTemp->pNext = g_pHead;
g_pHead = g_pTemp; g_pHead g_pEnd 此时由于是头插入,新增的g_pTemp地址指向头部,并且之后g_pHead变为第一个
}
}
3.链表的遍历
①全部遍历
void ScanList()
{
struct Node *pTemp = g_pHead; //初始化*pTemp为头部,如果不初始化,则pTemp为最后一位,在本案例为10
while(pTemp != NULL)//当链表结点的内容指向null,说明遍历结束
{
printf("%d\n", pTemp->a);
pTemp = pTemp->pNext; //每一次循环指向链表前一个结点
}
}
在main函数中
直接调用ScanList()
②查询指定结点 通过返回结构体类型,然后通过结构体类型变量pFind来接收
struct Node * SelectNode(int a)
{
struct Node *pTemp = g_pHead; //初始化*pTemp为头部,如果不初始化,则pTemp为最后一位,在本案例为10
while(pTemp != NULL)//当链表结点的内容指向null,说明遍历结束
{
if (pTemp->a == a)
{
return pTemp;
}
pTemp = pTemp->pNext; //每一次循环指向链表前一个结点
}
return NULL; // 如果找不到返回null
}
在main函数中
struct Node * pFind = SelectNode(-1);
if (pFind != NULL)
{
printf("%d\n", pFind->a);
}
else
{
printf("链表中没有该值\n");
}
4.链表清空
①清空列表
void FreeList()
{
struct Node *pTemp = g_pHead; //初始化*pTemp为头部,如果不初始化,则pTemp为最后一位,在本案例为10
while(pTemp != NULL)//当链表结点的内容指向null,说明遍历结束
{
struct Node * pt = pTemp;
pTemp = pTemp->pNext; //每一次循环指向链表前一个结点
free(pt);
}
// 头尾清空
g_pHead = NULL;
g_pEnd = NULL;
}
在这里,重要的是free要放在最后free
如果free(pTemp)再去指向,则pTemp已经为空,没有指针的作用,如果先指向之后直接free(pTemp)则会导致指向失败
关键是通过结构体指针变量来接收之后,free该变量。最后千万记得去头尾清空,不然g_pHead以及g_pEnd变为野指针,不再是NULL
此时一定要置野指针为NULL
5.指定位置插入结点
void AddListRand(int index, int a)
{
//链表为空
if (NULL == g_pHead)
{
printf("链表没有结点\n");
return;
}
//找位置
struct Node * pt = SelectNode(index);
if (NULL == pt)
{
printf("没有指定结点\n");
return;
}
//有此结点
//给a创建结点
struct Node * pTemp = (struct Node*)malloc(sizeof(struct Node));
//给结点成员进行赋值
pTemp->a = a;
pTemp ->pNext = NULL;
//链接到链表上
if (pt == g_pEnd)
{
g_pEnd->pNext = pTemp;
g_pEnd = pTemp;
}
else
{
//先连
pTemp->pNext = pt->pNext;
//后断
pt->pNext = pTemp;
}
}
6.删除结点
①头删除
void DeleteListHead()
{
if (NULL == g_pHead)
{
printf("链表为NULL,无需释放\n");
return;
}
//记录旧的头
struct Node*pTemp = g_pHead;
//头的下一个结点变成新的头
g_pHead = g_pHead->pNext;
//释放旧的头
free(pTemp);
}
从链表的首项开始删除
②尾删除
void DeleteListEnd()
{
//判断链表是否为空
if (NULL == g_pEnd)
{
printf("链表为NULL,无需释放\n");
return;
}
//链表不为空
//链表有1个结点
if (g_pHead == g_pEnd)
{
free(g_pHead);
g_pHead = NULL;
g_pEnd = NULL;
}
else
{
//找到尾巴前一个结点
struct Node * pTemp = g_pHead;
while (pTemp->pNext != g_pEnd)
{
pTemp = pTemp->pNext;
}
//找到了,删除尾巴
//释放尾巴
free(g_pEnd);
//尾巴前移
g_pEnd = pTemp;
//尾的下一个指针赋值NULL
g_pEnd->pNext = NULL;
}
}
从列表的尾部开始删除,思想:首先从链表的首项开始遍历,找到最后一项的前一个则停止循环
③指定位置删除
void DeleteListRand(int a)
{
if (NULL == g_pHead)
{
printf("链表为NULL,无需释放\n");
return;
}
//链表有东西,找这个结点
struct Node* pTemp = SelectNode(a);
if (NULL == pTemp)
{
printf("查无此结点\n");
return;
}
//找到了
//只有一个结点
if (g_pHead == g_pEnd)
{
free(g_pHead);
g_pHead = NULL;
g_pEnd = NULL;
}
//有两个结点
else if (g_pHead->pNext == g_pEnd)
{
if (g_pHead == pTemp)
{
DeleteListHead();
}
else
{
DeleteListEnd();
}
}
else //有多个结点
{
if (g_pHead == pTemp)
{
DeleteListHead();
}
else if (g_pEnd == pTemp)
{
DeleteListEnd();
}
else
{
//找删除结点的前一个结点
struct Node * pT = g_pHead;
while (pT->pNext != pTemp)
{
pT = pT->pNext;
}
//找到了
//链接
pT->pNext = pTemp->pNext;
//释放
free(pTemp);
}
}
}
posted on 2019-05-14 15:24 zhaoy_shine 阅读(290) 评论(0) 编辑 收藏 举报