单向链表
链表的查找
在链表中找到指定的第一个元素
沿用遍历思想,每次访问一个节点元素判断是否是要找的节点
符合条件,返回该节点地址
到最后依旧没有找到符合条件的节点,返回NULL;
cs
//返回第一个指定元素节点的地址
linknode *find_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while(ptmpnode != NULL)
{
if(ptmpnode->data == tmpdata)
{
return ptmpnode;
}
ptmpnode = ptmpnode->pnext;
}
return NULL;
}
cs
//main.c中调用
pret = find_linklist(plinklist,3);//找出链表中第一个3数据的地址并返回
printf("&plinklist = %p\n", pret);
printf("%d\n", pret->data);
链表的修改
使用遍历,找到符合条件的元素修改为新的值
cs
//更新链表中指定元素的值
int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while(ptmpnode != NULL)
{
if(ptmpnode->data == olddata)
{
ptmpnode->data = newdata;
}
ptmpnode = ptmpnode->pnext;//没有找到olddata,就继续下一个节点
}
return 0;
}
cs
//main.c中调用
update_linklist(plinklist, 4, 5);
show_linklist(plinklist);
链表的尾插法

在链表末尾插入一个元素
1、申请一个节点空间
2、存数据
3、存地址,赋值为NULL;
4、遍历找到最后一个节点
5、最后一个节点的pnext赋值为新申请的节点
cs
//尾插
int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
linknode *pnewnode = NULL;
ptmpnode = phead;//指向头节点
//或者ptmpnode = phead->pnext;指向第一个有效节点
//但是如果只有头节点情况下不能使用
//申请空间
pnewnode = malloc(sizeof(linknode));//分配新节点
if(NULL == pnewnode)
{
perror("fail to malloc");
return -1;
}
pnewnode->data = tmpdata;//数据存放到节点中
pnewnode->pnext = NULL;//将节点地址赋值为NULL
while(ptmpnode->pnext != NULL)//找到最后一个节点
{
ptmpnode = ptmpnode->pnext;
}
ptmpnode->pnext = pnewnode;//将最后一个节点的pnext指向新申请的节点
return 0;
}
cs
//main.c中调用
insert_tail_linklist(plinklist, 6);
show_linklist(plinklist);
链表的销毁
将所有链表节点空间都释放掉,使用二级指针

1、定义两个指针pfreenode和ptmpnode都指向头节点
2、使ptmpnode向后走
3、再释放pfreenode指向的节点
4、再将pfreenode指向ptmpnode指向的空间
cs
//销毁,使用二级指针
int destory_linklist(linknode **pphead)
{
linknode *pfreenode = NULL;
linknode *ptmpnode = NULL;
ptmpnode = *pphead;
pfreenode = *pphead;
while(ptmpnode != NULL)
{
ptmpnode = ptmpnode->pnext;
free(pfreenode);
pfreenode = ptmpnode;
}
*pphead = NULL;
return 0;
}
cs
//main.c中调用
destory_linklist(&plinklist);
printf("plinklist = %p\n", plinklist);
show_linklist(plinklist);
查找链表中间节点

