1 打印俩个有序链表公共部分
思路
这道题很简单,核心思路就是,如果当L1的值小于L2当前的值,L1一直往后走,直到找到大于等于L2的值,等于输出,大于的话,L2就开始遍历做跟L1一样的事情。
代码
#include<iostream>
using namespace std;
struct Node
{
int _data;
Node * next;
}
void Print(Node * head1 , Node * head2)
{
assert(head1&&head2);
while(head!=NULL&&head2!=NULL)
{
int temp = head2->_data;
while(head&&head2&&head->_data<temp)
head = head->next;
if(head2&&head&&temp==head->_data)
{
cout<<temp<<" ";
head = head->next;
}
if(head)
temp=head->_data;
while(head&&head2&&head2->_data<temp)
head2 = head2->next;
if(head&&head2&&temp==head2->_data)
{
cout<<temp<<" ";
head2 = head2->next;
}
}
}
实现方法2
void printf2(Node * head1 , Node *head2)
{
assert(head1&&head2);
while(head1&&head2)
{
if(head1->_data<head2->_data)
head1 = head1->next;
else if(head1->_data < head2->_data)
head2 = head2->next;
else
{
cout<<head1->_data;
head1 = head1->next;
head2 = head2->next;
}
}
}
2在单链表中总有N个节点让你删除倒数第K个节点
方法1
删除倒数第K个节点就是让你找到第N-K个节点,仔细想下,让你删除倒数第一个节点,你得找到第N-1个节点,因为第N个节点是最后一个节点,你得找到它前面的那个节点。
找到第N-K个节点的方法,遍历一次链表直到NULL,每次迭代的时候,K值减一。从第一个节点到NULL这个位置,刚好走迭代N次,因为一开始是从第一个节点走的,那么到第N个节点,一共走了N-1步,再走到NULL的时候,刚好迭代了N次。
此时,k的值为k-N , 那么我们现在开始再次遍历链表,每走一次让k+1,直到走到值为0的节点,那个节点就是N-K个节点。因为 (N-K) + (K-N) = 0 , 所以当K走到0的时候,就一共走了N-K步,那么就找到了第N-K个节点。
方法2
先找到倒数第K个节点,然后从头开始走判断下一个节点是否等于它,等于它跳出循环。
找到倒数第K个节点的方法,先让一个快指针走K步,然后慢指针开始走,当fast走到最后一个节点了,那么此时慢指针指向的节点就是倒数第K个节点。
细节总结
如果是第一个方法,K=0就代表被删除节点为头节点。但是对于第二个方法得自己把这个情况先过滤一遍。
Node * DeletelastK(Node*head,int k)
{
assert(head);
if (k < 1)
return NULL;
Node * pCur = head;
Node * del = nullptr;
while (pCur)
{
--k;
pCur = pCur->next;
}
if (k>0)
return pCur;
else if (k == 0)
{
del = pCur = head;
pCur = head->next;
}
else
{
/// 现在 k == K-N , 因为 共走了 N步, 每走一步 K减1 此时 K 的值等于 K - N
// 我们 可以让 K加到0 ,然后 让它走 N-K步。找到 第 N-K个节点 ,它就是倒数第K个节点的前一个节点
pCur = head;
while (++k < 0) // ++k 是因为直接从1号节点进入,2号节点,所以得先把1号节点那
{ //份值减掉
pCur = pCur->next;
}
del = pCur ->next;
pCur = del->next;
}
delete del;
return pCur;
}
Node *DeletelastK(Node * head, int K)
{
assert(head != NULL&&K >= 1);
Node * fast = head;
Node * last = head;
while (fast&&--K)
{
fast = fast->next;
}
if (K > 0)
return NULL;
while (fast->next)
{
fast = fast->next;
last = last->next;
}
/*先找到第K个节点*/
Node * del = head;
if (last == head)
{
del = head;
head = head->next;
}
else
{
while (del->next != last)
{
del = del->next;
}
del->next = last->next;
del = last;
}
delete del;
return head;
}
删除链表的中间节点和a/b处的节点
思路
方法一先把链表遍历一边,得到链表的长度,然后找到待删节点位置的前一个节点即可。
方法二,慢节点走一步,快节点走俩步,当快节点,走到最后一个位置的时候,慢节点找到待删节点的前一个位置。(最后一个位置可能是最后一个节点,也可能是倒数第二个节点)
题目描述
1->2 删除1 , 1->2->3 删除2 , 1->2->3->4 删除2,这里有个规律就是,如果是偶数就待删除的中间节点就是,N/2处的节点,如果是奇数,待删除节点就是N/2+1的节点。
第二道题待删除节点是这样找的,(N*a)/b 就是待删除位置,这个位置如果小于0就是非法return 即可,否则删除掉。
Node* deleteMid(Node*head)
{
int Length = 0;
Node * pCur =head;
while(pCur)
{
++Length;
pCur = pCur->next;
}
if(Length==1)
{
delete head;
return NULL;
}
Length = (Length%2>0)?((Length>>1)+1):Length>>1;
Length-=1;
Node * del = head; // 下面递归结束 del 就是被删除的前一个节点
while(Length>0&&--Length)
{
del = del->next;
} // -- Length是因为 直接从1号节点出发 所以得先减再判断
if(del == head)
{
head = head->next;
delete del;
return head;
}
del->next = del->next->next;
delete del->next;
return del;
}
Node * DeleteMid(Node * head)
{
if(head==NULL||head->next==NULL)
return head;
Node * del = NULL;
if(head->next->next==NULL)
{
del = head->next;
delete del;
}
Node * last = head;
del = head->next->next;
while(del->next&&del->next->next)
{
last = last ->next;
del = del->next->next;
}
last->next = last->next->next;
delete last->next;
return last;
}
/*删除 第 a/b个节点*/
Node*removeRatio(Node*head,int a, int b)
{
if(a/b>1||head==NULL)
return head;
int N = 0;
Node * pCur = head;
while(pCur)
{
N++;
pCur=pCur->next;
}
N = (a*N)/b;
pCur = head;
if(N==1)
{
head = head->next;
delete pCur;
}
if(N>1)
{
while(--N!=1)
{
pCur=pCur->next;
}
pCur->next = pCur->next->next;
delete pCur->next;
}
return head;
}
反转链表/反转部分链表
思路
定义一个新节点,头插法即刻解决。
第二道题,就是找到部分区间的前一个节点与后一个节点,反转部分节点后,将其再拼接即可。主要细节就是,可能从第一个节点开始反转,则可能更换头节点,所以做个条件过滤即可。
/*反转单链表*/
Node * reserve(Node*head)
{
if(head==NULL)
return NULL;
Node * NewHead = NULL;
Node * pCur =head;
Node * pNext = NULL;
while(pCur)
{
pNext = pCur->next;
pCur->next = NewHead;
NewHead = pCur;
pCur = pNext;
}
return NewHead;
}
Node* reversepart(Node*head,int from ,int to)
{
Node * fpre = NULL;
Node * Nto = head;
int length = 0;
Node * pCur = head;
int temp=0;
while(pCur)
{
++length;
pCur = pCur->next;
}
if(from>to||to>N||from<1)
return head;
temp = from;
if(temp != 1)
{
fpre = head;
while(--temp!=1)
{
fpre = fpre->next;
}
}
temp = to;
while(--temp!=-1)
{
Nto = Nto->next;
}
Node * pNext = NULL;
Node * Newhead = Nto;
if(fpre!=NULL)
pCur = fpre;
else
pCur = head;
while(pCur!=Nto)
{
pNext = pCur->next;
pCur->next = NewHead;
NewHead = pCur;
pCur = pNext;
}
if(fpre)
{
fpre->next=NewHead;
NewHead = head;
}
return NewHead;
}
复杂链表的赋值
方法1
用unordered_map , 保存每一个节点得到指针和新构造出来的节点指针。然后遍历链表的时候,用operator[] 返回对应key的复制出来节点的指针,然后如果这个当前节点next/random有值的话,可以快速从unordered_map中查找到,对应的value就是复制出来的节点,原理很绕口,代码很简单。
方法2
上面的空间复杂度O(n),那么可以不用 unordered_map, 就直接定义临时变量,然后把新复制的节点,放到原节点的后面,设置下一个位置的时候,复制节点的下一个位置就是它的下一个位置的下一个位置,random就是原节点的random的下一个位置。因为它们串插在链表中,所以复制节点的下一个节点是原节点的下一个节点,那么原节点的下一个节点就是复制节点的下一个节点,看代码即可。
方法1:
Node* CopyRandList(Node*head)
{
unordered_map<Node*,Node*> hash;
Node * pCur = head;
while(pCur)
{
hash.insert(pair<Node*,Node*>(pCur,new Node(*pCur)));
pCur = pCur->next;
}
pCur = head;
while(pCur)
{
hash[pCur]->next =(pCur->next)?hash[pCur->next]:NULL;
hash[pCur]->rand =(pCur->rand)?hash[pCur->rand]:NULL;
pCur = pCur->next;
}
return hash[head];
}
方法 2 :
RandomListNode *copyRandomList(RandomListNode *head)
{
if(head==NULL)
return NULL;
Node * Copy =NULL;
Node * pCur = head;
Node * pNext =NULL;
/*复制*/
while(pCur)
{
Copy = new Node(*pCur);
pNext = pCur->next;
Copy->next = pNext;
pCur->next = Copy;
pCur = pNext;
}
/*设置随机指针*/
pCur = head;
while(pCur)
{
Copy = pCur->next;
Copy->random = (pCur->random)?(pCur->random->next):(NULL);
pCur = pCur->next->next;
}
/*拆分*/
pCur = head;
pNext = pCur->next;//保存新链表的第一个节点
Copy =pCur->next;
while(pCur)
{
pCur->next = Copy->next;
if(Copy->next)
{
Copy->next = Copy->next->next;
Copy = Copy->next;
}
else
{
Copy->next = NULL;
Copy = NULL;
}
pCur = pCur->next;
}
return pNext;
}
返回俩个可能带环链表的交点
分析
先判断俩个链表是否带环,如果都带环/都不带环可能有交点否则无交点。
第二步,如果都不带环,进入不带环返回交点的函数。
第三部,都带环,有俩个模型:
模型1与模型2是俩个带环相交的可能情况,模型三是俩个带环不相交的情况。
Node * LoopNode(Node * head)
{
if(head == NULL)
return NULL;
Node * fast = head;
Node * low = head;
while(fast&&fast->next)
{
fast = fast->next->next;
low = low->next;
if(low==fast)
break;
}
if(fast==NULL||fast->next==NULL)
return NULL;
fast = head;
while(fast!=low)
{
fast = fast->next;
low = low->next;
}
return fast;
}
Node * Associate(Node * head1, Node* head2)//返回俩个不带环链表的交点
{
if(head1==NULL||head2==NULL)
return NULL;
int Length1 = 0;
int Length2 = 0;
Node * pCur = head1;
Node * pCur2 = head2;
while(pCur->next)
{
++Length1;
pCur = pCur->next;
}
while(pCur2->next)
{
++Length2;
pCur2 = pCur2->next;
}
if(pCur!=pCur2)
return NULL;
Length1+=1;
Length2+=2;
pCur = (Length1>Length2)? head1 : head2;
pCur2 = (Length1<Length2)? head1 : head2;
Length1 = (Length1>Length2)?Length1-Length2:Length2-Length1;
while(Length1--)
{
pCur = pCur->next;
}
while(pCur!=pCur2)
{
pCur=pCur->next;
pCur2 = pCur2->next;
}
return pCur;
}
Node * LoopAssociate(Node * head1, Node * head2)//返回俩个可能带环链表的交点
{
Node * Loop1 = LoopNode(head1);
Node * Loop2 = LoopNode(head2);
if(Loop1==NULL&&Loop2==NULL)
return Associate(head1,head2);
if(Loop1&&Loop2)
{
if(Loop1==Loop2) // 环入口点相同,说明是俩个带环相交的模型1
return Loop1;
Node * pCur = Loop1->next;
while(pCur!=Loop1&&pCur!=Loop2)//这里得判断下,俩个带换不一定就
{ //相交,如果相交的话,就为模型2,从Loop1转
pCur = pCur->next;//一圈肯定内遇见Loop2,一圈没遇见说明没交点
}
if(pCur == Loop2)
return Loop2;
else
return NULL;
}
return NULL;
}