剑指offer-链表专题总结

常见技巧

1、在单链表首元节点前加一个“头节点”,头指针指向头节点。
好处在于:
防止链表空时头指针为空
插入/删除 首元节点,操作与其他节点一致
使空链表和非空链表操作一致
2、指针差法:用一个指针遍历不行的时候,可以考虑两个指针遍历。
可以让其中一个快一些(比如一次走两步),或者让它先走若干步。
3、删除节点不一定要获得前一个节点,复制后一个节点过来,再删掉后面也行
4、递归也常用

01 面试题6:从尾到头打印链表

(1)构造一个栈,遍历链表入栈,然后出栈打印
(2)递归

 void printListFromTailToHead(ListNode* head){
    if(head == nullptr) return;
    if(head->next) printListFromTailToHead(head->next);
    //子问题返回后再打印
    cout << head->val << endl;
}

02 面试题18:删除链表的节点

题意:给定单向链表头指针和一个节点指针,删掉节点指针指向的节点,在o(1)时间内。
(1)常规方法是,遍历,得到节点前一个节点的指针,然后删除。o(n)
(2)可以将待删节点的下一个节点的值复制到待删节点,然后删除下一个节点。如果要删除最后一个节点,还是要遍历。 O(1)

03 面试题22:链表中倒数第k个节点

指针差法:两个指针,第一个先走k步,第二个不动,然后第一个和第二个一起走,知道第一个到尾节点。

04 面试题23:链表中环的入口节点

1 步长分别为2和1的一快一慢俩指针,跑到他们 1)相遇,则有环,相遇点在环内 2)遇到null,则没环
2 相遇点走一圈计数,算出环内点数n
3 p1从首元位置先走n步,然后p1和p2一起走,直到相遇,相遇点就是入口点。(p2走环外点数个点到入口处,p1先走n步,再走环外点数个点也到入口处)

05 面试题24:反转链表

(1)利用大量的调整指针操作
(2)使用递归

ListNode* ReverseList(ListNode* pHead) {
    //如果链表为空或者链表中只有一个元素
    if(pHead == NULL || pHead->nex t== NULL)  //递到终点,即最后一个节点,返回pHead,然后归
        return pHead;
    ListNode* pReverseNode = ReverseList(pHead -> next);        
    pHead->next->next = pHead; //再将当前节点设置为后面节点的后续节点
    pHead->next = NULL;
    return pReverseNode;
}

06 面试题25:合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则
(1)按照归并排序的思路归并,但是不新建节点,
(2)两个链表先比较头,头比较小的做新链表的头,再递归的合并小头链表的后半部分和另一个链表

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {  
        //递归解法
        if(pHead1 == nullptr) return pHead2;
        if(pHead2 == nullptr) return pHead1;
        
        ListNode* pMergeHead = nullptr;
        if(pHead1 -> val <= pHead2 -> val){
            pMergeHead = pHead1;
            pMergeHead -> next = Merge(pHead1 -> next, pHead2);
        }
        else{
            pMergeHead = pHead2;
            pMergeHead -> next = Merge(pHead1, pHead2 -> next);
        }
        return pMergeHead;
    }
};

07 面试题35:复杂链表的复制

题意:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个next指向下一个节点,另一个特殊指针sibling指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
(1)暴力解法 O(n^2)
复制每个节点,为N创建N’,用next链接起来
对于节点N,如果它的sibling指向S,那么N‘ 也指向S’,原始链表从头到S是x步,则复制链表也是x步。
对于每个节点,重复2
(2)借助哈希表:时 O(n),空O(n)
复制每个节点,为N创建N’,用next链接起来;同时用哈希表保存<N, N’>;
设置sibling,对于N,如果它指向S,那么根据<S,S’>查询S‘,N’就指向S‘
(3)连接-构建指针-拆除:时 O(n),空O(1)
每个节点N复制一个值一样的节点N’连接到N后面。
根据N构建每个 N‘ 的随机指针。
拆除连接

 RandomListNode* Clone(RandomListNode* pHead)
    {
        if(pHead == nullptr)
            return nullptr;
        //复制后链表的头指针
        CloneNodes(pHead);  //连接
        ConnectRandomPtr(pHead); //构建随机指针
        return ReConnectNodes(pHead); //拆除
    }
    //把复制出来的每个节点依次放在原节点的后面
    void CloneNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode){
            RandomListNode* pCloned = new RandomListNode(pNode -> label);
            pCloned -> next = pNode -> next;
            pNode -> next = pCloned;
            pNode = pCloned -> next;
        }
    }
    //设置复制节点的 Random 指针
    void ConnectRandomPtr(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode){
            if(pNode -> random){
                pNode -> next -> random = pNode -> random -> next;
            }
            pNode = pNode -> next -> next;
        }
    }
    //拆除连接
    RandomListNode* ReConnectNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead; //用于遍历长的组合链表中的原始节点
        RandomListNode* pClonedHead = nullptr; //拆分后复制链表的头指针
        RandomListNode* pClonedNode = nullptr; //用于遍历复制后链表    
        //设置pClonedHead
        pClonedHead = pNode -> next;
        pClonedNode = pNode -> next;
        pNode -> next = pClonedNode -> next;
        pNode = pNode -> next;       
        while(pNode){
            pClonedNode -> next = pNode -> next;
            pClonedNode = pClonedNode -> next;
            pNode -> next = pClonedNode -> next;
            pNode = pNode -> next;
        }
        return pClonedHead;
    }

08 面试题52:两个链表的第一个公共节点

(1)两个链表分别入栈st1和st2,再同时出栈,最后一个相等节点就是第一个公共节点。时O(m+n),空O(m+n)
(2)遍历两个链表得到长度m和n,计算长度差k,长的那个指针先走k步,然后一起走,第一个相同的节点就是了。时O(m+n),空O(1)

09 面试题62:圆圈中最后剩下的数字

题意:0~n-1,从0开始每次删掉第m个数字,再从下一个数字开始报数,找到最后一个数字
用链表模拟过程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值