定义两个指针,一个快指针,一个慢指针
1、快指针每次走 2步,慢指针每次都1步
2、快指针走到末尾,慢指针走到中间
cs
//找中间节点
linknode *fine_midnode(linknode *phead)
{
linknode *pfast = NULL;
linknode *pslow = NULL;
pfast = phead->pnext;
pslow = phead->pnext;
while(pfast != NULL)
{
pfast = pfast->pnext;//pfast走一步
if(NULL == pfast)
{
break;
}
pfast = pfast->pnext;//pfast走第二步
if(NULL == pfast)
{
break;
}
pslow = pslow->pnext;//pslow走一步
}
return pslow;
}
cs
//main.c中调用
pret = fine_midnode(plinklist);
show_linklist(plinklist);
printf("midnode = %d\n", pret->data);
查找链表倒数第k个节点
1、快指针先走k步
2、慢指针和快指针每次走一步
3、快指针走到末尾,慢指针少走k步,即倒数第k个元素
cs
//找倒数第k个节点
linknode *find_last_kth_node(linknode *phead, int k)
{
int i = 0;
linknode *pfast = NULL;
linknode *pslow = NULL;
pfast = phead->pnext;
for(i = 0; i < k && pfast != NULL; i++)
{
pfast = pfast->pnext;
}
if(NULL == pfast)
{
return NULL;
}
pslow = phead->pnext;
while(pfast != NULL)
{
pfast = pfast->pnext;
pslow = pslow->pnext;
}
return pslow;
}
cs
//main.c中调用
pret = find_last_kth_node(plinklist, 3);
show_linklist(plinklist);
printf("last_kth_node = %d\n", pret->data);
不知道头节点,删除指定节点
将指针指向的下一个节点的值覆盖当前节点的值
再去删除下一个节点
下一个节点的值和地址都赋给当前节点ptmpnode,删除下一个节点
cs
/* 删除指定节点 */
int delete_linknode(linknode *ptmpnode)
{
linknode *pnextnode = NULL;
pnextnode = ptmpnode->pnext;
ptmpnode->data = pnextnode->data;
ptmpnode->pnext = pnextnode->pnext;
free(pnextnode);
return 0;
}
倒置

先将原链表断开
将所有元素依次使用头插法插入
cs
//链表倒置
int reverse_linklist(linknode *phead)
{
linknode *pinsertnode = NULL;
linknode *ptmpnode = NULL;
//将链表从头节点处断开
ptmpnode = phead->pnext; //ptm指针指向第一个有效节点
phead->pnext = NULL; //头结点的pnext指向NULL,断开 头节点
//依次将所有元素使用头插法插入链表中
while(ptmpnode != NULL)
{
pinsertnode = ptmpnode; //使插入指针pin走到指针ptm的位置
ptmpnode = ptmpnode->pnext; //ptmp指针向后走
pinsertnode->pnext = phead->pnext; //插入节点指向第一个有效节点,存入下一个节点地址
phead->pnext = pinsertnode; //头结点指向插入节点
}
return 0;
}
链表冒泡:
定义三个指针,ptmpnode1和ptmpnode2,还有pend(初始为NULL)
两个指针相邻两个元素比较
指针循环向后走,知道ptmp2为NULL,即等于pend,循环停止
每次循环,pend赋值为ptmp1的节点地址
循环将所有元素都比完,剩余一个小的元素

cs
//冒泡排序
int bubble_sort_linklist(linknode *phead)
{
linknode *ptmpnode1 = NULL;
linknode *ptmpnode2 = NULL;
linknode *pend = NULL;
datatype tmpdata;
//少于两个节点直接结束
if(NULL == phead->pnext || NULL == phead->pnext->pnext)
{
return 0;
}
while(1)
{
ptmpnode1 = phead->pnext; //ptmp1指向第一个有效节点
ptmpnode2 = phead->pnext->pnext;//ptmp2指向第一个有效节点的pnext,也就是第二个有效节点
//每一轮开始前判断是否已经排序完成
if(pend == ptmpnode2)
{
break;
}
while(ptmpnode2 != pend) //ptm2不等于pend时,进入循环
{
if(ptmpnode1->data > ptmpnode2->data) //指针1的元素是否比指针2的大
{
tmpdata = ptmpnode1->data; //如果1的值大于2的值,则交换
ptmpnode1->data = ptmpnode2->data;
ptmpnode2->data = tmpdata;
}
ptmpnode1 = ptmpnode1->pnext; //如果不满足交换的条件,指针1向后走
ptmpnode2 = ptmpnode2->pnext; //指针2向后走
}
pend = ptmpnode1; //让pend指针走到指针1的位置上,开始新一轮循环
}
return 0;
}
选择排序

