C/C++每日一练:删除链表的倒数第N个节点

news2024/12/27 9:31:56

链表(Linked List)

         链表是一种线性数据结构,由一系列节点(Node)通过指针链接在一起。与数组不同,链表中的元素在内存中不需要连续存储,每个节点包含两部分:

  • 数据部分:存储节点的值或数据。
  • 指针部分:存储指向下一个节点的地址(单链表)或上一个和下一个节点的地址(双向链表)。

         链表的类型主要有以下几种:

  • 单链表:每个节点只指向下一个节点。
  • 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。
  • 循环链表:链表的最后一个节点指向链表的头节点,形成循环。

         链表的特点:

  • 动态大小:可以根据需要动态地增加或减少元素,无需预分配存储空间。
  • 插入/删除效率高:在链表的任意位置进行插入或删除操作只需修改指针,不涉及大量元素的移动,效率较高。
  • 随机访问效率低:由于链表不支持直接访问任意位置的元素,需要通过遍历来查找特定位置的节点。

         如下图所示:

题目要求

题目描述:给定一个单链表和一个整数 n,请删除链表的倒数第 n 个节点,并返回链表的头节点。

输入输出示例

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

要求:尝试在一次遍历内解决问题,并保证时间复杂度为 O(N)。


解题思路

快慢指针法

  • 设置两个指针 fast 和 slow。
  • 先让 fast 指针移动 n 步,这样 fast 和 slow之间就相隔 n 个节点。
  • 接下来,两个指针同时移动,当 fast 到达链表末尾时,slow正好停在要删除节点的前一个位置。
  • 通过调整 slow 的指针指向,完成节点删除。

特殊情况处理

  • 如果链表只有一个节点,直接返回 NULL。
  • 如果要删除的节点是头节点,特殊处理即可。

算法过程

  1. 创建一个哑节点(dummy node)指向链表头部,用于简化操作。
  2. 初始化两个指针 fast  和 slow,均指向哑节点。
  3. 移动 fast  指针 n+1 步,保持 slow和 fast  之间的距离为 n+1。
  4. 同时移动 fast 和 slow,直到 fast 到达链表末尾。
  5. 调整 slow->next 指向,跳过目标节点,完成删除操作。
  6. 返回哑节点的下一节点 dummy->next 作为新的头节点。

示例代码

C 实现

#include <stdio.h>  // 标准输入输出库
#include <stdlib.h> // 动态内存分配和释放的库

// 定义链表节点结构
typedef struct ListNode {
    int val;              // 节点的值
    struct ListNode *next; // 指向下一个节点的指针
} ListNode;

// 辅助函数:创建新节点
ListNode* createNode(int val) {
    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); // 分配内存给新节点
    newNode->val = val;  // 设置节点值
    newNode->next = NULL; // 初始化为没有下一个节点
    return newNode;      // 返回新创建的节点
}

// 删除链表的倒数第n个节点
ListNode* removeNthFromEnd(ListNode* head, int n) {
    // 创建一个哑节点(dummy),方便操作链表头部
    ListNode* dummy = createNode(0);
    dummy->next = head;  // 哑节点的next指向链表头部
    ListNode *fast = dummy, *slow = dummy; // 初始化快慢指针都指向哑节点

    // 让fast指针先移动n+1步,使得fast和slow之间间隔n个节点
    for (int i = 0; i <= n; i++) {
        fast = fast->next; // fast每次向前移动一步
    }

    // 快慢指针同时移动,直到fast到达链表末尾
    while (fast != NULL) {
        fast = fast->next; // fast向前移动一步
        slow = slow->next; // slow向前移动一步
    }

    // 此时,slow指向要删除节点的前一个节点
    ListNode* temp = slow->next; // 保存要删除的节点
    slow->next = slow->next->next; // 跳过目标节点,完成删除
    free(temp); // 释放目标节点的内存

    // 返回链表的新头节点
    ListNode* newHead = dummy->next; // 哑节点的next即为新头节点
    free(dummy); // 释放哑节点的内存
    return newHead; // 返回新链表的头节点
}

