致我们终将忘记的算法(单链表那些事)

1->一个单链表L0->L1->L2->.......->Ln-1->Ln,翻转链表使其成为L0->Ln->L1->Ln-1.........不能通过改变结点的值来实现

解题方法:找到链表的中间结点,断开链表,把后半部分链表reverse一下,再合并两个单链表。算法的时间复杂度为O(n),空间复杂度为O(1)

ListNode* Reverse(ListNode *head){                                           //反转链表的代码

    if(head==NULL ||head->next==NULL)    return head;

    ListNode* cur=head;

    ListNode* pre=NULL;

    while(cur){

        ListNode* pNext=cur->next;

        cur->next=pre;

        pre=cur;

        cur=pNext;       

    }

    head=pre;

    return head;

}

void reorderList(ListNode *head){

    if(head==NULL ||head->next==NULL)    return ;

    ListNode *prev=NULL;  ListNode *pslow=head;  ListNode *pfast=head;

    while(pfast && pfast->next){

        prev=pslow;  pslow=pslow->next; pfast=pfast->next->next;

    }

    prev->next=NULL;                          //将链表从中间位置断开

    pslow=Reverse(pslow);               //将后半段链表反转

   /*合并两个链表*/

    ListNode* cur=head;

    while(cur->next){

        ListNode* tmp=cur->next;  cur->next=pslow;  pslow=pslow->next; cur->next->next=tmp; cur=tmp;

    }

    cur->next=pslow;

}

 

2->判断一个链表是否存在环

解题方法:设置两个指针,一个快一个慢,慢的一次走一步,快的一次走两步。如果两个相遇则说明存在环。

bool hasCycle(ListNode *head){

    ListNode* p1=head;

    ListNode* p2=head;

    while(p2 &&p2->next){

        p1=p1->next;

        p2=p2->next->next;

        if(p1==p2)  return true;

    }

    return false;

}

 

3->先判断一个链表是否存在环,若存在返回环的入口结点,否则返回NULL

解题方法:

ListNode *detectCycle(ListNode *head){

    ListNode *p1=head;  ListNode* p2=head;

    while(p2 && p2->next){

         p1=p1->next;   p2=p2->next->next;

         if(p1==p2){

             ListNode *p=head;

             while(p!=p1){p=p->next; p1=p1->next}

             return p;

         }

    }

    return NULL;

}

 

4->复杂链表复制

解题方法:先复制链表的每个结点,再设置链表Random指针的指向,最后将链表拆分成两个链表。

struct RandomListNode{

    int label;

    RandomListNode *next,*random;

    RandomListNode(int x):label(x),next(NULL),random(NULL){}

};

 

RandomListNode *copyRandomList(RandomListNode *head){

    for(RandomListNode *cur=head ; cur!=NULL;){

        RandomListNode *newNode=new RandomListNode(cur->lable);             //创建新的结点

        newNode->next=cur->next;   cur->next=newNode;  cur=newNode->next;      //将新建的结点加入到当前链表

    }

   /*设置新建结点的Random指针的值*/

   for(RandomListNode *cur=head;cur!=NULL;){

        if(cur->random!=NULL)   cur->next->random=cur->random->next;

        cur->next->next;

    }

   /*将一个链表拆分成两个链表*/

   RandomListNode  dummy(-1);

   for(RandomListNode *cur=head,*new_cur=&dummy;cur!=NULL;){

        new_cur->next=cur->next;   new_cur=new_cur->next;

        cur->next=cur->next->next;  cur=cur->next;

   }

    return dummy->next;

}


5->将一个链表分成KGroups,分别反转每一组内的链表。例如1->2->3->4->5.当k=2时,返回2->1->4->3->5.当k=3时返回3->2->1->4->5

解题方法:采用递归的方法按组逐个递归。

ListNode * reverseKGroup(ListNode* head,int k){

    if(head==NULL ||head->next==NULL ||k<2)    return head;

    /*将链表分组,当出现剩余元素不足以构成一组时,不反转*/

    ListNode *next_group=head;

    for(int i=0;i<k;i++){

        if(next_group)  next_group=next_group->next;

        else  return head;

    }

      /*调用递归程序反转下一个组的元素*/

    ListNode *new_next_group=reverseKGroup(next_group,k);

    /*反转当前组内的元素*/

    ListNode *prev=NULL,*cur=head;

    while(cur!=next_group){

        ListNode *next=cur->next;

        if(prev==NULL) cur->next=new_next_group;   else  cur->next=prev;

        prev=cur;   cur=next;

    }

    return prev;

}


6->成对的反转链表,相当于5题中k=2的情况。例如1->2->3->4 经过变换后成为2->1->3->4

解题方法:当然这题也可以采用5的递归的办法解决,其中将k设置为2.下面提供一种非递归的方法求解这个问题

ListNode  *swapPairs(ListNode *head){

    if(head==NULL ||head->next==NULL)    return head;

    ListNode dummy(-1);

    dummy.next=head;

    for(ListNode* prev=dummy,*cur=dummy->next,*next=cur->next; next ; prev=cur,cur=cur->next,next=cur?cur->next:NULL){

        pre->next=next;   cur->next=next->next; next->next=cur;

    }

    return dummy.next;

}


