当前位置: 首页 > news >正文

网站更新文章衡水网站建设03181688

网站更新文章,衡水网站建设03181688,全州建设完小网站,营销型网站创建每日一题19. 删除链表的倒数第 N 个结点题目总体思路算法步骤时间复杂度与空间复杂度代码知识点哑节点的核心作用1. 统一处理删除头节点的情况2. 避免空指针异常3. 简化指针操作哑节点的工作原理链表结构变化哑节点的其他应用场景1. 链表反转2. 链表合并3. 链表排序24. 两两交换…

每日一题

  • 19. 删除链表的倒数第 N 个结点
    • 题目
    • 总体思路
      • 算法步骤
      • 时间复杂度与空间复杂度
    • 代码
    • 知识点
      • 哑节点的核心作用
        • 1. 统一处理删除头节点的情况
        • 2. 避免空指针异常
        • 3. 简化指针操作
      • 哑节点的工作原理
        • 链表结构变化
      • 哑节点的其他应用场景
        • 1. 链表反转
        • 2. 链表合并
        • 3. 链表排序
  • 24. 两两交换链表中的节点
    • 题目
    • 总体思路
      • 算法步骤
      • 时间复杂度与空间复杂度
    • 代码

2025.9.1

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

题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:
在这里插入图片描述
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:

输入:head = [1], n = 1
输出:[]
示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

进阶:你能尝试使用一趟扫描实现吗?

总体思路

这个算法采用快慢指针+哑节点的技巧来删除链表的倒数第n个节点。核心思想是让快指针先移动n步,然后快慢指针同时移动,当快指针到达链表末尾时,慢指针正好指向要删除节点的前一个节点。

算法步骤

  1. 创建哑节点:作为头节点的前驱,简化边界处理
  2. 快指针先移动n步:创建n个节点的间隔
  3. 同时移动快慢指针:直到快指针到达最后一个节点
  4. 删除目标节点:修改慢指针的next指向
  5. 返回结果:返回哑节点的下一个节点

时间复杂度与空间复杂度

  • 时间复杂度:O(L)
    • L 是链表的长度
    • 只需要遍历链表一次
    • 快指针最多移动 L 步
  • 空间复杂度:O(1)
    • 只使用了常数级别的额外空间(哑节点和两个指针)
    • 原地操作,不依赖链表长度

代码

golang