// 辅助函数:打印链表
void printList(ListNode* head) {
    while (head != NULL) {         // 遍历链表直到尾节点
        printf("%d -> ", head->val); // 打印当前节点的值
        head = head->next;         // 移动到下一个节点
    }
    printf("NULL\n"); // 打印链表末尾标记
}

// 主函数
int main() 
{
    // 创建链表 1 -> 2 -> 3 -> 4 -> 5
    ListNode* head = createNode(1);
    head->next = createNode(2);
    head->next->next = createNode(3);
    head->next->next->next = createNode(4);
    head->next->next->next->next = createNode(5);

    // 打印原链表
    printf("Original linked list: ");
    printList(head);

    int n = 2; // 要删除倒数第n个节点
    head = removeNthFromEnd(head, n); // 调用函数删除目标节点

    // 打印删除后的链表
    printf("after deleting the last% d node: ", n);
    printList(head);

    return 0; // 程序结束
}

C 实现

#include <iostream> // 标准输入输出流库
using namespace std;

// 定义链表节点结构
struct ListNode {
    int val;           // 节点的值
    ListNode* next;    // 指向下一个节点的指针
    ListNode(int x) : val(x), next(nullptr) {} // 构造函数初始化节点
};

// 删除链表的倒数第n个节点
ListNode* removeNthFromEnd(ListNode* head, int n) {
    // 创建一个哑节点(dummy),方便操作链表头部
    ListNode* dummy = new ListNode(0);
    dummy->next = head; // 哑节点的next指向链表头部
    ListNode *fast = dummy, *slow = dummy; // 初始化快慢指针都指向哑节点

    // 让fast指针先移动n+1步,使得fast和slow之间间隔n个节点
    for (int i = 0; i <= n; i++) {
        fast = fast->next; // fast每次向前移动一步
    }

    // 快慢指针同时移动,直到fast到达链表末尾
    while (fast != nullptr) {
        fast = fast->next; // fast向前移动一步
        slow = slow->next; // slow向前移动一步
    }

    // 此时,slow指向要删除节点的前一个节点
    ListNode* temp = slow->next; // 保存要删除的节点
    slow->next = slow->next->next; // 跳过目标节点,完成删除
    delete temp; // 释放目标节点的内存

    // 返回链表的新头节点
    ListNode* newHead = dummy->next; // 哑节点的next即为新头节点
    delete dummy; // 释放哑节点的内存
    return newHead; // 返回新链表的头节点
}

// 辅助函数:打印链表
void printList(ListNode* head) {
    while (head != nullptr) {       // 遍历链表直到尾节点
        cout << head->val << " -> "; // 打印当前节点的值
        head = head->next;          // 移动到下一个节点
    }
    cout << "NULL" << endl; // 打印链表末尾标记
}

// 主函数
int main() 
{
    // 创建链表 1 -> 2 -> 3 -> 4 -> 5
    ListNode* head = new ListNode(1);
    head->next = new ListNode(2);
    head->next->next = new ListNode(3);
    head->next->next->next = new ListNode(4);
    head->next->next->next->next = new ListNode(5);

    // 打印原链表
    cout << "Original linked list: ";
    printList(head);

    int n = 2; // 要删除倒数第n个节点
    head = removeNthFromEnd(head, n); // 调用函数删除目标节点

    // 打印删除后的链表
    cout << "after deleting the last " << n << " node: ";
    printList(head);

    return 0; // 程序结束
}

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

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

相关文章

【python】爬去二手车数据 未完成

