目录
1.逆置单链表
思路:已知单链表后,根据头插法的方式,将头结点后的第一个节点的next置为空,然后将后面结点一次挂在它的前面
如图所示:
代码实现:
void Reverse2(List plist)//头插法逆置 { assert(plist != NULL); if(IsEmpty(plist)) { return ; } Node *cur = plist->next; plist->next = NULL; while(cur != NULL) { Node *curNext = cur->next; cur->next = plist->next; plist->next = cur; cur = curNext; } }
2.反转单链表
思路:依次将每个节点取出,从后往前排列.设置一个end中间节点,开始时将它的next置为NULL,然后将它的地址赋给它的前一个节点的next.
注:将它理解为取出第一个节点(即头结点,将它的next置为NULL,作为新链表最后的节点,然后取原链表下一个节点,将它的next附为新链表最后一个节点的地址)
如图所示:
代码实现:
Node *Fun(List plist)// 单链表反转 { assert(plist != NULL); if(IsEmpty(plist)) { return false; } Node *cur = plist; Node *end = NULL; while(cur != NULL) { Node *tmp = cur; cur = cur->next; tmp->next = end; end = tmp; } return end; }
问:那么单链表的逆置和反转有什么区别呢?
3.合并两个有序的单链表思路:比较两个链表值的大小,将小的排在前面;由于最开始比较的是头结点的值,所以最后显示时要多往后走一步.
注意:要注意链表是否为空的情况,当第一个链表为空链表,也就是它的头结点是一个空指针,那么把它和第二个链表合并,显然合并的结果就是第二个链表;同理,第二个链表的头结点是空指针的时候,把它和第一个链表合并,结果就是第一个链表.如果两个链表都是空链表,则合并结果是得到一个空链表.
代码实现:
Node *Merge(List plist1,List plist2) { if(plist1 == NULL) { return plist2; } else if(plist2 == NULL) { return plist1; } Node *pMergeHead = NULL; if(plist1->data < plist2->data) { pMergeHead = plist1; pMergeHead->next = Merge(plist1->next,plist2); } else { pMergeHead = plist2; pMergeHead->next = Merge(plist1,plist2->next); } return pMergeHead; } void Show1(List plist) { assert(plist != NULL); if(IsEmpty(plist)) { return; } Node *cur = plist->next->next; while(cur->next != NULL) { printf("%d ",cur->data); cur = cur->next; } printf("%d \n",cur->data); } int main() { Node sq; InitList(&sq); for(int i = 0;i < 9;i++) { Insert_tail(&sq,i+3); } Show(&sq); printf("============\n"); //合并两个有序单链表 Node sq2; InitList(&sq2); for(int i = 0;i < 4;i++) { Insert_tail(&sq2,i+10); } Show(&sq2); printf("===============\n"); Node *sq3 = NULL; sq3 = Merge(&sq,&sq2); Show1(sq3); return 0; }
4.判断单链表是否有环?环的入口点?环的长度?
在网上看到一哥们儿写的解释特别好,所以将他的讲解和图借来用用,代码是我自己写的,特此声明.
判断是否有环
如果链表有环,那么在遍历链表时则会陷入死循环,利用这个特征,我们可以设计这样的算法。
如果fast指针在遍历过程中,遍历到了NULL节点说明链表没有环。
否则当slow指针和falst指针相同,则说明环有节点。
- 使用一个slow指针,一个fast指针。
- slow指针一次往后遍历以1个节点,fast指针一次往后遍历2个节点,一直做这样的操作。
环的入口节点
假定链表头到环入口的距离是len,环入口到slow和fast交汇点的距离为x,环的长度为R。slow和fast第一次交汇时,设slow走的长度为:d = len + x,而fast走的长度为:2d = len + nR + x,(n >= 1),从而我们可以得知:2len + 2x = len + nR + x,即len = nR - x,(n >= 1),于是我们可以得到这样的算法。
使用一个cur指针指向链表头节点,一个inter指针指向第一次的交汇点。
cur指针和inter指针一起往后遍历.
cur指针和inter指针相等时,cur和inter指针指向的就是环的入口节点。
代码实现:
void CreatLoop(List plist) { assert(plist != NULL); Node *cur = plist; Node *cur1 = plist; while(cur->next != NULL) { cur = cur->next; } cur->next = cur1->next->next->next->next; } Node *Fun2(List plist,int *count)//判断单链表是否有环?环的入口点?环的长度? { assert(plist != NULL &&count != NULL); if(IsEmpty(plist)) { return NULL; } Node *pb = plist->next; Node *pe = plist->next; Node *cur = plist->next; Node *inter = plist->next; while(pb->next != NULL && pe->next != NULL ) { pb = pb->next;//慢指针走一步 pe = pe->next->next; //快指针走两步 if(pb == pe) { inter = pb;//len = nR-x; len :第一个节点到入口点的距离 R:环长 x:入口点到交互点的距离 printf("it have loop\n"); break; } } while(cur != inter) { cur = cur->next; inter = inter->next; } int foot = 1; cur = cur->next; while(inter != cur) { foot++; cur = cur->next; } *count = foot; return inter; }
5.判断两个单链表是否相交?交点?
问:在解释之前,先提出一个问题,两个单链表相交,他是什么形状的?是X型?还是Y型?
答:那当然是Y型了,不然你以为呢.
思路:要想知道两个单链表是否相交,最简单的方法就是看他们最后一个节点的地址是否相同.因为相交之后的节点都是一样的.那麻烦的是,如何去找交点.大致思路就是,假设两个单链表的长短不一,然后去计算他们相差的节点数X,然后让长的链表往后走X步,此时两个单链表到最后的长度是一样的,这样就可以边走,边进行判断,看他们是否一样.
如图所示:
代码实现:
void CreateCut(List plist,List plist1)//节点相交 { plist->next->next = plist1->next->next->next; } bool IsCut(List plist1,List plist2)//判断是否相交,Y字形 { assert(plist1 != NULL && plist2 != NULL); if(IsEmpty(plist1) || IsEmpty(plist2)) { return false; } int len1 = GetLength(plist1); int len2 = GetLength(plist2); Node *plong = plist1; Node *pshort = plist2; int len = len1-len2; if(len < 0) { plong = plist2; pshort = plist1; len = len2-len1; } //plong肯定指向长的 pshort //len > 0 for(int i = 0;i < len;i++) { plong = plong->next; } while(plong != NULL && pshort != NULL && plong != pshort) { plong = plong->next; pshort = pshort->next; } if(plong == pshort && plong != NULL) { return true; } return false; } Node* IsCutNode(List plist1,List plist2) { int len1 = GetLength(plist1); int len2 = GetLength(plist2); Node *plong = plist1; Node *pshort = plist2; int len = len1-len2; if(len < 0) { plong = plist2; pshort = plist1; len = len2-len1; } //plong肯定指向长的 pshort //len > 0 for(int i = 0;i < len;i++) { plong = plong->next; } while(plong != NULL && pshort != NULL && plong != pshort) { plong = plong->next; pshort = pshort->next; } if(plong == pshort && plong != NULL) { return plong; } return NULL; }
6.O(1)时间删除单链表的一个节点
思路:如果是循环删除,那时间复杂度为O(n),而这道题是让在O(1)的时间内完成,显然说的是平均时间.为什么呢?因为如果是要删除最后一个节点,就必须知道他的前一个节点,不然怎么删除.这就意味着,此时需要遍历一遍单链表.如果是非最后一个节点要被删除,直接用后一个节点的数据覆盖它即可.所以这道题说的是平均时间复杂度O(1).
代码实现:
void DeleteNode(List plist,Node *pDel)//O(1)时间删除单链表的一个节点 pDel==>要删除的节点 { assert(plist != NULL && pDel != NULL); if(IsEmpty(plist)) { return ; } Node *cur1 = plist; if(pDel->next != NULL) { Node *pDelNext = pDel->next; pDel->data = pDelNext->data; pDel->next = pDelNext->next; free(pDelNext); } else { Node *pCur = plist; while(pCur->next != pDel) { pCur = pCur->next; } pCur->next = NULL; free(pDel); } }
7.最快时间内找到单链表倒数第K个节点
思路:用最快时间找到第k个节点,显然使用循环遍历到所找节点这样的方法不是最快的.那该如何解答?应该设置两个指针,让他们相差k-1步,当靠后的指针走到链尾,前面的指针正好在所找节点上
如图所示:
代码实现:
Node *LastK(List plist,int k)//最快时间内找到单链表倒数第K个节点 { assert(plist != NULL); if(k < 0 || k > GetLength(plist) || IsEmpty(plist)) { return NULL; } Node *cur1 = plist; Node *cur2 = plist; while(k-1 > 0) { if(cur1->next != NULL) { cur1 = cur1->next; --k; } else { return NULL; } } while(cur1->next != NULL) { cur1 = cur1->next; cur2 = cur2->next; } return cur2; }
8.最快时间内删除单链表倒数第K个节点
思路:和上一个算法的思想一样,只是多加了一步找到后删除
代码实现:
void DeleteLastK(List plist,int k)//最快时间内删除单链表倒数第K个节点 { assert(plist != NULL); if(k < 0 || k > GetLength(plist) || IsEmpty(plist)) { return ; } Node *cur1 = plist; Node *cur2 = plist; while(k > 0) { if(cur1->next != NULL) { cur1 = cur1->next; --k; } else { return; } } while(cur1->next != NULL) { cur1 = cur1->next; cur2 = cur2->next; } Node *tmp = cur2->next; cur2->next = tmp->next ; free(tmp); tmp = NULL; }
以下是完整代码,仅供参考
//linklist.cpp
#include"linklist.h"
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
void InitList(List plist)//初始化单链表
{
assert(plist != NULL);
plist->next = NULL;
}
static Node *GetNode(int val)
{
Node* pGet = (Node*)malloc(sizeof(Node));
assert(pGet != NULL);
pGet->data = val;
pGet->next = NULL;
return pGet;
}
bool Insert_head(List plist,int val)//头插法
{
assert(plist != NULL);
Node *pGet = GetNode(val);
pGet->next = plist->next;
plist->next = pGet;
return true;
}
bool Insert_tail(List plist,int val)//尾插法
{
assert(plist != NULL);
Node *cur = plist;//指向头结点处 依赖前驱信息的
while(cur->next != NULL)
{
cur = cur->next;
}
Node *pGet = GetNode(val);
cur->next = pGet;
return true;
}
bool Insert_pos(List plist,int pos,int val)//pos 位置插入
{
assert(plist != NULL);
if(pos < 0 || pos > GetLength(plist))
{
return false;
}
Node *cur = plist;
for(int i = 0;i <= pos-1;i++)
{
cur = cur->next;
}
Node *pGet = GetNode(val);
pGet->next = cur->next;
cur->next = pGet;
return true;
}
Node *Search_pre(List plist,int key)//查找 key 的前驱
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return NULL;
}
Node *cur = plist;
while(cur->next != NULL)
{
if(cur->next->data != key)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
bool Delete(List plist,int key)//删除 key 这个结点
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return false;
}
Node *cur = plist;
while(cur->next != NULL)
{
if(cur->next->data == key)
{
Node *p = cur->next;
cur->next = p->next;
free(p);
p = NULL;
return true;
}
cur = cur->next;
}
return false;
}
bool IsEmpty(List plist)//是否为空
{
if(plist == NULL || plist->next == NULL)
{
return true;
}
return false;
}
void Destroy(List plist)//摧毁函数(如果有动态开辟内存)
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return;
}
Node *cur = plist;
while(cur->next != NULL)
{
Node *p = cur->next;
cur->next = p->next;
free(p);
p = NULL;
}
free(cur->next);
plist->next = NULL;
return ;
}
int GetLength(List plist)//得到单链表的长度
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return 0;
}
int count = 0;
Node *pCur = plist->next;
while(pCur != NULL)
{
count++;
pCur = pCur->next;
}
return count;
}
void Show(List plist)//打印单链表
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return;
}
Node *cur = plist;
while(cur->next != NULL)
{
printf("%d %d\n",cur->data,cur->next);
cur = cur->next;
}
printf("%d %d\n",cur->data,cur->next);
}
Node *Fun(List plist)//带头结点逆置 == 反转
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return false;
}
Node *cur = plist;
Node *end = NULL;
while(cur != NULL)
{
Node *tmp = cur;
cur = cur->next;
tmp->next = end;
end = tmp;
}
return end;
}
bool Fun1(List plist,List plist1)//合并两个有序单链表
{
assert(plist != NULL && plist1 != NULL);
if(IsEmpty(plist) || IsEmpty(plist1))
{
return false;
}
Node *cur1 = plist;
Node *cur2 = plist1->next;
while(cur1->next != NULL)
{
cur1 = cur1->next;
}
while(cur2 != NULL)
{
cur1->next = cur2;
cur1 = cur1->next;
cur2 = cur2->next;
}
cur1->next = cur2;
return true;
}
void CreatLoop(List plist)
{
assert(plist != NULL);
Node *cur = plist;
Node *cur1 = plist;
while(cur->next != NULL)
{
cur = cur->next;
}
cur->next = cur1->next->next->next->next;
}
Node *Fun2(List plist,int *count)//判断单链表是否有环?环的入口点?环的长度?
{
assert(plist != NULL &&count != NULL);
if(IsEmpty(plist))
{
return NULL;
}
Node *pb = plist->next;
Node *pe = plist->next;
Node *cur = plist->next;
Node *inter = plist->next;
while(pb->next != NULL && pe->next != NULL )
{
pb = pb->next;//慢指针走一步
pe = pe->next->next; //快指针走两步
if(pb == pe)
{
inter = pb;//len = nR-x; len :第一个节点到入口点的距离 R:环长 x:入口点到交互点的距离
printf("it have loop\n");
break;
}
}
while(cur != inter)
{
cur = cur->next;
inter = inter->next;
}
int foot = 1;
cur = cur->next;
while(inter != cur)
{
foot++;
cur = cur->next;
}
*count = foot;
return inter;
}
void CreateCut(List plist,List plist1)//节点相交
{
plist->next->next = plist1->next->next->next;
}
bool IsCut(List plist1,List plist2)//判断是否相交,Y字形
{
assert(plist1 != NULL && plist2 != NULL);
if(IsEmpty(plist1) || IsEmpty(plist2))
{
return false;
}
int len1 = GetLength(plist1);
int len2 = GetLength(plist2);
Node *plong = plist1;
Node *pshort = plist2;
int len = len1-len2;
if(len < 0)
{
plong = plist2;
pshort = plist1;
len = len2-len1;
}
//plong肯定指向长的 pshort
//len > 0
for(int i = 0;i < len;i++)
{
plong = plong->next;
}
while(plong != NULL && pshort != NULL && plong != pshort)
{
plong = plong->next;
pshort = pshort->next;
}
if(plong == pshort && plong != NULL)
{
return true;
}
return false;
}
Node *Reverse1(List plist)//单链表反转
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return NULL;
}
Node *cur = plist;
Node *prev = NULL;
Node *reverHead = NULL;
while(cur != NULL)
{
Node *curNext = cur->next;
if(curNext == NULL)
{
reverHead = cur;
}
cur->next = prev;//NULL
prev = cur;
cur = curNext;
}
return reverHead;
}
void Show2(Node *reverHead)
{
Node *p = reverHead;
while(p->next != NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
void Reverse2(List plist)//头插法逆置
{
assert(plist != NULL);
if(IsEmpty(plist))
{
return ;
}
Node *cur = plist->next;
plist->next = NULL;
while(cur != NULL)
{
Node *curNext = cur->next;
cur->next = plist->next;
plist->next = cur;
cur = curNext;
}
}
/*
合并两个有序的单链表
判断单链表是否有环?环的入口点?环的长度?
判断两个单链表是否相交?交点?
*/
Node *LastK(List plist,int k)//最快时间内找到单链表倒数第K个节点
{
assert(plist != NULL);
if(k < 0 || k > GetLength(plist) || IsEmpty(plist))
{
return NULL;
}
Node *cur1 = plist;
Node *cur2 = plist;
while(k-1 > 0)
{
if(cur1->next != NULL)
{
cur1 = cur1->next;
--k;
}
else
{
return NULL;
}
}
while(cur1->next != NULL)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur2;
}
void DeleteLastK(List plist,int k)//最快时间内删除单链表倒数第K个节点
{
assert(plist != NULL);
if(k < 0 || k > GetLength(plist) || IsEmpty(plist))
{
return ;
}
Node *cur1 = plist;
Node *cur2 = plist;
while(k > 0)
{
if(cur1->next != NULL)
{
cur1 = cur1->next;
--k;
}
else
{
return;
}
}
while(cur1->next != NULL)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
Node *tmp = cur2->next;
cur2->next = tmp->next ;
free(tmp);
tmp = NULL;
}
void DeleteNode(List plist,Node *pDel)//O(1)时间删除单链表的一个节点 pDel==>要删除的节点
{
assert(plist != NULL && pDel != NULL);
if(IsEmpty(plist))
{
return ;
}
Node *cur1 = plist;
if(pDel->next != NULL)
{
Node *pDelNext = pDel->next;
pDel->data = pDelNext->data;
pDel->next = pDelNext->next;
free(pDelNext);
}
else
{
Node *pCur = plist;
while(pCur->next != pDel)
{
pCur = pCur->next;
}
pCur->next = NULL;
free(pDel);
}
}
//linklist.h
#pragma once//保证头文件只被编译一次
typedef struct Node
{
int data;
struct Node *next;
}Node,*List;
void InitList(List plist);//初始化单链表
bool Insert_head(List plist,int val);//头插法
bool Insert_tail(List plist,int val);//尾插法
bool Insert_pos(List plist,int pos,int val);//pos 位置插入
Node *Search_pre(List plist,int key);//查找 key 的前驱
bool Delete(List plist,int key);//删除 key 这个结点
bool IsEmpty(List plist);//是否为空
void Destroy(List plist);//摧毁函数(如果有动态开辟内存)
int GetLength(List plist);//得到单链表的长度
void Show(List plist);//打印单链表
Node *Fun(List plist);//带头结点逆置
bool Fun1(List plist,List plist1);//合并两个有序单链表
void CreatLoop(List plist);
Node *Fun2(List plist,int *count);//判断单链表是否有环?环的入口点?环的长度?
Node *Reverse1(List plist);//单链表反转
void Show2(Node *reverHead);//反转之后的打印
void Reverse2(List plist);//头插法逆置
Node *LastK(List plist,int k);//最快时间内找到单链表倒数第K个节点
void DeleteNode(List plist,Node *del);//O(1)时间删除单链表的一个节点 del==>要删除的节点
bool IsCut(List plist1,List plist2);//判断是否相交,Y字形
//源.cpp
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include"linklist.h"
//#include<vld.h>//测内存泄漏
int main()
{
Node sq;
InitList(&sq);
for(int i = 0;i < 9;i++)
{
//Insert_head(&sq,i+3);//头插法逆置
Insert_tail(&sq,i+3);
}
Show(&sq);
printf("============\n");
int count = 0;
Node *inter = NULL;
CreatLoop(&sq);
inter = Fun2(&sq,&count);
printf("环入口为:%d %d\n",inter->data,inter->next);
printf("环长为:%d\n",count);
/*Node head;//头结点
InitList(&head);
for(int i = 0; i < 10;i++)
{
Insert_head(&head,i);
}
Show(&head);
printf("=======\n");
Node head2;//头结点
InitList(&head2);
for(int i = 10; i < 30;i++)
{
Insert_head(&head2,i);
}
Show(&head2);
printf("=======\n");
CreateCut(&head,&head2); 两个链表相交
if(IsCut(&head,&head2))
{
Node *p = IsCutNode(&head,&head2);
printf("is cut ! %d\n",p->data);
}
else
{
printf("is not cut!\n");
}*/
//Reverse2(&sq);//头插法逆置
//Show(&sq);
/*int len = GetLength(&sq);
printf("%d\n",len);*/
/*Insert_pos(&sq,2,99);
Show(&sq);
Delete(&sq,99);
Show(&sq);*/
//带头结点的逆置 == 反转
/*Node *sq1= Fun(&sq);
Show(sq1);*/
//合并两个有序单链表
/*Node sq2;
InitList(&sq2);
for(int i = 0;i < 4;i++)
{
Insert_tail(&sq2,i+10);
}
Show(&sq2);
printf("===============\n");
Fun1(&sq,&sq2);
Show(&sq);*/
/*Destroy(&sq);
printf("===============\n");
Show(&sq);*/
return 0;
}