链表的扩展操作21——legend050709

来源:互联网 发布:数控铣床加工中心编程 编辑:程序博客网 时间:2024/06/13 11:12
链表的扩展操作21:


(一)双指针:


(1)定义:
指的是用两个指针来迭代访问链表。


(2)范例:
1.求取链表中倒数第k个节点。
2.求取链表中的中间节点。(快慢双指针)
3.判断链表是否有环(快慢双指针)以及环的入口处。
4.假设有一个链表a1->a2->...->an->b1->b2->..->bn,将其重新排列为a1->b1->a2->b2->...->an->bn(即完美洗牌问题);


解决方法:
1)定义两个指针p1,p2,p1每次走两步,p2每次走一步,
2)当p1到达链表末尾时,p2刚好到达中间位置。(当p1=null时,p2=b1).
3)然后p3从头开始,每次走一步,将p2所指的节点放到p3后面。


(二)删除链表中的重复节点:
(1)使用HashTable;
时间复杂度为O(N),空间复杂度为O(N)


void deleteRepeat(LinkNode* head){
HashTable<DataType , bool> table=new HashTable<DataType , bool>();
LinkNode* pre=head;
LinkNode* cur=head->next;


while(cur!=NULL){
if(table.containKey(cur->data)){
pre->next=cur->next;


}else{
table.put(cur->data,true);
pre=cur;
}
cur=cur->next;

}


(2)不使用缓冲区:
时间复杂度为O(n^2),空间复杂度为O(1);


1)分析:使用两个指针,cur迭代扫描整个链表,runner用于扫描后续的节点是否重复。


void deleteRepeat(LinkNode* head){
if(NULL==head || NULL==head->next)
return ;
LinkNode* cur=head->next;


while(cur!=NULL){
LinkNode* runner=cur;
while(runner->next){
if(runner->next->data==cur->data){
runner->next=runner->next->next;//删除runner->next;
}else{
runner=runner->next;
}
}
cur=cur->next;
}
}


--------


(三)删除单链表中的某个节点,假定只能访问该待删除节点,不能访问首节点。


1)分析:
直接将后继节点的数据复制到当前节点,然后删除后继节点。
2)实现:
bool deleteNode(Node* pnode){
if(NULL==pnode || NULL==pnode->next)
return false;


Node* pnext=pnode->next;
pnode->data=pnext->data;
pnode->next=pnext->next;
delete pnext;
return true;



--------


(四)以x为基准将链表分为两个部分,所有小于x的节点排在大于或等于x的节点之前。


1)分析:类似于数组的快速排序。但是此中是链表,不需要移动数据,更容易实现。


2)方法:
1.创建两个链表,一个链表存放小于x的节点,另一个链表存储大于等于x的节点。
2.迭代访问整个链表,将元素插入到before或after链表中,一旦抵达链表的末端,则表明拆分完成,最后合并两个链表。


------


(五)给定两个用链表表示的整数,每个节点表示一个数位,这些数位是反向的,即个位排在链表的首部。编写函数对两个整数求和。最终的和仍然存储在链表中,且为反向存储。


(1)分析:结合加法的对齐方式,以及链表的访问必须从头到尾访问,反向存储是较为简单的方法。
(2)实现:
/*
根据当前list1->data,list2->data,以及carry,
返回一个data=(list1->data+list2->data+crray)%10的节点
*/
LinkNode* addLists(LinkNode* list1,LinkNode* list2,int carry ){
if(list1==NULL && list2==NULL && carray==0)
return NULL;


linkNode* result=new LinkNode();
int sum=carry;
if(list1){
sum+=list1->data;
}
if(list2){
sum+=list2->data;
}


result->data=sum%10;


LinkNode* pnode=addLists(list1==NULL?NULL:list1->next,
list2==NULL?NULL:list2->next2,
value>10?1:0);


result->next=pnode;
result=pnode;
return result;
}


注意:此中注意的是处理两个链表长度不一样时。


(六)进阶:
当这些数位都是正序存放的,结果也正序存放。


(1)分析:
1)当两个链表长度不相同时,则应该用0头插法来填充较短的链表。
2)结果为正序存放,所以需要采用头插法建立和链表。




--------------------


(七)给定一个有环的链表,返回链表环的开头节点。


(1)检测链表是否有环?


1)方法:快慢两个指针fast和slow。
2)分析:fast一次走两步,slow一次走一步,会不会fast越过slow?
不会,反证法:
假设越过了,fast处于i+1位置,slow处于i位置,那么前一步fast处于i-1,slow也处于i-1,已经相遇了。


(2)什么时候相遇?
假设这个链表有一部分不存在环,设长度为k、
当slow走了p步,fast走了2p步。所以当slow走了k步进入环时,fast走了2k步,离入口点为2k-k=k步,由于k可能大于环的大小loop_size,所以离环口应该是K=mod(k,loop_size),(用大K表示)。
综上:
1.slow处于环内的0步位置时;
2.fast处于环内的K步位置;
3.slow落后于fast,相离K步;
4.fast落后于slow,相离loop_size-K步;
5.每过一个单位时间,fast就会更加接近slow一步。


什么时候相遇?
如果fast落后于slow,相距loop_size-K步,每经过一个单位时间,fast就接近slow一步,那么两者将在loop_size-K步之后相遇。此时两者与环口处距离K步(因为下次走到环口需要K步,所以相遇点到环口距离为K,环口到相遇点的距离为loop_size-K),相遇位置定义为collisionSpot(相撞点)。


(3)如何寻找环口点?
相遇处于环口相距K个节点。由于K=mod(k,loop_size),所以k=K+M*loop_size;(k为链表起始点到环口loopStart的距离,K为相遇点到环口的距离)所以可以定义两个指针,一个从链表头开始,一个从相遇collisionSpot开始,以同样的速度前行,则必定相遇。
(其实:相遇时collisionSpot距离链表的起点的距离为k+loop_size-K=K+M*loop_size+loop_size-K=
n*loop_size;)




(4)实现:
LinkNode* findLoopStart(LinkNode* list){
LinkNode* slow=list;
LinkNode* fast=list;


while(fast!=NULL && fast->next!=NULL){
slow=slow->next;
fast=fast->next->next;
if(slow==fast){
break;
}
}


if(fast==NULL || fast->next==NULL)//没有环 
return NULL;


slow=list;//slow指向首部


while(slow!=fast){
slow=slow->next;
fast=fast->next;
}


return fast;
}

0 0
原创粉丝点击