pswapnode指向要交换的节点
pminnode指向假设的最小值
ptmpnode和后续节点比较
cs
//选择排序
int select_sort_linklist(linknode *phead)
{
linknode *ptmpnode = NULL;
linknode *pminnode = NULL;
linknode *pswapnode = NULL;
datatype tmpdata;
//少于两个节点直接结束
if(NULL == phead->pnext || NULL == phead->pnext->pnext)
{
return 0;
}
pswapnode = phead->pnext; //交换指针指向第一个有效节点
while(pswapnode->pnext !=NULL) //遍历到最后一个节点结束
{
pminnode = pswapnode; //最小值指针指向交换指针的节点
ptmpnode = pswapnode->pnext;//ptmp指针指向交换指针的下一个节点
while(ptmpnode != NULL) //当ptmp指针不等于NULL,进入循环
{
if(ptmpnode->data < pminnode->data) //判断ptmp的元素是否 < 最小值的元素
{
pminnode = ptmpnode; //满足交换条件,让最小值的指针指向ptmp(当前的最小值)
}
ptmpnode = ptmpnode->pnext; //指针ptmp向后走
}
if(pswapnode != pminnode) //判断交换指针是否与最小值指针元素不相等
{
tmpdata = pswapnode->data; //不相等就交换,交换指针的data和最小值指针的data相互交换
pswapnode->data = pminnode->data;
pminnode->data = tmpdata;
}
pswapnode = pswapnode->pnext; //交换指针向后走
}
return 0;
}
判断有环

判断链表是否有环:
定义两个指针:快指针(每次走2步)和慢指针(每次走一步)
当快指针 - 慢指针 == 环长时,即相遇,快指针和慢指针相等即为链表有环
计算环长:
定义一个指针从相遇点开始走一圈,直到走到该节点为止
每走一个节点计数,最终可得到环长
获得环的入口位置:
a = c + (n-1) * l; n为圈数
定义一个指针从相遇点开始每次走一步,定义一个指针从开头每次走一步
两个指针相遇的位置即为环入口的位置
cs
//判断链表是否有环 计算环长 找到环的入口位置
int circle_linklist(linknode *phead, int *pis_circle, int *pcirlen, linknode **ppnode)
{
linknode *pfast = NULL;
linknode *pslow = NULL;
linknode *ptmpnode = NULL;
linknode *pstartnode = NULL;
int count = 1;
//判断是否有环
pfast = phead->pnext;
pslow = phead->pnext;
while(1)
{
pfast = pfast->pnext;
if(NULL == pfast)
{
break;
}
pfast = pfast->pnext;
if(NULL == pfast)
{
break;
}
pslow = pslow->pnext;
if(pfast == pslow)
{
break;
}
}
if(NULL == pfast)
{
*pis_circle = 0;
return 0;
}
else
{
*pis_circle = 1;
}
//统计环长
ptmpnode = pslow->pnext;
while(ptmpnode != pslow)
{
count++;
ptmpnode = ptmpnode->pnext;
}
*pcirlen = count;
//找到环入口
pstartnode = phead->pnext;
ptmpnode = pslow;
while(pstartnode != ptmpnode)
{
pstartnode = pstartnode->pnext;
ptmpnode = ptmpnode->pnext;
}
*ppnode = ptmpnode;
return 0;
}
双向链表
节点定义

cs
typedef int datatype;//存放数据的类型
typedef struct node{
datatype data; //存放数据
struct node *ppre; //指向前一个节点的指针
struct node *pnext; //指向后一个节点的指针
}linknode;
创建空白节点
1、申请空白空间
2、对pnext和ppre赋值为NULL
3、返回头节点地址
cs
//创建一个空链表
linknode *create_empty_linklist(void)
{
linknode *ptmpnode = NULL;
ptmpnode = malloc(sizeof(linknode));//分配一个节点空间当做头结点(不存数据)
if(NULL == ptmpnode)
{
perror("fail to malloc");
return NULL;
}
ptmpnode->pnext = NULL;//把它的 pnext 指针设为 NULL,代表链表为空。
ptmpnode->ppre = NULL;
return ptmpnode;
}
头插法