func removeNthFromEnd(head *ListNode, n int) *ListNode {dummy := &ListNode{Next: head}fast := dummyslow := dummyfor i:=0; i<n; i++ {fast = fast.Next}for fast.Next != nil {fast = fast.Nextslow = slow.Next}slow.Next = slow.Next.Nextreturn dummy.Next
}
/*** 删除链表的倒数第 N 个结点* 使用快慢指针和哑节点技巧来高效解决* * @param head *ListNode 链表的头节点* @param n int 要删除的倒数第n个节点的位置* @return *ListNode 删除节点后的新链表头节点*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {// 创建哑节点(dummy node)作为头节点的前驱// 作用:1. 简化删除头节点的特殊情况 2. 统一操作逻辑dummy := &ListNode{Next: head}// 初始化快慢指针,都指向哑节点// fast: 快指针,用于创建间隔和定位链表末尾// slow: 慢指针,用于定位要删除节点的前一个位置fast := dummyslow := dummy// 第一阶段:快指针先移动n步// 这样快慢指针之间就保持了n个节点的间隔for i := 0; i < n; i++ {// 快指针向前移动一步// 由于有哑节点的存在,不需要担心空指针问题fast = fast.Next}// 第二阶段:快慢指针同时移动// 循环条件:快指针的下一个节点不为nil(即快指针不是最后一个节点)// 当循环结束时,快指针指向最后一个节点,慢指针指向倒数第n+1个节点for fast.Next != nil {fast = fast.Next  // 快指针移动一步slow = slow.Next  // 慢指针移动一步}// 删除操作:将慢指针的next指向下下一个节点// 此时slow指向倒数第n+1个节点,slow.Next就是要删除的倒数第n个节点// slow.Next.Next可能是nil(如果要删除的是最后一个节点)或有效的节点slow.Next = slow.Next.Next// 返回哑节点的下一个节点,即新链表的头节点// 如果删除的是原头节点,dummy.Next就是新的头节点return dummy.Next
}

知识点

哑节点的核心作用

1. 统一处理删除头节点的情况

没有哑节点

  • 删除头节点需要特殊处理
  • 需要额外的条件判断

有哑节点

  • 头节点变成第二个节点
  • 删除操作统一处理
2. 避免空指针异常
// 没有哑节点,可能访问空指针
if head == nil {return nil
}// 有哑节点,始终有有效的节点可以操作
dummy := &ListNode{Next: head}  // 即使head为nil,dummy也存在
3. 简化指针操作

删除头节点的对比

// 没有哑节点:需要特殊处理
if 要删除的是头节点 {return head.Next  // 直接返回第二个节点
}// 有哑节点:统一处理
slow.Next = slow.Next.Next  // 无论删除哪个节点,操作都一样
return dummy.Next           // 总是返回哑节点的下一个

哑节点的工作原理

链表结构变化

原始链表

head → node1 → node2 → node3 → nil

添加哑节点后:

dummy → head → node1 → node2 → node3 → nil

哑节点的其他应用场景

1. 链表反转
func reverseList(head *ListNode) *ListNode {dummy := &ListNode{}current := headfor current != nil {next := current.Nextcurrent.Next = dummy.Nextdummy.Next = currentcurrent = next}return dummy.Next
}
2. 链表合并
func mergeTwoLists(l1, l2 *ListNode) *ListNode {dummy := &ListNode{}tail := dummy// ...合并逻辑return dummy.Next
}
3. 链表排序
func sortList(head *ListNode) *ListNode {dummy := &ListNode{Next: head}// ...排序逻辑return dummy.Next
}

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

题目

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

提示:

链表中节点的数目在范围 [0, 100] 内
0 <= Node.val <= 100

总体思路

在这里插入图片描述
链表两两交换节点的核心难点是 指针重连顺序
如果直接在原链表上操作,需要同时修改 3 组指针,一不小心就会断链。

算法步骤

  1. 引入 虚拟头节点 dummy,使“头节点”也有前驱,简化边界判断。
  2. 每次循环只处理 两个节点(first、second)。
  3. 四步指针重连:
    • pre → second
    • first → second.Next
    • second → first
    • pre 前移到 first(下一轮的 pre)
  4. 循环条件:
    pre.Next != nil && pre.Next.Next != nil
    保证剩余节点至少有两个,避免空指针或单节点无法成对的问题。

时间复杂度与空间复杂度

时间复杂度 O(n) 每个节点被访问一次,常数级交换操作。
空间复杂度 O(1) 仅用若干指针变量,没有递归栈或额外切片。

代码

golang
一开始写的,并不好

/*** 两两交换链表中的节点* 将链表中每两个相邻节点交换位置* 示例: 1->2->3->4 变成 2->1->4->3* * @param head *ListNode 链表的头节点* @return *ListNode 交换后的链表头节点*/
func swapPairs(head *ListNode) *ListNode {// 边界条件处理:空链表或单节点链表直接返回if head == nil || head.Next == nil {return head}// 创建哑节点,简化头节点交换的处理// 哑节点的Next指向原链表的头节点dummy := &ListNode{Next: head}// 初始化三个指针:// pre - 指向当前要交换的两个节点的前一个节点// first - 指向要交换的第一个节点// second - 指向要交换的第二个节点pre := dummyfirst := pre.Nextsecond := first.Next// 循环交换节点,直到没有更多节点对可交换for first != nil && first.Next != nil {// 交换first和second节点:// 1. 将pre的Next指向second(将前驱节点连接到第二个节点)pre.Next = second// 2. 将first的Next指向second的Next(第一个节点连接到下一对节点)first.Next = second.Next// 3. 将second的Next指向first(第二个节点指向第一个节点,完成交换)second.Next = first// 移动指针到下一对要交换的节点:// pre移动到当前交换后的第二个节点(实际上是原来的第一个节点)pre = first// first移动到下一对的第一个节点first = pre.Next// 如果first为nil,说明没有更多节点了,退出循环if first == nil {break}// second移动到下一对的第二个节点second = first.Next}// 返回哑节点的下一个节点,即新链表的头节点return dummy.Next
}
/*** Definition for singly-linked list.* type ListNode struct {*     Val int*     Next *ListNode* }*/
func swapPairs(head *ListNode) *ListNode {if head == nil || head.Next == nil {return head}denny := &ListNode{Next: head}pre := dennyfirst := pre.Nextsecond := first.Nextfor first != nil && first.Next != nil {pre.Next = secondfirst.Next = second.Nextsecond.Next = firstpre = firstfirst = pre.Nextif first == nil {break}second = first.Next}return denny.Next
}

