链表究极分析总结大作战

来源:互联网 发布:tp主人网络与访客网络 编辑:程序博客网 时间:2024/04/28 06:16

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;        }    }}实现方法2void 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;}