7->删除链表的倒数第K个节点。例如:1->2->3->4->5.删除链表的倒数第2个节点之后的链表为1->2->3->5

解题方法:定义两个指针等于头节点,p1=p2=head.p1先走k步,P2才开始走。当p1到达链表的尾部时,p2刚好指向要删除的节点

ListNode *removeNthFromEnd(ListNode* head,int k){

    int len=0;

    for(ListNode *p=head;p!=NULL;p=p->next)  len++;

    if(head==NULL || len<k) return NULL;

    ListNode* prev=NULL,*p1=head,*p2=head;

    for(int i=0;i<k;i++){

        p1=p1->next;

    }

    while(p2!=NULL){

        prev=p2;p1=p1->next;  p2=p2->next;

    }

    prev->next=p2->next;   free(p2);

    return head;

}


8->在链表的倒数第K个位置翻转链表。例如:1->2->3->4->5->NULL,当k=2时,返回4->5->1->2->3->NULL

解题方法:先遍历一遍链表,得出链表的长度len,因此令k%=len。然后将尾节点指向首节点形成一个环,接着走len-k步,在此处断开。就得到题目要求的链表

ListNode *rotateRight(ListNode *head,int k){

    if(head==NULL ||k==0)  return head;

    /*求链表的长度,并使p指向链表的最后一个节点*/

    int len=1;   ListNode *p=head;

    while(p->next){

        len++;    p=p->next;

    }

    k=len-k%len;

    p->next=head;     //尾首相接形成一个环

    for(int i=0;i<k;i++){

        p=p->next;

    }

    head=p->next;  p->next=NULL;

    return head;

}


9->删除一个已经排序的链表中值重复的节点。例如:1->1->2->2->3 返回1->2->3

ListNode *deleteDuplicates(ListNode *head){

    if(head==NULL)   return NULL;

    for(ListNode *prev=head,*cur=head->next;cur;cur=cur->next){

        if(prev->val==cur->val){ prev->next=cur->next;  delete(cur)}

        else prev=cur;

    }

    return head;

}


10->一个已经排序好的链表,删除所有出现过重复的节点。例如:1->1->1->2->3 返回链表2->3

ListNode *deleteDuplicates(ListNode *head){

    if(head==NULL)   return head;

    ListNode dummy(INT_MIN);     //定义一个额外的头结点

    ListNode* prev=&dummy,*cur=head;

    while(cur!=NULL){

        bool flags=false;

        while(cur->next!=NULL && cur->val==cur->next->val){

            flags=true;

            ListNode *tmp=cur;   cur=cur->next;  free(tmp);

        }

        if(flags){ListNode * tmp=cur; cur=cur->next; free(tmp);  continue;}  //删除重复的最后一个元素

        prev->next=cur;  prev=prev->next; cur=cur->next;

    }

    prev->next=cur;

    return dummy->next;

}

 

11->将一个链表按照value x的值分割成两部分,一部分值全都小于x,另一部分的值都大于等于x

ListNode *partition(ListNode *head,int x){

    ListNode left_dummy(-1);              //定义辅助头结点

    ListNode right_dummy(-1);

    ListNode *left_cur=&left_dummy;

    ListNode *right_cur=&right_dummy;

    for(ListNode *cur=head;cur;cur=cur->next){

        if(cur->val<x){left_cur->next=cur;  left_cur=cur}

        else {right_cur->next=cur;  right_cur=cur;}

    }

    left_cur->next=right_dummy.next;

    right_cur>next=NULL;

    return left_dummy.next;

}

 

12->反转链表m到n的子链

ListNode *reverseBetween(ListNode *head,int m,int n){

    ListNode dummy(-1);  dummy.next=head;

    /*找到要反转的子链的前一个结点*/

    ListNode *prev=&dummy;

    for(int i=0;i<m-1;i++)   prev=prev->next;

    ListNode *head2=prev;

  

   /*反转子链*/

    prev=prev->next;   ListNde* cur=prev->next;

    for(int i=m;i<n;i++){

        prev->next=cur->next;   cur->next=head2->next;

        head2->next=cur;  cur=prev->next;  //头插入法

    }

    return dummy.next;

}

 

13->两个链表中的值相加

ListNode *addTwoNumbers(ListNode *l1,ListNode *l2){

    ListNode dummy(-1);         //头结点

    int  carry=0;

    ListNode* prev=&dummy;

    for(ListNode *pa=l1,*pb=l2;pa!=NULL | pb!=NULL; pa=(pa==NULL)?NULL:pa-next,pb=(pb==NULL)?NULL:pb->next,prev=prev->next){

        const int ia=(pa==NULL)?0:pa->val;

        const int ib=(pb==NULL)?0:pb->val;

         cont int value=(ia+ib+carry)/10;    carry=(ia+ib+carry)/10;

         pre->next=new ListNode(value);            //尾插入法

    }

    if(carry>0)

        prev->next=new ListNode(carry);

     return dummy.next;

}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值