(只有主要思路,不是具体代码)
第一题:翻转链表
SLTNode* newphead = NULL;
SLTNode* cur = *pphead;
SLTNode* Next = (*pphead)->next;
while (cur)
{
cur->next = newphead;
newphead = cur;
cur = Next;
if(Next)
Next = Next->next;
}
*pphead = newphead;
return *pphead;
}
第二题:求链表中间元素
//方法一:通过计算链表中元素个数/2来确定中间元素
//此时时间复杂度为f(n)
/*SLTNode* tail = phead;
int i = 0;
while (tail)
{
tail = tail->next;
i++;
}
int n = i / 2;
SLTNode* find = phead;
while (n)
{
find = find->next;
n--;
}
return find;*/
//方法二:快慢指针
SLTNode* slow, * fast;
slow = phead;
fast = phead;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
情况二:
第三题:寻找倒数第K个数
先让fast走K步,然后再slow,fast依次前进
//寻找倒数第K个数
//方法一:先求出链表中元素个数,然后求出正数第几个元素
//方法二:快慢指针
if (pos > 0)
{
SLTNode* slow, * fast;
slow = phead;
fast = phead;
for (; pos > 0; pos--)
{
if (fast == NULL)
return NULL;
fast = fast->next;
}
while (fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
else
{
return -1;
}
第四题:将两个升序链表合并成一个新的升序链表
SLTNode* tail = NULL;
SLTNode* l1 = phead1;
SLTNode* l2 = phead2;
SLTNode* phead3 = NULL;
if (l1 == NULL)
{
return l2;
}
if (l2 == NULL)
{
return l1;
}
//不带哨兵位的方法:其实就是在两个链表当中选择一个链表的头为新链表的头
/*while (l1 && l2)
{
if (l1->data < l2->data)
{
if (phead3 == NULL)
{
phead3 = tail = l1;
}
else
{
tail->next = l1;
tail=l1;
}
l1 = l1->next;
}
else
{
if (phead3 == NULL)
{
phead3 = tail = l2;
}
else
{
tail->next = l2;
tail = l2;
}
l2 = l2->next;
}
}
if (l1)
{
tail->next = l1;
}
if (l2)
{
tail->next = l2;
}
return phead3;*/
//带哨兵位的方法:其实就是开辟一个空间作为新链表的头
SLTNode* head=tail=(SLTNode*)malloc(sizeof(SLTNode));
while (l1 && l2)
{
if (l1->data < l2->data)
{
tail->next = l1;
tail = l1;
l1 = l1->next;
}
else
{
tail->next = l2;
tail = l2;
l2 = l2->next;
}
}
if(l1)
{
tail->next = l1;
}
if (l2)
{
tail->next = l2;
}
SLTNode* list = head->next;
free(head);
return list;
}
第五题:分割链表
重新开辟两个链表,一个放大于K的数,一个放小于K的数,在将两个链表链接起来。
SLTNode* cur = pphead;
SLTNode* phead1 , * tail1;
SLTNode* phead2 , * tail2;
phead1 = tail1 = (SLTNode*)malloc(sizeof(SLTNode));
phead2 = tail2 = (SLTNode*)malloc(sizeof(SLTNode));
while (cur)
{
if (cur->data < x)
{
tail1->next= cur;
tail1 = cur;
}
else
{
tail2->next = cur;
tail2 = cur;
}
cur = cur->next;
}
tail1->next = phead2->next;
tail2->next = NULL;
SLTNode* newhead = phead1->next;
free(phead1);
free(phead2);
return newhead;
第六题:比如一个链表:1->2->2->1;或者1->2->3->2->1
链表的回文
//快慢指针寻找中间的节点
SLTNode* mid = SListFindMiddle(plist);
//旋转中间节点后的节点
SLTNode* nhead = SListRevereseLisst(&mid);
SLTNode* cur1 = plist;
SLTNode* cur2 = nhead;
while (cur1 && cur2)
{
if (cur1->data == cur2->data)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
else
{
return 0;
}
}
return 1;
第七题:相交链表
先计算两个链表中节点的个数,然后让长链表先走节点个数的差数,然后两个链表同时走
因为当两个链表个数相同时方便判断,所以要想办法将两个链表变为长度相同的链表。
int lenA = 1;
int lenB = 1;
SLTNode* tailA = plist1;
SLTNode* tailB = plist2;
while (tailA->next)
{
lenA++;
tailA = tailA->next;
}
while (tailB->next)
{
lenB++;
tailB = tailB->next;
}
if (tailA != tailB)
{
return NULL;
}
//abs是取绝对值
int gap = abs(lenA - lenB);
SLTNode* longplist = plist1;
SLTNode* smalllist = plist2;
if (lenA < lenB)
{
longplist = plist2;
smalllist = plist1;
}
while (gap--)
{
longplist = longplist->next;
}
while (longplist != smalllist)
{
longplist = longplist->next;
smalllist = smalllist->next;
}
return longplist;
第八题:循环链表
判断一个链表是否为循环链表
用快慢指针来判断,本质上为追及问题
fast先进循环,slow后进循环,再循环中fast迟早追上slow
环形链表延伸问题:
1:为什么slow与fast一定会在环中相遇?会不会在环里错过永远遇不上?
结论:他们一定会相遇
解释:当slow一次走一步,fast一次走两步,当slow走到环形链表的入口时,二者开始追及,此时二者之间的距离一次减小1个
那么必定会追上
2:为什么fast一次走两步,当fast一次走n>2步行不行?
结论:不行。
解释:比如fast一次走3步:当slow走到环形链表的入口时,二者开始追及,此时二者之间的距离一次减小2个,如果当slow刚进入环形链表的入口
时,若二者之间的距离为奇数,那么在这次追及的过程中不会相遇,此时fast在前,slow在后,二者之间差1,那么此时fast又开始追及slow,此时
二者之间的距离为N(环形链表的长度)-1,若N-1还为奇数则永远追不上,若N-1为偶数,则可以追上。
SLTNode* slow = plist;
SLTNode* fast = plist;
//考虑奇数与偶数的问题
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next ;
if (slow == fast)
{
//相遇
SLTNode* meet = slow;
SLTNode* begin = plist;
//公式证明,一个从链表的头开始走,一个从相遇点开始走,两者相遇时必为入口点
//从头到入口点的距离为L,入口点到相遇点的距离为X,
//相遇时slow走的距离为L+X;fast走的距离为L+X+n*C(环形链表的距离),n>=1
//2(L+X)=L+X+n*C
//L=n*C-X -> L=(n-1)*C+C-X;
//
while (meet != begin)
{
meet = meet->next;
begin = begin->next;
}
//寻找入口点
return meet;
//判断是否是循环链表
//return 1;
}
}
return 0;
第九题:复制带随机指针的链表
//1,先复制一个节点插在两个节点中间
struct Node*cur=head;
while(cur)
{
struct Node*newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->val=cur->val;
newNode->next=cur->next;
cur->next=newNode;
cur=newNode->next;
}
cur=head;
//2,复制节点的random其实就已经等于对应节点random的next
while(cur)
{
struct Node*newhead=cur->next;
//开始向复制节点里复制random
if(cur->random==NULL)
{
newhead->random=NULL;
}
else
{
newhead->random=cur->random->next;
}
cur=newhead->next;
}
//3,开始将复制链表提出来
cur=head;
struct Node*Head=NULL,*tail=NULL;
while(cur)
{
//用来记录复制链表的下一个节点
struct Node*copy=cur->next;
//用来记录原链表下一个节点
struct Node*next=copy->next;
//连接复制链表的
if(Head==NULL)
{
Head=tail=cur->next;
}
else
{
tail->next=copy;
tail=copy;
}
cur->next=next;
cur=next;
}
return Head;