单向链表练习
valgrind:gnu 提供的内存探测工具,可以用来检测内存错误和内存泄漏。
内存泄漏:申请的空间使用完时没有及时释放,造成内存泄露。
快慢指针法:是一种通过两个指针以不同速度遍历数据结构的算法技巧,常用于解决链表、数组相关问题。快指针移动速度是慢指针的两倍(或其他固定倍数),通过这种差异实现特定功能。
//函数功能:查找链表中的中间节点(快慢指针法)
//参数://-plink:指向链表对象的指针
//返回值://-成功:返回中间节点的指针//-失败:返回NULL
Node_t *intermediateNode(Link_t *plink)
{Node_t *ptmp = plink->phead;if(ptmp == NULL){printf("error\n");return NULL;}//初始化快慢指针Node_t *pfast = ptmp;Node_t *pshow = ptmp;while(pfast != NULL){pfast = pfast->pnext; //快指针先移动一步if(NULL ==pfast) //若快指针移动一步后已经到末尾(链表长度为奇数){break; //直接退出循环,此时慢指针已经指向中间节点}else{pfast = pfast->pnext; //快指针再移动一步(共移动两步)pshow = pshow->pnext; //慢指针移动一步}}return pshow; //返回中间节点(慢指针最终位置)
}
//函数功能:查找单向链表的倒数第K个节点
//参数://-plink:指向链表对象的指针//-k:要查找的倒数位置Node_t * kNode(Link_t *plink,Data_type_t k)
{/*初始化快慢指针:均指向链表头节点*/Node_t *pfast = plink->phead;Node_t *pshow = plink->phead;int i;/*快指针先移动k个节点*/ for(i = 0;i < k;i++){if(pfast == NULL){return NULL;}pfast = pfast->pnext;}//快慢指针同步移动:直到快指针到达链表末尾(NULL)//此时慢指针与快指针仍保持k个节点的距离,慢指针即指向倒数第k个节点while(pfast!= NULL){pfast = pfast->pnext; //快指针继续后移1步pshow = pshow->pnext; //慢指针同步后移1步}return pshow; 返回慢指针(此时指向倒数第k个节点)
}
//函数功能:链表逆序
//参数://-plink:指向链表对象的指针
void reserve_link(Link_t *plink)
{Node_t *ptmp = plink->phead;plink->phead = NULL;//遍历原链表while(ptmp != NULL){//保护当前要插入的节点Node_t *pinsert = ptmp;//将ptmp指向下一个节点,以便继续处理下一个节点,防止丢失链表后续部分ptmp = ptmp->pnext;/*头插*/pinsert->pnext =plink->phead;plink->phead = pinsert;}
}
//函数功能:对单向链表进行插入排序(升序)
//参数://-plink:指向链表对象的指针
void sort_link(Link_t *plink)
{//检查链表是否无效或为空if (plink == NULL || plink->phead == NULL) {return ; //若链表为空或链表头指针为空,直接返回,不排序}//初始化相关指针Node_t *ptmp = plink->phead->pnext; //ptmp指向链表的第二个节点,用于后续操作plink->phead->pnext = NULL; //将原链表的头节点变成新链表的第一个节点,并断开后续节点//遍历原链表剩余节点并插入到合适的位置while(ptmp != NULL){Node_t *pinsert = ptmp; //保存当前要插入的节点ptmp = ptmp->pnext; //ptmp指向下一个节点,以便后续操作//判断当前节点是否插入新链表头部if(plink->phead->data >= pinsert->data){/*头插*/pinsert->pnext = plink->phead;plink->phead = pinsert;}else{Node_t *p = plink->phead;while(p->pnext != NULL && pinsert->data > p->pnext->data){p = p->pnext; //向后移动指针p,直到找到合适的位置}pinsert->pnext = p->pnext; p->pnext = pinsert;}}
}
循环链表:其最后一个节点的指针指向第一个节点,形成一个闭环。
有环链表:链表中存在至少一个节点的指针指向链表中的某个先前节点,导致链表形成环状结构。与循环链表不同,有环链表的环不一定从首尾节点开始。
循环链表是一种特殊的有环链表。
循环链表是有环链表,但有环链表不一定是有环链表。
//函数功能:判断链表是否有环
int is_loop_link(Link_t *plink)
{if(plink == NULL ||plink->phead == NULL){return 0;}Node_t *pfast = plink->phead;Node_t *pshow = plink->phead;while(pfast != NULL && pfast->pnext != NULL){pfast = pfast->pnext->pnext;pshow = pshow->pnext;if(pfast == pshow){return 1;}}return 0;
}
Node_t *Joseph_loop(Link_t *plink)
{if (is_empty_link(plink)){return NULL;}Node_t *ppre = plink->phead;Node_t *pdel = ppre;while (plink->clen > 1){ppre = ppre->pnext;pdel = pdel->pnext->pnext;ppre->pnext = pdel->pnext;free(pdel);plink->clen--;ppre = ppre->pnext;pdel = ppre;}return ppre;
}