草庐IT

代码随想录【链表】--->删除倒数第N个节点、链表相交、环形链表

syseptember 2024-05-11 原文

⭐️代码随想录⭐️
数组篇:
二分查找
移除数组
有序数组的平方
长度最小的数组
螺旋矩阵
链表篇
链表移除
设计链表
反转链表
交换链表中的节点

文章目录

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

题目LeetCode19. 删除链表的倒数第 N 个结点

思路

这道题的逻辑比较清晰
1. 先找到倒数第n+1个节点
2. 删除倒数第n个节点

为什么要找倒数第n+1个节点而不是倒数第n个节点呢?因为删除第n个节点时我们需要知道该节点的前一个节点在哪里,修改前一个节点的指针域来实现删除该节点,所以关键在于如何寻找倒数第n+1个节点
可以通过快慢指针来寻找倒数第n+1个节点让快指针始终在慢指针前面n+1个长度,这样当快指针走到链表结尾时,慢指针指向的就是倒数第n+1个节点
思考:本题是否需要和之前的提题目一样构造虚拟头节点呢?

使用虚拟头节点会更加方便,因为这题有可能会将头节点删除,这样不能够直接返回head,而使用虚拟头节点可以将真正的头节点和其他的节点统一处理,最后只需要返回dummHead->next即可

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    typedef struct ListNode ListNode;
    //构造虚拟头节点
    ListNode* dummyHead = (ListNode*)malloc(sizeof(ListNode));
    dummyHead->next = head;
    ListNode* fast = dummyHead;
    ListNode* slow = dummyHead;
    //快指针先走n + 1步
    int cnt = 0;
    while (cnt < n + 1)
    {
        //判断删除的节点是否合法
        if (fast == NULL)   return head;
        fast = fast->next;
        cnt++;
    }
    //快慢指针同时移动,直到快指针遍历完
    while (fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    //slow是待删除节点的下一个节点
    //删除节点
    ListNode* tmp = slow->next;
    slow->next = tmp->next;
    free(tmp);
    //返回新的头节点
    return dummyHead->next;
}


面试题 02.07. 链表相交

题目LeetCode面试题 02.07. 链表相交

思路

首先明白一点:链表相交指的是两个链表从某一个节点开始往后所有的节点在物理空间上的地址是一样的,而不是节点的val域一样
如果两链表相交,那么相交的部分最多的情况就是从较短的链表头节点开始相交,所以先让长链表遍历到一个位置使该位置往后的节点数和短链表节点数相同,从该位置开始判断链表节点的空间是否一样,继续判断两链表后一个节点的位置是否一样
如果链表不相交,那么直到curA和curB遍历完后两个指针的值仍然不相交(物理空间不同)

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    typedef struct ListNode ListNode;
    int lenA = 0;
    int lenB = 0;
    ListNode* curA = headA;
    ListNode* curB = headB;
    //求A链表的节点数
    while (curA)
    {
        lenA++;
        curA = curA->next;
    }
    //求B链表节点数
    while (curB)
    {
        lenB++;
        curB = curB->next;
    }
    curA = headA;
    curB = headB;
    //求两链表长度差
    int gap = lenA - lenB;
    //对齐链表末尾
    if (gap > 0) while (gap--) curA = curA->next;
    else
    {
        gap *= -1;//注意gap为负数的情况
        while (gap--) curB = curB->next;
    }
    //寻找相同节点
    while (curA)
    {
        //找到相交的第一个节点了,后续的节点一定相交
        if (curA == curB)   return curA;
        //两链表的该节点不相交,继续向后遍历
        else 
        {
            curA = curA->next;
            curB = curB->next;
        }
    }
    return NULL;
   
    
}


142. 环形链表 II

题目LeetCode142. 环形链表 II

思路

本题的两个关键问题

  • 如何判断链表有环?
  • 如果有环,如何确定环的入口?

判断链表有环

  • 如果链表为空或者链表只有一个节点,那么链表一定无环
  • 如果链表有多个节点,可以通过快慢指针判断链表是否有环

定义快指针fast、满指针slow,快指针一次走2个节点慢指针一次走一个节点如果经过若干步、快慢指针的值相等(指向同一个节点),那么链表一定有环

理论上来说如果一个链表有环,只需要保证快慢指针的速度差为1个节点,最终快慢指针的值一定会相等,所以这里快指针一次走3个节点、慢指针一次走2个节点也是可以的,但是这样的话可能会导致一次性越过链表尾节点,需要更多的条件来判断,所以这里使用快指针一次走2节点、慢指针一次走1节点是最合适的

确定环的入口

在确定链表一定有环之后,接下来就该确定环的入口了。
假设从头结点到环形入口节点的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点节点数为y。从相遇节点 再到环形入口节点节点数为 z。 如图所示:

慢指针经过的节点数为x+y,快指针经历的节点数为x+n(y+z)+y,其中n大于等于1因为快指针一定是在环内部走过了一圈才和慢指针相遇,由快指针的速度是慢指针的两倍,所以在相同的时间内有等式
2(x+y)=x+n(y+z)+y,化简可得x=(n-1)(y+z)+z,n大于等于1即可,所以当n=1时(快指针在环内走一圈后和慢指针相遇)x=z,这说明了如果快指针在环内走一圈后和慢指针相遇那么x=z,因此我们可以定义index1指向相遇点,index2指向头节点index1和index2每次走1个节点直到index1和index2的值相等,此时index1指向的就是环形入口节点

当n>1时,x的值一定比z大,所以index1从相遇点需要多转几圈才会和index2重合,但是总归会相遇,所以我们不需要更改代码,在相遇前仍然让index1和index2每次跨过一个节点

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    typedef struct ListNode ListNode;
    //链表为空或者 只有一个节点一定无环
    if ( head == NULL || head->next == NULL) return NULL;
    
    ListNode* fast = head;
    ListNode* slow = head;
    //寻找快慢指针相遇点
    ListNode* index1;

    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
          if (fast == slow)
        {
            index1 = fast;
            break;
        }
    }
    //判断链表是否有环
    if (fast != slow)   return NULL;
    ListNode* index2 = head;
    int x = 0;//入口到头节点的距离
    int y = 0;//相遇点到入口的距离
    //找到环的入口点
   while (index1 != index2)
   {
       index1 = index1->next;
       index2 = index2->next;
   }
   return index1;
}

有关代码随想录【链表】--->删除倒数第N个节点、链表相交、环形链表的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  4. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  5. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  6. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  7. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  8. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  9. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  10. ruby - 如何安全地删除文件? - 2

    在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?

随机推荐