240422 leetcode exercises

240422 leetcode exercises

@jarringslee

文章目录

  • 240422 leetcode exercises
    • [237. 删除链表中的节点](https://leetcode.cn/problems/delete-node-in-a-linked-list/)
      • 🔁节点覆盖法
    • [392. 判断子序列](https://leetcode.cn/problems/is-subsequence/)
      • 🔁直接遍历
      • 🔁动归预处理
    • [LCR 136. 删除链表的节点](https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/)
      • 🔁直接遍历

237. 删除链表中的节点

有一个单链表的 head,我们想删除它其中的一个节点 node

给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head

链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。

删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:

  • 给定节点的值不应该存在于链表中。
  • 链表中的节点数应该减少 1。
  • node 前面的所有值顺序相同。
  • node 后面的所有值顺序相同。

​ 他是想说什么意思呢,就是在不给你整个链表的情况下,让你根据这个值来将这个节点删除。

​ 题目要求我们的函数被调用后输出整个链表,而我们又注意到所写函数是void类型,所以我们只需要执行删除该节点的操作即可。

​ 如果毫无思路,我们可以回忆一下删除链表的原理:

让上一个结点的next指针直接指向下一个节点。

​ 由于我们需要对链表进行遍历,才能获得前驱指针并执行上述操作。但是这里我们根本无法获取前驱指针。但是我们写的函数又不需要我们返回链表,所以如果让当前节点的值直接变成下一结点的值,也就是覆盖下一个节点,是不是就等价于删除了当前节点呢?

🔁节点覆盖法

假设链表在内存中是这样的(箭头表示 next 指针):

 → [ A | * ] → [ B | * ] → [ C | * ] → …↑题目给出的node
  • [A|*] 表示当前节点的 val = Anext 指向下一个节点。
  • node 指针正指向值为 A 的节点,我们删掉它。
void deleteNode(struct ListNode* node) {*node = *node -> next; //哈哈就一行。
}

这一行等价于同时做了两件事:

node->val  = node->next->val;    // 把后继节点的值 B 复制到当前节点
node->next = node->next->next;   // 把后继节点的 next 指针复制过来

我们看看这个操作带来了什么样的影响:

  • 操作前

    [ A | -> X ]   [ B | -> Y ]node           next_node
    
    • node->val = A
    • node->next 指向下一个节点(值 B)
  • 执行 *node = *node->next;

    • node->val 变成了 B
    • node->next 变成了 node->next->next(即原来 B 的 next,指向 C)
  • 操作后

    [ B | -> Y ]   [ B | -> Y ]  node         (原 B 节点,已被孤立)
    

    现在的链表里,从 …→node 开始

    … → [ B | * ] → [ C | * ] → …
    

    ——等价于把原来的 B 节点直接「搬到」A 的位置,然后把原 B 节点从链表里跳过去了。

​ 这道题通过 复制后继节点 到当前节点,再跳过后继,实现了在不知道前驱的情况下删除节点的目标。关键一句 *node = *node->next;,相当于同时做了赋值 val 和重连 next,从链表逻辑上删掉了下一节点。

​ 我简直是天才。

392. 判断子序列

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

示例 1:

输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:

输入:s = "axc", t = "ahbgdc"
输出:false

​ 乍一看以为是包含字符串的问题,结果仔细一看,发现如果子序列的所有字符能被给出序列顺序地包含就符合条件。那么单次遍历的话就会简单很多。

🔁直接遍历

直接建立循环直接进行比较。

bool isSubsequence(char* s, char* t) {if (s[0] == '\0') return true;//排除空字符串情况int i = 0;for (int j = 0; t[j] != '\0'; j++){ //外循环移动大串if (s[i] == t[j]) i++;//如果发现该位置与小串相等,小串移动if (s[i] == '\0') return true;//循环内移动完了就成功}return false;
}

​ 但是题目又说, “当 t 很长、要对它执行大量(10亿)子序列判断查询”,在这种变态情况下,我们想刚才那样愚蠢地遍历好像是有点笨拙了。但倘若我预处理目标串 t,一次性记录好从每个位置往后字符 a…z 下次出现的位置,然后再用这个表快速「跳跃式」地在 t 中定位每个要匹配的 s[j],阁下又该如何应对?

🔁动归预处理

1. What is nxt?

​ 我们使用 (n+1)×26 的二维整型数组,用来存储 “从位置 i 开始,字母 'a'+c 下一次出现的位置”(其中 0 ≤ i ≤ n0 ≤ c < 26)。

nxt[i][c] 的含义是,在字符串 t 中,从下标 i(包含)往后,第一个字符等于 'a'+c 的位置索引;
如果再也不出现,即t 中再也没有字符 ('a'+c),我们就把它设为 n(一个「越界」值)。

int (*nxt)[SIGMA] = malloc((n + 1) * sizeof(int[SIGMA]));

​ 如果 t 中再也没有字符 ('a'+c),我们就把它设为 n(一个「越界」值)。

​ 这样,查找 “从位置 i 往后,‘x’ 下次出现在哪里” 就只需要读 nxt[i]['x'-'a'] 一次,时间 O(1)。

2. 构造 nxt

  1. 初始化末尾行

    for (int j = 0; j < SIGMA; j++) {nxt[n][j] = n;
    }
    

    位置 n 之后没有任何字符,所有字母的「下次出现」都标记为 n

  2. 自底向上填表

    for (int i = n - 1; i >= 0; i--) {// 先拷贝后一行:默认后续出现位置和 i+1 一样memcpy(nxt[i], nxt[i + 1], SIGMA * sizeof(int));// 然后把 t[i] 这一个字符的“下次出现”位置修正为 i 自己nxt[i][t[i] - 'a'] = i;
    }
    
    • “如果我在 i+1 后面第一次见到 c,位置是谁?” → nxt[i+1][c]

    • “但如果 t[i] 本身就是 c,就应该最近出现的位置是 i” → 覆盖 nxt[i][c] = i

    • 最终,nxt[0][c] 恰好告诉我们「整个串中第一次出现 c 的位置」。

3. 用 nxt 快速匹配子序列

int i = -1;
for (int j = 0; s[j] && i < n; j++) {// 在 t 中,从 i+1 开始,寻找 s[j] 下一个出现的位置i = nxt[i + 1][s[j] - 'a'];
}
return i < n;

我们用 i 记录上一次匹配到 t 的哪个位置。若初始 i = -1,表示还没匹配过任何字符;要找下一个,就从 i+1 = 0 开始搜。对于对每个 s[j],我们先计算 c = s[j]-'a',再读 pos = nxt[i+1][c]

如果 pos < n,说明在 t 中找到了,就把 i = pos,继续下一个 s[j+1]

如果 pos == n,说明找不到,就会让 i >= n ,循环后自然跳出,最后 return false

整个过程只做了 |s| 步 O(1) 的跳跃查询,匹配结束后检查 i < n 即可判断 s 是否完全匹配为子序列。

时间和空间复杂度

  • 预处理
    • 时间:构造 nxt 需要做 n+1 行,每行拷贝 26 个整数 → O(26·n) ≈ O(n)。
    • 空间:存 (n+1)×26int → O(n·Σ),这里 Σ=26。
  • 匹配阶段
    • 时间:遍历 s 一次,每步 O(1) 查表 → O(|s|)。

对于极多次查询同一个 t 是否包含不同 s 的变态情况,这种预处理 + 跳表的方法尤其高效。

LCR 136. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

示例 1:

输入:head = [4,5,1,9], val = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入:head = [4,5,1,9], val = 1
输出:[4,5,9]
解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

​ 我们使用前去节点遍历的方法来找到需要删除的值。

🔁直接遍历

​ 首先去掉目标只在头结点的情况。

​ 我们让前驱指针在下个节点的指针不指向空下个节点的值不等于目标值的情况下向前移动,在题目保证数据的情况下,一定会在指针走向尽头之前因为找到目标值而跳出这个循环。这时候我们让前驱指针所在的节点的next指针直接指向下一个节点的next指针,也就是下下一个节点的地址。

struct ListNode* deleteNode(struct ListNode* head, int val) {if (head -> val == val) return head -> next;struct ListNode* prev = head;
//不满足条件就遍历while( (prev -> next != NULL) && (prev -> next -> val != val)) prev = prev -> next;
//找到目标值就删if (prev -> next != NULL) prev -> next = prev -> next -> next; return head;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/77857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL之库的操作

创建数据库 语法很简单, 主要是看看选项(与编码相关的): CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name [DEFAULT] COLLATE collation_name 1. 语句中大写的是…

Git Flow分支模型

经典分支模型(Git Flow) 由 Vincent Driessen 提出的 Git Flow 模型,是管理 main(或 master)和 dev 分支的经典方案: main 用于生产发布,保持稳定; dev 用于日常开发,合并功能分支(feature/*); 功能开发在 feature 分支进行,完成后合并回 dev; 预发布分支(rele…

【Spring】依赖注入的方式:构造方法、setter注入、字段注入

在Spring框架中&#xff0c;除了构造器注入&#xff08;Constructor Injection&#xff09;和Setter注入&#xff08;Setter Injection&#xff09;&#xff0c;还有一种依赖注入方式&#xff1a;字段注入&#xff08;Field Injection&#xff09;。字段注入通过在Bean的字段上…

【数学建模】随机森林算法详解:原理、优缺点及应用

随机森林算法详解&#xff1a;原理、优缺点及应用 文章目录 随机森林算法详解&#xff1a;原理、优缺点及应用引言随机森林的基本原理随机森林算法步骤随机森林的优点随机森林的缺点随机森林的应用场景Python实现示例超参数调优结论参考文献 引言 随机森林是机器学习领域中一种…

HttpSessionListener 的用法笔记250417

HttpSessionListener 的用法笔记250417 以下是关于 HttpSessionListener 的用法详解&#xff0c;涵盖核心方法、实现步骤、典型应用场景及注意事项&#xff0c;帮助您全面掌握会话&#xff08;Session&#xff09;生命周期的监听与管理&#xff1a; 1. 核心功能 HttpSessionLi…

【Python爬虫基础篇】--2.模块解析

目录 1.urllib库 1.1.request模块 1.1.1、urllib.request.urlopen() 函数 1.1.2.urllib.request.urlretrieve() 函数 1.2. error模块 1.3. parse 模块 2. BeautifulSoup4库 2.1.对象种类 2.2.对象属性 2.2.1.子节点 2.2.2.父节点 2.2.3.兄弟节点 2.2.4.回退和前进 …

Ubuntu-Linux从桌面到显示的全流程:技术分析总结

引言 Ubuntu作为主流的Linux发行版&#xff0c;其显示系统经历了从传统X11到现代Wayland的演进。本文将详细分析从应用程序到屏幕显示的完整技术流程&#xff0c;包括桌面环境、显示服务器、图形栈和硬件交互等核心环节。 1. 系统架构概览 Ubuntu的显示系统架构可分为四个主要…

在PyCharm中部署AI模型的完整指南

引言 随着人工智能技术的快速发展,越来越多的开发者开始将AI模型集成到他们的应用程序中。PyCharm作为一款强大的Python IDE,为AI开发提供了出色的支持。本文将详细介绍如何在PyCharm中部署AI模型,从环境配置到最终部署的完整流程。 第一部分:准备工作 1. 安装PyCharm …

WHAT - 静态资源缓存穿透

文章目录 1. 动态哈希命名的基本思路2. 具体实现2.1 Vite/Webpack 配置动态哈希2.2 HTML 文件中动态引用手动引用使用 index.html 模板动态插入 2.3 结合 Cache-Control 避免缓存穿透2.4 适用于多环境的动态策略 总结 在多环境部署中&#xff0c;静态资源缓存穿透是一个常见问题…

PoCL环境搭建

PoCL环境搭建 **一.关键功能与优势****二.设计目的****三.测试步骤**1.创建容器2.安装依赖3.编译安装pocl4.运行OpenCL测试程序 Portable Computing Language (PoCL) 简介 Portable Computing Language (PoCL) 是一个开源的、符合标准的异构计算框架&#xff0c;旨在为 OpenCL…

【区块链技术解析】从原理到实践的全链路指南

目录 前言&#xff1a;技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块技术选型对比 二、实战演示环境配置要求核心代码实现&#xff08;10个案例&#xff09;案例1&#xff1a;创建简单区块链案例2&#xff1a;工作…

在Windows上安装Git

一、安装 Git 下载 Git地址&#xff1a;Git - Downloads (git-scm.com) 1、在页面中找到适用于 Windows 系统的最新版本安装包&#xff08;通常为.exe 格式文件&#xff09;&#xff0c;点击下载链接。 出于访问Git官网需要科学上网&#xff0c;不会的可以私信我要软件包&…

Golang interface总结(其一)

本篇是对golang 中的interface做一些浅层的、实用的总结 多态 常用场景 interface内仅包含函数类型&#xff0c;然后定义结构体去实现&#xff0c;如下 package mainimport "fmt"type Animal interface {Sound()Act() }type Cat struct{}func (c *Cat) Sound() {…

TVM计算图分割--Collage

1 背景 为满足高效部署的需要&#xff0c;整合大量优化的tensor代数库和运行时做为后端成为必要之举。现在的深度学习后端可以分为两类&#xff1a;1&#xff09;算子库(operator kernel libraries)&#xff0c;为每个DL算子单独提供高效地低阶kernel实现。这些库一般也支持算…

Redis——内存策略

目录 前言 1.过期策略 1.1过期策略——DB结构 1.2过期策略——惰性删除 1.3过期策略——定期删除 2.淘汰策略 2.1最少最近使用和使用频率原理 2.2内存淘汰策略执行流程 总结&#xff1a; 前言 Redis之所以性能强&#xff0c;主要的原因就是基于内存存储。然而单节点的R…

原型模式详解及在自动驾驶场景代码示例(c++代码实现)

模式定义 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;通过克隆已有对象来创建新对象&#xff0c;避免重复执行昂贵的初始化操作。该模式特别适用于需要高效创建相似对象的场景&#xff0c;是自动驾驶感知系统中处理大量重复数据结构的…

在kali中安装AntSword(蚁剑)

步骤一、下载压缩包 源码&#xff1a;https://github.com/AntSwordProject/antSword&#xff0c;下载压缩包。 加载器&#xff1a;https://github.com/AntSwordProject/AntSword-Loader&#xff0c;根据系统选择压缩包&#xff08;kali选择AntSword-Loader-v4.0.3-linux-x64&…

华为仓颉编程语言基础概述

第一章&#xff1a;技术演进与诞生背景 1.1 万物智联时代的编程挑战 在5G、物联网、边缘计算等技术推动下&#xff0c;全球智能设备数量呈指数级增长。据IDC预测&#xff0c;2025年全球IoT设备将突破550亿台&#xff0c;这对系统级编程语言提出新要求&#xff1a; 异构硬件兼…

【Linux篇】探索进程间通信:如何使用匿名管道构建高效的进程池

从零开始&#xff1a;通过匿名管道实现进程池的基本原理 一. 进程间通信1.1 基本概念1.2 通信目的1.3 通信种类1.3.1 同步通信1.3.2 异步通信 1.4 如何通信 二. 管道2.1 什么是管道2.2 匿名管道2.2.1 pipe()2.2.2 示例代码&#xff1a;使用 pipe() 进行父子进程通信2.2.3 管道容…

【LeetCode】嚼烂热题100【持续更新】

2、字母异位词分组 方法一&#xff1a;排序哈希表 思路&#xff1a;对每个字符串排序&#xff0c;排序后的字符串作为键插入到哈希表中&#xff0c;值为List<String>形式存储单词原型&#xff0c;键为排序后的字符串。 Map<String, List<String>> m new Ha…