技术方案 python selenium 先下载Microsoft Edge WebDriver Microsoft Edge WebDriver 官网 先看一下自己的edge版本 搜索到版本然后下载自己的版本 安装依赖 pip install seleniumimport time from selenium import webdriverdriver webdriver.Edge(executable_pathr&qu…

在鸿蒙应用中 Debug 对开发者的帮助

文章目录 摘要引言Debug 的意义与挑战案例&#xff1a;页面渲染性能优化中的 Bug 排查Debug 过程详解问题定位问题解决优化布局与渲染逻辑 代码详细讲解示例代码详细讲解1. 导入必要模块2. 数据生成3. 使用虚拟列表组件items 属性itemHeight 属性renderItem 属性 4. 返回完整组…

YOLO-学习笔记

文章目录 划分区域筛选需要的目标聚类NMS(非极大值抑制) YOLOV1代码解析特征提取层 He 初始化&#xff08;He Initialization&#xff09;问题He 初始化的原理解释&#xff1a; 检测头train()输入处理函数target_processYOLO-V2基于Anchor的偏移量Ground Truth (GT)&#xff1a…

虚拟机VMware安装OpenWrt镜像

前提已经安装VMware Workstation Pro,我使用的是VM16 一.下载OpenWrt系统固件 固件有很多种&#xff0c;我选择下面这个链接的固件: Index of /releases/23.05.3/targets/x86/64/ 下载好之后将红框的镜像解压成绿框的镜像 二.安装转换工具 转换工具下载地址&#xff1a;htt…

Java设计模式 —— 【创建型模式】原型模式(浅拷贝、深拷贝)详解

文章目录 前言原型模式一、浅拷贝1、案例2、引用数据类型 二、深拷贝1、重写clone()方法2、序列化 总结 前言 先看一下传统的对象克隆方式&#xff1a; 原型类&#xff1a; public class Student {private String name;public Student(String name) {this.name name;}publi…

go使用mysql实现增删改查操作

1、安装MySQL驱动 go get -u github.com/go-sql-driver/mysql2、go连接MySQL import ("database/sql""log"_ "github.com/go-sql-driver/mysql" // 导入 mysql 驱动 )type Users struct {ID intName stringEmail string }var db *sql.DBfu…

vulnhub靶场【哈利波特】三部曲之Fawkes

前言 这次的靶机与前面不同&#xff0c;这里涉及到缓冲区溢出等 这个靶机也让我知道薄弱点了&#xff0c;缓冲溢出这方面之前接触少&#xff0c;所以刚拿到这个靶机打开后&#xff0c;人蒙了&#xff0c;在网上查阅好多资料&#xff0c;也只是浅学一下&#xff0c;这里主要也是…

mac下安装Ollama + Open WebUI + Llama3.1

本文介绍mac下安装Ollama Open WebUI Llama3.1 8b具体步骤。 目录 推荐配置Ollama Open WebUI Llama3.1简介安装Ollama安装Open WebUI 推荐配置 m1以上芯片&#xff0c;16g内存&#xff0c;20g以上硬盘空间 Ollama Open WebUI Llama3.1简介 Ollama: 下载&#xff0c;管理…

Android 图形系统之四:Choreographer

Choreographer 是 Android 系统中负责帧同步的核心组件&#xff0c;它协调输入事件、动画和绘制任务&#xff0c;以确保界面以固定频率&#xff08;通常是每 16ms&#xff0c;一帧&#xff09;流畅渲染。通过管理 VSYNC 信号和调度任务&#xff0c;Choreographer 是实现流畅 UI…

如何构建一个可扩展、全球可访问的 GenAI 架构?

你有没有尝试过使用人工智能生成图像&#xff1f; 如果你尝试过&#xff0c;你就会知道&#xff0c;一张好的图像的关键在于一个详细具体的提示。 我不擅长这种详细的视觉提示&#xff0c;所以我依赖大型语言模型来生成详细的提示&#xff0c;然后使用这些提示来生成出色的图像…

ceph手动部署

ceph手动部署 一、 节点规划 主机名IP地址角色ceph01.example.com172.18.0.10/24mon、mgr、osd、mds、rgwceph02.example.com172.18.0.20/24mon、mgr、osd、mds、rgwceph03.example.com172.18.0.30/24mon、mgr、osd、mds、rgw 操作系统版本&#xff1a; Rocky Linux release …

【iOS】多线程基础

【iOS】多线程基础 文章目录 【iOS】多线程基础前言进程与线程进程进程的状态进程的一个控制结构进程的上下文切换 线程为什么要用线程什么是线程线程和进程的关系线程的上下文切换 线程和进程的优缺点 小结 前言 笔者由于对于GCD不是很了解&#xff0c;导致了项目中网络请求哪…

ArraList和LinkedList区别

文章目录 一、结构不同二、访问速度三、插入和删除操作的不同1、决定效率有两个因素&#xff1a;数据量和位置。2、普遍说法是“LinkedList添加删除快”&#xff0c;这里是有前提条件的 四、内存占用情况五、使用场景六、总结 一、结构不同 LinkedList&#xff1a;它基于双向链…

芯片测试-RF中的S参数,return loss, VSWR,反射系数,插入损耗,隔离度等

RF中的S参数&#xff0c;return loss, VSWR&#xff0c;反射系数&#xff0c;插入损耗&#xff0c;隔离度 &#x1f4a2;S参数&#x1f4a2;&#x1f4a2;S11与return loss&#xff0c;VSWR&#xff0c;反射系数&#x1f4a2;&#x1f4a2;S21&#xff0c;插入损耗和增益&#…

arkTS:持久化储存UI状态的基本用法(PersistentStorage)

arkUI&#xff1a;持久化储存UI状态的基本用法&#xff08;PersistentStorage&#xff09; 1 主要内容说明2 例子2.1 持久化储存UI状态的基本用法&#xff08;PersistentStorage&#xff09;2.1.1 源码1的相关说明2.1.1.1 数据存储2.1.1.2 数据读取2.1.1.3 动态更新2.1.1.4 显示…

《Django 5 By Example》阅读笔记:p455-p492

《Django 5 By Example》学习第 16 天&#xff0c;p455-p492 总结&#xff0c;总计 38 页。 一、技术总结 1.myshop (1)打折功能 使用折扣码实现&#xff0c;但是折扣码是手动生成的&#xff0c;感觉实际业务中应该不是这样的。 (2)推荐功能 使用 Redis 做缓存&#xff0…

深入浅出:开发者如何快速上手Web3生态系统

Web3作为互联网的未来发展方向&#xff0c;正在逐步改变传统互联网架构&#xff0c;推动去中心化技术的发展。对于开发者而言&#xff0c;Web3代表着一个充满机遇与挑战的新领域&#xff0c;学习和掌握Web3的基本技术和工具&#xff0c;将为未来的项目开发提供强大的支持。那么…

otter 高可用策略

关于otter高可用在设计之初&#xff0c;提供了这样几个基本的需求&#xff1a; 1.网络不可靠&#xff0c;异地机房尤为明显. 2.manager/node的jvm不可靠&#xff0c;需要考虑异常crash情况 3.node的jvm不可靠&#xff0c;需要考虑异常crash的情况 4.数据库不可靠&#xff0c;需…

C底层 函数栈帧

文章目录 一&#xff0c;什么是寄存器 二&#xff0c;栈和帧 前言 我们在学习c语言程序的时候&#xff0c;是不是有很多的疑问&#xff0c;如 1&#xff0c;为什么形参不可以改变实参 2&#xff0c;为什么我们编写程序的时候会出现烫烫烫......这个乱码 3&#xff0c;那些局…

力扣1382:将二叉搜索树便平衡

给你一棵二叉搜索树&#xff0c;请你返回一棵 平衡后 的二叉搜索树&#xff0c;新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法&#xff0c;请你返回任意一种。 如果一棵二叉搜索树中&#xff0c;每个节点的两棵子树高度差不超过 1 &#xff0c;我们就称这棵二…