[算法]单链表专题
如何判断链表环的入口位置?
一个指针从头开始单步走,一个指针从第一次相遇位置开始单步走,再相遇的位置就是环入口,证明如下:
设链表头到环入口位置距离为a,入口位置到第一次相遇位置为b,相遇位置回到入口位置距离为c。
1、每次走一步的慢指针走了s步,则快指针走了2s步。s=a+b。相遇时慢指针一定没走完一圈。
2、快指针走的总步数为a+b+n(b+c),走到相遇位置,并走了n圈。因此a+b+n(b+c)=2*(a+b)
a=n(b+c)-b=(n-1)(b+c)+c,也就证明了上面所提的方法。
class linklist { public: int val; linklist *next; linklist():next(NULL){} void push_back(int val); void clear(); ~linklist(){ cout<<this->val<<" has been deleted"<<endl; } }; void linklist::push_back(int val){ linklist *p=this; while(p->next!=NULL){ p=p->next; } p->next=new linklist; p->next->val=val; } void linklist::clear(){ linklist *p=this,*ptmp; while(p!=NULL){ ptmp=p->next; delete p; p=ptmp; } } linklist* merge(linklist *p1,linklist *p2){ if(p1==NULL){ return p2; } if(p2==NULL){ return p1; } linklist *head,*cur; if(p1->val<=p2->val){ head=p1; cur=p1; p1=p1->next; }else{ head=p2; cur=p2; p2=p2->next; } while(p1!=NULL && p2!=NULL){ if(p1->val<=p2->val){ cur->next=p1; cur=p1; p1=p1->next; }else{ cur->next=p2; cur=p2; p2=p2->next; } } if(p1!=NULL){ cur->next=p1; } if(p2!=NULL){ cur->next=p2; } return head; } linklist* merge_recur(linklist *p1,linklist *p2){ if(!p1){ return p2; } if(!p2){ return p1; } if(p1->val<=p2->val){ p1->next=merge_recur(p1->next,p2); return p1; }else{ p2->next=merge_recur(p1,p2->next); return p2; } } linklist* reverse(linklist *head){ linklist *pre,*cur,*next; pre=NULL; cur=head; while(cur){ next=cur->next;//传递next的值放开头,不放最后,可以避免cur为NULL的判断 cur->next=pre; pre=cur; cur=next; } return pre; } linklist* reverse_recur(linklist *head,linklist *&newhead){ if(!head){ return NULL; } linklist *p=reverse_recur(head->next,newhead); if(p){ p->next=head; }else{ newhead=head; } head->next=NULL; return head; } linklist* find_cross(linklist *p1,linklist *p2){ if(!p1 || !p2){ return NULL; } int len1=1,len2=1; linklist *ptmp=p1; while(ptmp->next){ len1++; ptmp=ptmp->next; } linklist *p1end=ptmp; ptmp=p2; while(ptmp->next){ len2++; ptmp=ptmp->next; } if(ptmp!=p1end){ return NULL; } //两个链表尾重叠了,证明链表交叉 if(len1>len2){//去掉较长链表的多余部分,然后开始一一对比 int count=len1-len2; while(count){ p1=p1->next; count--; } }else if(len2>len1){ int count=len2-len1; while(count){ p2=p2->next; count--; } } while(true){ if(p1==p2){ return p1; } p1=p1->next; p2=p2->next; } return NULL; } linklist* find_loop(linklist *head){ linklist *p1=head,*p2=head; do{ p1=p1->next; if(p2->next){ p2=p2->next->next; }else{ return NULL; } }while(p1 && p2 && p1!=p2); if(!p1 || !p2){ return NULL; } p2=head; while(p1!=p2){ p1=p1->next; p2=p2->next; } return p1; } linklist* find_middle(linklist *head){ linklist *fast=head,*slow=head; while(fast && fast->next){ fast=fast->next->next; slow=slow->next; } return slow; } linklist* del_last_k(linklist *head,int k){ /*linklist *reval; linklist *tmp=head,*pre_tmp=NULL;//用来存放当前节点的前k个节点的地址 int i=k-1; linklist *p=head; while(p->next){ if(i==0){//先判断i,再进行i--,不能反过来 pre_tmp=tmp; tmp=tmp->next; } p=p->next; if(i>0){ i--; } } if(i>0){//如果i没到0,证明k的大小比链表还长,不用进行删除 return head; } if(tmp==head){//如果删除了链表头,则需返回新的链表头 reval=head->next; }else{ reval=head; } if(tmp){ if(pre_tmp){ pre_tmp->next=tmp->next; } delete tmp; tmp=NULL; } return reval;*/ linklist *p=head,*reval; while(p && k>0){ p=p->next; k--; } if(p==NULL && k==0){//刚好删除第一个 reval=head->next; delete head; return reval; }else if(p==NULL && k>0){//k长度超出链表长度,不需删除 return head; }else{//要删的在第二个到最后一个之间,把p2移动到需要删除的元素之前 linklist *p2=head; while(p->next!=NULL){ p=p->next; p2=p2->next; } linklist *tmp=p2->next; p2->next=p2->next->next; delete tmp; return head; } } //根据地址删除一个中间节点:把删除点后接节点的值复制到删除点,然后删除后接点 void DeleteMidNode(linklist *del) { if (!del) { return; } linklist *next = del->next; if (next) { del->val= next->val; del->next= next->next; delete next; } } //在给定地址的节点前插入节点:在给定地址点后插入,然后交换前后两节点的值 void insert_before(linklist *node,linklist *insert){ if (!node || !insert) { return; } linklist temp = *node; node->val = insert->val; node->next= insert; *insert = temp; }