链表究极分析总结大作战

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值