1、申请节点
2、存数据
3、申请的pnext赋值为phead->pnext ptmpnode->pnext = phead->pnext
4、申请的ppre赋值为phead的地址 ptmpnode->ppre = phead
5、将phead->pnext赋值为新申请节点地址 phead->pnext = ptmpnode
6、如果有后一个节点,需要让后一个节点的ppre指向该节点ptmpnode->pnext->ppre = ptmpnode
cs
//头插法
int insert_head_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if(NULL == ptmpnode)
{
perror("fail to malloc");
return -1;
}
ptmpnode->data = tmpdata; //存数据
ptmpnode->pnext = phead->pnext; //头节点的pnext赋值给当前节点
ptmpnode->ppre = phead; //ppre指向头节点
phead->pnext = ptmpnode; //头结点的pnext指向ptmp节点
if(ptmpnode->pnext != NULL) //判断是否有下一个节点
{
ptmpnode->pnext->ppre = ptmpnode;
}
return 0;
}
遍历(和单向一样)
cs
//遍历(和单向一样)
void show_linklist(linknode *phead)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext; //指向第一个有效节点
while(ptmpnode != NULL) //遍历每个节点元素
{
printf("%d ", ptmpnode->data);
ptmpnode = ptmpnode->pnext;
}
printf("\n");
return;
}
查找(和单向一样)
cs
//查找(和单向一样)
linknode *find_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext; //指向第一个有效节点
while(ptmpnode != NULL) //遍历
{
if(ptmpnode->data == tmpdata) //判断是否是要找的tmpdata
{
return ptmpnode; //找到了,返回当前节点指针
}
ptmpnode = ptmpnode->pnext;
}
return NULL; //循环结束依旧找不到,返回NULL
}
修改(和单向一样)
cs
//修改(和单向一样)
int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext; //指向第一个有效节点
while(ptmpnode != NULL)
{
if(ptmpnode->data == olddata) //判断是否是要修改的olddata
{
ptmpnode->data = newdata;
}
ptmpnode = ptmpnode->pnext; //指针向后走
}
return 0;
}
删除指定元素

找到要删除的节点
如果前一个节点存在,则让前一个节点的pnext赋值为 要删除节点的 后一个节点
如果后一个节点存在,则让后一个节点的ppre赋值为 要删除节点的 前一个节点
让pfree指针指向要删除的节点
ptmp指针向后走
释放pfree指向的节点空间
cs
//删除指定元素
int delete_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
linknode *pfreenode = NULL;
ptmpnode = phead->pnext;
while(ptmpnode != NULL)
{
if(ptmpnode->data == tmpdata) //判断是否找到指定元素
{
//如果前一个节点存在,则修改他的pnext
if(ptmpnode->ppre != NULL)
{
ptmpnode->ppre->pnext = ptmpnode->pnext;//前一个节点的pnext赋值为后一个节点
}
//如果后一个节点存在,则修改它的ppre
if(ptmpnode->pnext != NULL)
{
ptmpnode->pnext->ppre = ptmpnode->ppre;//后一个节点的ppre赋值为前一个节点
}
pfreenode = ptmpnode; //释放指针赋值为ptmp指针
ptmpnode = ptmpnode->pnext; //ptmp指针向后走
free(pfreenode); //释放pfree指针指向的节点空间
return 0;//删除成功
}
else
{
ptmpnode = ptmpnode->pnext; //没有找到指定元素,ptmp指针向后走
}
}
return -1;//未找到指定数据
}
销毁所有元素(和单向一样)
cs
//销毁(和单向一样)
int destory_linklist(linknode **pphead)
{
linknode *ptmpnode = NULL;
linknode *pfreenode = NULL;
ptmpnode = *pphead;
pfreenode = ptmpnode;
while(ptmpnode != NULL)
{
ptmpnode = ptmpnode->pnext;
free(pfreenode);
pfreenode = ptmpnode;
}
*pphead = NULL;
return 0;
}
尾插法
1、申请节点
2、将新申请节点的pnext赋值为NULL
3、找到链表的最后一个节点
4、将新申请节点的ppre赋值为最后一个节点地址
5、将最后一个节点的pnext赋值为新申请节点
cs
//尾插法
int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
linknode *plastnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if(NULL == ptmpnode)
{
perror("fail to malloc");
return -1;
}
plastnode = phead; //指针plast先指向头节点
while(plastnode->pnext != NULL) //找到最后一个节点
{
plastnode = plastnode->pnext;
}
ptmpnode->data = tmpdata; //新申请的节点存数据
ptmpnode->pnext = NULL; //新申请节点的pnext指向NULL
ptmpnode->ppre = plastnode; //新申请节点的ppre指向最后一个节点
plastnode->pnext = ptmpnode; //最后一个节点的pnext指向新申请节点
return 0;
}