玩转链表之链表位置匹配

一、两个链表的第一个公共结点

解法一:双指针长度比较(推荐)

  • 单独的遍历两个链表,取得各自长度
  • 求得长度差为n,让较长的链表后移n个节点
  • 两链表同步后移,遇到的第一个+相同的节点即为公共节点
class Solution {
public:
    int size(ListNode*p)
    {
        ListNode*cur=p;
        int count=0;
        while(cur)
        {
            cur=cur->next;
            count++;
        }
        return count;
    }
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        int len1=size(pHead1),len2=size(pHead2);//取得长度
        if(len1>len2)//分类讨论
        {
            int n=len1-len2;
            while(n--)//先移n位
            {
                pHead1=pHead1->next;
            }
            for(int i=0;i<len2;i++)//后面长度一样为较小的链表长
            {
                if(pHead1==pHead2)//判断相同节点
                    return pHead1;
                pHead1=pHead1->next;
                pHead2=pHead2->next;
            }
        }
        else
        {
            int n=len2-len1;
            while(n--)
            {
                pHead2=pHead2->next;
            }
            for(int i=0;i<len1;i++)
            {
                if(pHead1==pHead2)
                    return pHead1;
                pHead1=pHead1->next;
                pHead2=pHead2->next;
            }
        }
        return NULL;//未找到返回NULL
    }
};

时间复杂度:O(n),n为链表较长者长度
空间复杂度:O(1)

解法二:双指针连接法

  • 判断链表,若有空者,无公共节点返回空
  • 两链表都从头遍历
  • 无须物理上的连接,仅需指针在一个链表的尾部时直接跳到另一个链表的头部
  • 第一个相同的节点便是第一个公共节点
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(!pHead1||!pHead2)//有一为空返回空
            return NULL;
        ListNode*p1=pHead1,*p2=pHead2;
        while(p1!=p2)
        {
            p1=p1==NULL?pHead2:p1->next;//一链表到尾步就跳转另一链表
            p2=p2==NULL?pHead1:p2->next;
        }
        return p1;
    }
};

时间复杂度:O(n+m),n,m分别是两链表长度,依次遍历两链表,合并看也就是O(n)级
空间复杂度:O(1)

解法三:容器

  • 先存入一链表节点,然后用另一链表查找是否存在,第一个存在的即时公共节点
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        set<ListNode*>s;
        while(pHead1)//存入节点
        {
            s.insert(pHead1);
            pHead1=pHead1->next;
        }
        while(pHead2)
        {
            if(s.count(pHead2))//当查找到存在该节点时返回
                return pHead2;
            pHead2=pHead2->next;
        }
        return NULL;
    }
};

时间复杂度O(n+m):n,m分别表示 pHead1和pHead2的链表长度,最差情况下需要遍历完两个链表
空间复杂度O(n):需要额外集合空间存储 pHead1 结点

二、两链表相加

解法一:反转链表(推荐)

  • 考虑链表为空的特殊情况,一链表为空,直接返回另一个即可
  • 反转两个链表
  • 设置返回链表的链表头,设置进位ret(每个节点只能存10以内的自然数)
  • 从头遍历两链表,直到链表为空且进位ret为0,每次取出不为空的链表节点值,为空就设为0,将两数字与ret相加,判断是否进位,将进位后的结果加入新的链表节点中(模10后的结果),连接在返回链表后,继续向后遍历
  • 返回前将返回链表再反转回来
class Solution {
public:
    /**
     * 
     * @param head1 ListNode类 
     * @param head2 ListNode类 
     * @return ListNode类
     */
    ListNode*reverse(ListNode*p)//反转链表
    {
        if(p==NULL)
            return p;
        ListNode*newhead=NULL;
        while(p)
        {
            ListNode*tmp=p->next;
            p->next=newhead;
            newhead=p;
            p=tmp;
        }
        return newhead;
    }
    ListNode* addInList(ListNode* head1, ListNode* head2) {
        // write code here
        if(head1==NULL)//为空返回另一个
            return head2;
        if(head2==NULL)
            return head1;
        ListNode*dummy=new ListNode(-1),*head=dummy;//设置虚拟表头
        head1=reverse(head1);//反转
        head2=reverse(head2);
        int ret=0;
        while(head1||head2||ret)
        {
            int val1=head1==NULL?0:head1->val,//为空取零
            val2=head2==NULL?0:head2->val;
            int sum=val1+val2+ret;
            ret=sum/10;
            head->next=new ListNode(sum%10);//建立节点
            head=head->next;
            if(head1)
                head1=head1->next;//不为空更新
            if(head2)
                head2=head2->next;
        }
        return reverse(dummy->next);//再反转回来
    }
};

