• 《剑指offer》链表专题 (牛客10.23)


    难度 题目 知识点
    03. 返回链表的反序 vector 递归,C++ STL reverse()
    * 14. 链表中倒数第k个结点 指针操作
    15. 反转链表 头插法,递归
    16. 合并两个有序链表 指针操作
    *** 25. 复杂链表的复制 深度复制
    * 36. 两个链表的第一个公共结点 栈辅助,链表拼接,链表截取
    *** 55. 链表中环的入口结点 断链法,快慢指针
    * 56. 删除链表中重复的结点 指针操作

    03. 返回链表的反序 vector

    输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

    递归或者对正序 vector reverse。

    class Solution {
    public:
        vector<int> printListFromTailToHead(ListNode* head) {
            vector<int> rst;
            if(head == NULL)return rst;
            rst = printListFromTailToHead(head->next);
            rst.emplace_back(head->val);
            return rst;
        }
    };
    
    //----------------------vv---------------------------------
    class Solution {
    public:
        vector<int> printListFromTailToHead(ListNode* head) {
            vector<int> rst;
             
            ListNode* pNode = head;
            while(pNode!=NULL){
                rst.emplace_back(pNode->val);
                pNode=pNode->next;
            }
             
            reverse(rst.begin(),rst.end());
            return rst;
        }
    };
    

    14. 链表中倒数第k个结点+

    输入一个链表,输出该链表中倒数第k个结点。

    这道题去南大面试还被问了,两个指针的思路不错,但是涉及指针的细节很多,容易写崩。。。

    /*
    	首先要判断指针是否为空,以及k是否大于0!!
    	然后p1移到最后一个为止,不能指到空
    */
    class Solution {
    public:
        ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
            if(pListHead == NULL || k==0)
                return NULL;
             
            ListNode* p1=pListHead;
            ListNode* p2=pListHead;
            
            for(int i=1;i<k;i++){
                if(p1->next==NULL){
                    return NULL;
                }
                p1=p1->next;
            }
             
            while(p1->next != NULL){
                p1=p1->next;
                p2=p2->next;
            }
             
            return p2;
        }
    };
    

    15. 反转链表

    用头插法新建了一个链表。当然不新开链表是可以的,似乎正解也是这样。。然后递归也可以但没必要。

    16. 合并两个有序链表

    输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

    /*
    A:	考归并操作。
    T:	要时刻记得指针所指的结点,不要漏掉!
    */
    class Solution {
    public:
        ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
        {
            ListNode *newList = new ListNode(0);
            ListNode *p1 = pHead1, *p2 = pHead2, *p3 = newList;
            
            while(p1 && p2){
                if(p1->val <= p2 -> val){
                    p3->next = p1;
                    p1 = p1->next;
                }else{
                    p3->next = p2;
                    p2 = p2->next;
                }
                p3 = p3->next;
                p3->next = NULL;
            }
            
            if(p1){
                p3->next = p1;
            }else if(p2){
                p3->next = p2;
            }
            
            return newList->next;
        }
    };
    

    25. 复杂链表的复制 ++

    题目描述

    输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空。

    方法 1:map关联新节点旧结点

    方法 2:妙啊(滑稽

    img

    public class Solution {
        public RandomListNode Clone(RandomListNode pHead)
        {
            if(pHead==null) return null;
            //1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
            RandomListNode p=pHead,newHead=new RandomListNode(0),q;
            while(p!=null){
                RandomListNode newNode =new RandomListNode(p.label);
                newNode.next=p.next;
                p.next=newNode;
                p=newNode.next;
            }
    
            //2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
            p=pHead;
            while(p!=null){
                if(p.random!=null)
                    p.next.random=p.random.next;
                p=p.next.next;
            }
    
            //3、拆分链表,将链表拆分为原链表和复制后的链表
            p=pHead;
            q=newHead;
            while(p!=null){
                q.next=p.next;
                p.next=q.next.next;
    
                p=p.next;
                q=q.next;
            }
    
            return newHead.next;
        }
    }
    

    36. 两个链表的第一个公共结点+

    从第一个公共结点开始,都为公共结点,

    方法 1:借助辅助栈

    我们可以把两个链表的结点依次压入到两个辅助栈中,这样两个链表的尾结点就位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同。如果相同,则把栈顶弹出继续比较下一个,直到找到最后一个相同的结点。此方法也很直观,时间复杂度为O(m+n),但使用了O(m+n)的空间,相当于用空间换区了时间效率的提升。

    方法 2:将两个链表设置成一样长

    具体做法是先求出两个链表各自的长度,然后将长的链表的头砍掉,也就是长的链表先走几步,使得剩余的长度与短链表一样长,这样同时向前遍历便可以得到公共结点。时间复杂度为O(m+n),不需要额外空间。

    方法 3:将两个链表拼接起来。

    将两个链表进行拼接,一个链表1在前链表2在后,另一个链表2在前链表1在后,则合成的两个链表一样长,然后同时遍历两个链表,就可以找到公共结点,时间复杂度同样为O(m+n)。

    //方法3
    class Solution {
    public:
        ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
            ListNode *p1 = pHead1;
            ListNode *p2 = pHead2;
            while(p1!=p2){
                p1 = (p1==NULL ? pHead2 : p1->next);
                p2 = (p2==NULL ? pHead1 : p2->next);
            }
            return p1;
        }
    };
    

    55. 链表中环的入口结点+++

    题目描述

    给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

    方法 1:断链法

    采用断链法,访问过的节点都断开,最后到达的那个节点一定是循环的入口节点。

    public class Solution {
        public ListNode EntryNodeOfLoop(ListNode pHead) {
            if (pHead == null || pHead.next == null) {
                return null;
            }
            ListNode fast = pHead.next;
            ListNode slow = pHead;
            while (fast != null) {
                slow.next = null;
                slow = fast;
                fast = fast.next;
            }
            return slow;
        }
    }
    

    方法 2:快慢指针

    利用快慢指针,快指针一次走两步,慢指针一次走一步,具体思路如下:
    ①判断是否有环:若链表中存在环,快慢指针一定会在环上某一点相遇(因为相对速度为1);
    ②确定环的起点:
    ​ 设x为环前面的路程(黑色路程),a为环入口到相遇点的路程(蓝色路程,假设顺时针走), c为环的长度(蓝色+橙色路程)。则有

    • 慢指针走的路程 Sslow = x + m * c + a
    • 快指针走的路程为Sfast = x + n * c + a
    • 2 Sslow = Sfast

    ​ 可得 x = c - a,即相遇点后环剩余部分的路程。所以,我们可以让一个指针从链表起点开始走,让一个指针从相遇点开始继续往后走,俩指针会在环的起点相遇。

    public class Solution {
    
        public ListNode EntryNodeOfLoop(ListNode pHead)
        {
            if(pHead==null || pHead.next == null ||pHead.next.next ==null)
                return null;
    
            // 判环
            ListNode fast= pHead.next.next;
            ListNode slow= pHead.next;
    
            while (fast!=slow){
                if(fast.next!=null && fast.next.next!=null){
                    fast = fast.next.next;
                    slow = slow.next;
                } else{
                    return null;
                }
            }
    
            //循环出来的话就是有环,且此时fast==slow,快慢指针在相遇点
            fast=pHead;
            while (fast!=slow){//将在环的起点相遇
                fast=fast.next;
                slow=slow.next;
            }
    
            return slow;
        }
    }
    

    56. 删除链表中重复的结点+

    题目描述:

    在一个排序的链表中,存在连续重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
    例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

    很容易写错了。

    public class Solution {
        public ListNode deleteDuplication(ListNode pHead)
        {
            ListNode newHead = new ListNode(-10000000);//增加头节点统一操作
            newHead.next=pHead;
    
            ListNode pre = newHead, cur;
    
            while(pre!=null && pre.next!=null && pre.next.next!=null){
                if(pre.next.val==pre.next.next.val){//pre指向重复元素的前一个元素
                    cur=pre.next;
                    while(cur.next!=null&&cur.next.val==pre.next.val){
                        cur=cur.next;
                    }
                    pre.next=cur.next;
                }
                else{// 放在else里是因为刚删完元素后,不能保证pre指针后面两个元素不等,所以pre要保持不动
                    pre = pre.next;
                }
            }
    
            return newHead.next;
        }
    }
    
  • 相关阅读:
    HTTP状态码详解
    Django QuerySet 进阶
    初识Django
    小白学习之路,网络编程(下)
    小白学习之路,网络编程(上)
    小白学习之路,面向对象补充
    用正则表达式实现的计算器
    小白学习之路,初识面向对象
    小白学习之路,常用模块
    函数补充知识
  • 原文地址:https://www.cnblogs.com/weseewe/p/11729919.html
Copyright © 2020-2023  润新知