代码随想录算法训练营day04

题目:24.两两交换链表中的节点、19.删除链表的倒数第N个节点、160.链表相交、142.环形链表II

参考链接:代码随想录

24.两两交换链表中的节点

思路:这题就正常画图思考,要使用虚拟头结点简化操作。遍历时两个一组考虑。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *dummyNode = new ListNode(0);
        dummyNode->next=head;
        ListNode *p=dummyNode;
        ListNode *q=head;
        ListNode *tmp;
        while(q && q->next){//每次遍历时,p为结点1的pre,q指向结点1,必须结点2不为空,才能完成交换
            tmp=q->next;
            p->next=tmp;
            q->next=tmp->next;
            tmp->next=q;
            p=q;
            q=q->next;
        }
        head=dummyNode->next;
        delete dummyNode;
        return head;
    }
};

标答没有用tmp,用的->next->next的方式,原理是一样的,而且标答没有删掉虚拟节点,我觉得不够严谨。

19.删除链表的倒数第N个节点

思路:这里一开始的想法是先获得链表长度,然后算出正数的节点数,然后遍历,需要两次遍历。对于一次遍历的方法,我想到的使用双指针,既然对倒数第n个节点,即需要删除的节点,和最后一个节点之间的距离为n-1,故我们一开始用p指针指向头,q指针指向p往后n-1距离的位置,然后同时开始移动p和q,直到q到达末尾,此时p节点就是需要删除的节点。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyNode = new ListNode(0);
        dummyNode->next=head;
        ListNode* p = dummyNode;
        ListNode* q = dummyNode;
        for(int i=1;i<n;i++){
            q=q->next;
        }
        ListNode* p_pre=dummyNode;//这个用于保存p的前置节点
        while(q->next){
            p_pre=p;
            p=p->next;
            q=q->next;
        }//这里q已经完成了它的使命,剩下我们操作p即可
        ListNode* tmp=p;
        p_pre->next=p->next;
        delete tmp;
        head = dummyNode->next;
        delete dummyNode;
        return head;
    }
};

这题思路并不难,但具体代码实现的时候debug了很久才完成,主要是在q到达末尾后我还想着操作q,实际上此时q已经完成任务,剩下就是一个简单的删除,依靠p->pre和p即可完成。标答没有写内存释放,其实我们在力扣中不写也可以,后面为简化操作我就不写了。

标答:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        
        // ListNode *tmp = slow->next;  C++释放内存的逻辑
        // slow->next = tmp->next;
        // delete nth;
        
        return dummyHead->next;
    }
};

标答中的slow相当于我们代码中的p_pre,fast相当于我们代码中的q->next

160.链表相交

思路:我看到的第一思路是首先计算两条链表的长度,然后得到长度差,然后让那条长的指针先走几步,等两个指针平齐的时候,再同时走,直到找到相同节点。解析也是这样作答的。

class Solution {
public:
    int getLength(ListNode* head){
        int len=0;
        ListNode* p=head;
        while(p){
            len++;
            p=p->next;
        }
        return len;
    }
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA=getLength(headA);
        int lenB=getLength(headB);
        ListNode *p=headA;
        ListNode *q=headB;
        if(lenA>lenB){
            for(int i=1;i<=lenA-lenB;i++){
                p=p->next;
            }
        }
        else{
            for(int i=1;i<=lenB-lenA;i++){
                q=q->next;
            }
        }//使p和q对齐
        while(p && q && p!=q){
            p=p->next;
            q=q->next;
        }
        return p;
    }
};

和标答比,我的代码将获得长度写成了一个函数,更加简洁。

142.环形链表II

思路:本题一开始我没想到思路,主要是判断环的方法我想不到,所以直接看了解析。

慢节点p一次走一个节点,快节点q一次走两个节点,这样如果有环,必定相遇。相遇时,p走了x+y,q走了x+y+n(y+z),其中n为q转过的圈数,由于快指针走过的路程是慢指针两倍,有2(x+y)=x+y+n(y+z),需要求x,得到x=(n-1)(y+z)+z,其中n>=1,由图可以看出,再选两个指针r和s分别指向head和相遇节点,同时往后走,最后相遇的节点就是入口节点。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* p=head;
        ListNode* q=head;
        while(q && q->next){//快指针不能到达空
            p=p->next;
            q=q->next->next;
            if(p==q){//相遇
                ListNode *r=head;
                ListNode *s=p;
                while(r!=s){
                    r=r->next;
                    s=s->next;
                }
                return r;
            }
        }
        return nullptr;//跳出循环一定是遇到了空
    }
};

本题思路比较复杂,而且需要数学运算,初次见到不好想到思路。我除了思路外,另一个启示是在写代码方面可以将真正需要的返回结果放到while中间进行,而不是仅仅把while用来遍历。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值