时间复杂度:O(max(m,n)),m,n分别为两链表长度,反转链表三次,分别为O(m),O(n),O(ma(m,n)),相加过程也是遍历较长的链表
空间复杂度:O(1),这里的新建链表属于必要空间

解法二:使用辅助栈

这就是将反转链表改成了栈,利用栈的特点实现反序相加

class Solution {
public:
    /**
     * 
     * @param head1 ListNode类 
     * @param head2 ListNode类 
     * @return ListNode类
     */
    ListNode* addInList(ListNode* head1, ListNode* head2) {
        // write code here
        if(head1==NULL)
            return head2;
        if(head2==NULL)
            return head1;
        stack<ListNode*>s1,s2;
        while(head1)
        {
            s1.push(head1);
            head1=head1->next;
        }
        while(head2)
        {
            s2.push(head2);
            head2=head2->next;
        }
        int ret=0;
        ListNode*dummy=new ListNode(-1),*head=dummy->next;
        while(!s1.empty()||!s2.empty())
        {
            int sum=ret;
            if(!s1.empty())
            {
                sum+=s1.top()->val;
                s1.pop();
            }
            if(!s2.empty())
            {
                sum+=s2.top()->val;
                s2.pop();
            }
            ret=sum/10;
            ListNode*node=new ListNode(sum%10);//因为没反转,这里都要头插连接
            node->next=head;
            head=node;
        }
        if(ret>0)//最后进位大于0,头插
        {
            ListNode*tmp=new ListNode(ret);
            tmp->next=head;
            head=tmp;
        }
        return head;
    }
};

时间复杂度:O(n),遍历常数次链表
空间复杂度:O(n),借助非必要栈空间

三、单链表的排序

解法一:归并排序(推荐)

  • 如果链表为空或只有一个元素,则是有序的
  • 设置三个指针,快指针right每次走两步,慢指针mid走一步,前序指针left每次跟在mid前一个位置,三指针遍历链表,当快指针到达链表尾部时,mid刚好走了一半
  • 从left位置断开链表,分为两个子问题递归
  • 终止条件为空或只剩一个节点
  • 每次返回两个排序且合并好的子链表
class Solution {
public:
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    ListNode*Merge(ListNode*p1,ListNode*p2)//按升序合并两个链表
    {
        if(p1==NULL)
            return p2;
        if(p2==NULL)
            return p1;
        ListNode*dummy=new ListNode(-1),*cur=dummy;
        while(p1&&p2)
        {
            if(p1->val<=p2->val)
            {
                cur->next=p1;
                p1=p1->next;
            }
            else
            {
                cur->next=p2;
                p2=p2->next;
            }
            cur=cur->next;
        }
        if(p1)
            cur->next=p1;
        if(p2)
            cur->next=p2;
        return dummy->next;
    }
    ListNode* sortInList(ListNode* head) {
        // write code here
        if(head==NULL||head->next==NULL)//链表为空或只有一个元素返回
            return head;
        ListNode*left=head,*mid=head->next,*right=head->next->next;//设置三个指针
        while(right&&right->next)//快指针到达尾步时,mid到中点
        {
            left=left->next;
            mid=mid->next;
            right=right->next->next;
        }
        left->next=NULL;//left指针起切断作用,将链表一分为二
        return Merge(sortInList(head),sortInList(mid));//返回排序和并后的链表
    }
};

时间复杂度:O(n log ⁡ 2 \log_2 log2n),归并排序的复杂度
空间复杂制度:O( log ⁡ 2 \log_2 log2n),递归栈的深度最坏为 log ⁡ 2 \log_2 log2n层

解法二:容器重排

  • 将节点值存入容器
  • 用sort排序函数排序
  • 从头遍历依次给链表赋值
class Solution {
public:
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    ListNode* sortInList(ListNode* head) {
        // write code here
        if(head==NULL||head->next==NULL)//特殊判断
            return head;
        vector<int>v;
        ListNode*cur=head;
        while(cur)//存值
        {
            v.push_back(cur->val);
            cur=cur->next;
        }
        sort(v.begin(),v.end());//默认从小到大排序
        cur=head;
        for(int i=0;i<v.size();i++)//赋值
        {
            cur->val=v[i];
            cur=cur->next;
        }
        return head;
    }
};

时间复杂度:O(n log ⁡ 2 \log_2 log2n),sort函数的复杂度
空间复杂制度:O(n),使用额外容器空间

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

...404 Not Found

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值