ai优化:

func swapPairs(head *ListNode) *ListNode {dummy := &ListNode{Next: head}pre := dummyfor pre.Next != nil && pre.Next.Next != nil {first := pre.Nextsecond := first.Nextpre.Next = secondfirst.Next = second.Nextsecond.Next = firstpre = first}return dummy.Next
}
/*** Definition for singly-linked list.* type ListNode struct {*     Val  int*     Next *ListNode* }*/func swapPairs(head *ListNode) *ListNode {// 1. 创建虚拟头节点,统一所有节点的操作方式dummy := &ListNode{Next: head}// 2. pre 指向“当前待交换节点对”的前驱pre := dummy// 3. 只要后面还有两个节点,就继续交换for pre.Next != nil && pre.Next.Next != nil {// 3-1 取出两个待交换节点first := pre.Next       // 第 1 个节点second := first.Next    // 第 2 个节点// 3-2 四步指针重连pre.Next = second       // 前驱指向 secondfirst.Next = second.Next // 第 1 个节点指向下一对的开头second.Next = first     // 第 2 个节点指向第 1 个节点// 3-3 pre 移动到“下一对”的前驱(即本轮 first)pre = first}// 4. dummy.Next 始终指向新头节点return dummy.Next
}
http://www.dtcms.com/a/522198.html

相关文章:

  • 做整装的网站怎么才能创建一个网站
  • 怎么在360做网站浏网站建设补贴
  • 做网站的价钱西安网站开发建设
  • 网上推广手段包括合肥官网seo服务
  • 人工智能风险与伦理(6)
  • 江苏省工程建设标准定额网站做网站哪个公司
  • 企业网站ui模板下载wordpress 页面不存在
  • 网站规划与设计方向黑科技引流推广神器下载
  • 合阳县建设局网站企业网站建设的劣势
  • 网站开发百度百科wordpress的404页面如何做
  • 网站开发专业建设先进网站
  • 伊宁网站建设优化绍兴百度seo排名
  • 金华建设学校继续教育网站网站建设公司上海做网站公司
  • 没有工信部备案的网站是骗子吗浙江做网站公司有哪些
  • 浏阳企业网站建设最新手游2022首发排行
  • 网站运营推广郑州seo
  • 制作网站的公司做网站去哪里找郑州怎么做网站排名
  • 【经典书籍】C++ Primer 第15章类虚函数与多态 “友元、异常和其他高级特性” 精华讲解
  • 公司网站开发语言林业网站源码
  • 棋牌游戏网站怎么做如何做好高端品牌网站建设
  • h5企业网站只做快速seo关键词优化方案
  • 电商网站建设注意凡科网做网站怎么样
  • 国内网站免备案坦桑尼亚网站域名后缀
  • 丑陋网站设计赏析wordpress 注册 邮箱验证
  • 网站推广外贸网站备案 深圳
  • 外贸网站模板建立建设网站 托管 费用
  • 网站建设备案是什么意思一站传媒seo优化
  • 网站开发平台建设今天贵阳最新头条新闻
  • 石狮住房和城乡建设局网站找阿里巴巴购买做网站的软件
  • 北京建设网站网站网站开发 实时更新