【代码随想录】链表刷题

2023-11-04

很多重复的题参考:【代码随想录】双指针法刷题

理论基础

单链表:

双链表:

循环链表:

单链表节点的定义:

public class ListNode {
    public int val; // 节点的值
    public ListNode next; // 指向的下一个节点

    public ListNode() {}

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

移除链表元素

题目:203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

移除链表元素

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

递归:

public ListNode removeElements(ListNode head, int val) {
    if (head == null) return head;
    head.next = removeElements(head.next, val);
    return head.val == val ? head.next : head;
}

迭代:找到要删除节点的前一个节点,进行删除操作

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) return head;
        ListNode node = head;
        while (node != null && node.next != null) {
            if (node.next.val == val) node.next = node.next.next;
            else node = node.next;
        }
        return head.val == val ? head.next : head;
    }
}

设计链表

题目:707. 设计链表

动态单链表

动态单链表:(虚拟头节点)

class ListNode {
    int val;
    ListNode next;

    ListNode() {}

    ListNode(int val) {
        this.val = val;
    }
}

class MyLinkedList {
    int size; // 链表元素个数
    ListNode head; // 虚拟头节点

    // 初始化链表
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }

    // 获取第 index 个元素的值
    public int get(int index) {
        if (index < 0 || index >= size) return -1;
        ListNode cur = head; // 从头节点开始查找
        // 包含虚拟头节点, 所以查找第 index+1 个节点
        while (index-- >= 0) cur = cur.next;
        return cur.val;
    }

     // 在链表最前面插入一个节点
     public void addAtHead(int val) {
        addAtIndex(0, val);
    }

     // 在链表的最后插入一个节点
     public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    // 在第 index 个节点之前插入一个新节点
    // 如果 index 为 0,那么新插入的节点为链表的新头节点
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空
    public void addAtIndex(int index, int val) {
        if (index > size) return;
        if (index < 0) index = 0;
        size++;
        // 找到要插入节点的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
        // 插入操作
        ListNode newNode = new ListNode(val);
        newNode.next = pre.next;
        pre.next = newNode;
    }

    // 删除第 index 个节点
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        size--;
        // 找到要删除节点的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
		// 删除操作
        pre.next = pre.next.next;
    }
}

动态双向链表

动态双向链表:(虚拟头节点和虚拟尾节点)

class ListNode {
    int val;
    ListNode next, prev;

    ListNode(int x) {
        val = x;
    }
}

class MyLinkedList {
    int size;
    ListNode head, tail;

    public MyLinkedList() {
        size = 0;
        head = new ListNode(0); // 虚拟头节点
        tail = new ListNode(0); // 虚拟尾节点
        head.next = tail;
        tail.prev = head;
    }

    public int get(int index) {
        if (index < 0 || index >= size) return -1;
        ListNode cur = head;

        // 索引 < 一半从前往后找, 索引 > 一半从后前找
        if (index < size / 2) {
            for (int i = 0; i <= index; i++) cur = cur.next;
        } else {
            cur = tail;
            for(int i = 0; i <= size - 1 - index; i++) cur = cur.prev;
        }
        return cur.val;
    }

    public void addAtHead(int val) {
        ListNode newNode = new ListNode(val);
        newNode.next = head.next;
        head.next.prev = newNode;
        head.next =  newNode;
        newNode.prev = head;
        size++;
        // addAtIndex(0, val);
    }

    public void addAtTail(int val) {
        ListNode newNode = new ListNode(val);
        newNode.next = tail;
        newNode.prev = tail.prev;
        tail.prev.next = newNode;
        tail.prev = newNode;
        size++;
        // addAtIndex(size, val);
    }

    public void addAtIndex(int index, int val) {
        if (index > size) return;
        if (index < 0) index = 0;
        // 找到要添加的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
        // 添加操作
        ListNode newNode = new ListNode(val);
        newNode.next = pre.next; // 新节点的 next 指向
        newNode.prev = pre; // 新节点的 prev 指向
        pre.next.prev = newNode; // 后继的 prev 指向新
        pre.next = newNode; // 前驱的 next 指向新
        size++;
    }

    public void deleteAtIndex(int index) {
        if(index >= size || index < 0) return;
        // 找到要删除的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
        // 删除操作
        pre.next.next.prev = pre;
        pre.next = pre.next.next;
        size--;
    }
}

静态单链表

静态单链表:(虚拟头节点)

class MyLinkedList {
    final int N = 1010;
    int[] e = new int[N];
    int[] ne = new int[N];
    int h = 0; // 头节点
    int idx = 1; // 有虚拟头节点
    int size = 0; // 长度

    public MyLinkedList() {}
    
    public int get(int k) {
        if (k < 0 || k >= size) return -1;
        int t = ne[h];
        while (k -- > 0) t = ne[t];
        return e[t];
    }
    
    public void addAtHead(int val) {
        e[idx] = val;
        ne[idx] = ne[h];
        ne[h] = idx++;
        size++;
    }
    
    public void addAtTail(int val) {
        int t = h;
        while (ne[t] != 0) t = ne[t];
        e[idx] = val;
        ne[idx] = ne[t];
        ne[t] = idx++;
        size++;
    }
    
    public void addAtIndex(int k, int val) {
        if (k <= 0) addAtHead(val);
        else if (k == size) addAtTail(val);
        else if (k > 0 && k < size) {
            k --;
            int t = ne[h];
            while (k -- > 0) t = ne[t];
            e[idx] = val;
            ne[idx] = ne[t];
            ne[t] = idx++;
            size++;
        }
    }
    
    public void deleteAtIndex(int k) {
        if (k < 0 || k >= size) return;
        int t = h;
        while (k -- > 0) t = ne[t];
        ne[t] = ne[ne[t]];
        size--;
    }
}

反转链表

题目:206. 反转链表

双指针:

public ListNode reverseList(ListNode head) {
    ListNode cur = head, pre = null;
    while (cur != null) {
        ListNode tmp = cur.next;
        cur.next = pre;
        pre = cur;
        cur = tmp;
    }
    return pre;
}

递归:

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode node = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return node;
}

头插法:空间 O(n)

public ListNode reverseList(ListNode head) {
    ListNode res = null;
    for (ListNode x = head; x != null; x = x.next)
        res = new ListNode(x.val, res);
    return res;
}

两两交换链表中的节点

题目:24. 两两交换链表中的节点

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

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

迭代 + 虚拟头节点:

public ListNode swapPairs(ListNode head) {
	ListNode vn = new ListNode(0, head); // 虚拟头节点
	ListNode cur = vn, tmp = null;
	while (cur != null && cur.next != null && cur.next.next != null) {
		tmp = cur.next;
		cur.next = cur.next.next;
		tmp.next = cur.next.next;
		cur.next.next = tmp;
		cur = cur.next.next;
	}
	return vn.next;
}

递归:

public ListNode swapPairs(ListNode head) {
	if (head == null || head.next == null) return head;
	ListNode next = head.next;
	head.next = swapPairs(next.next);
	next.next = head;
	return next;
}

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

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

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

快慢指针:

  • 快指针先走 n 步,然后和慢指针同时开始走
  • 当快指针指向最后一个节点,此时慢指针指向的便是要删除的节点(的前一个节点)
public ListNode removeNthFromEnd(ListNode head, int n) {
	if (head == null || head.next == null) return null;
	ListNode fast = head, slow = head;
	while (n-- > 0) fast = fast.next; // 快指针先走 n 步
	if (fast == null) return head.next; // 删除第一个节点
	while (fast.next != null) {
		fast = fast.next;
		slow = slow.next;
	}
	slow.next = slow.next.next;
	return head;
}

快慢指针 + 虚拟头节点:

一般使用虚拟头节点可以不用处理特殊情况

public ListNode removeNthFromEnd(ListNode head, int n) {
	ListNode vn = new ListNode(0, head); // 虚拟头节点
	ListNode slow = vn, fast = vn;
	while (n-- > 0) fast = fast.next; // 快指针先走 n 步
	while (fast.next != null) {
		fast = fast.next;
		slow = slow.next;
	}
	slow.next = slow.next.next;
	return vn.next;
}

递归:

class Solution {
    int cur = 0;
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null) return null;
        head.next = removeNthFromEnd(head.next, n);
        cur++;
        if (cur == n) return head.next;
        return head;
    }
}

链表相交

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

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

双指针:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
	ListNode curA = headA, curB = headB;
	int lenA = 0, lenB = 0;
	// 求链表 A 的长度
	while (curA != null) { 
		lenA++; 
		curA = curA.next; 
	}
	// 求链表 B 的长度
	while (curB != null) { 
		lenB++; 
		curB = curB.next; 
	}
	curA = headA;
	curB = headB;
	// 链表 A 更长则让 A 多走, B 更长则让 B 多走,保证 A B 从同一位置开始比较
	if (lenA > lenB) for (int i = 0; i < lenA - lenB; i++) curA = curA.next;
	else for (int i = 0; i < lenB - lenA; i++) curB = curB.next;
	// 逐个比较 A 和 B 
	while (curA != null) {
		if (curA == curB) return curA;
		curA = curA.next;
		curB = curB.next;
	}
	return curA;
}

巧妙做法:

  • a 走完走 b,b 走完走 a,如果有相交点,必然会遇到
  • 如果没有相交点,最终会在相遇在 null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
	ListNode h1 = headA, h2 = headB;
	while (h1 != h2) {
		h1 = (h1 == null) ? headB : h1.next;
		h2 = (h2 == null) ? headA : h2.next;
	}
	return h1;
}

哈希:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
	Set<ListNode> set = new HashSet<>();
	ListNode p = headA;
	while (p != null) {
		set.add(p);
		p = p.next;
	}
	p = headB;
	while (p != null) {
		if (set.contains(p)) return p;
		p = p.next;
	}
	return null;
}

环形链表:快慢指针

题目:141. 环形链表 - 力扣(LeetCode)

给你一个链表的头节点 head ,判断链表中是否有环。

快慢指针:

public boolean hasCycle(ListNode head) {
	ListNode slow = head, fast = head;
	while (fast != null && fast.next != null) {
		slow = slow.next;
		fast = fast.next.next;
		if (slow == fast) return true;
	}
	return false;
}

哈希:

public boolean hasCycle(ListNode head) {
	Set<ListNode> set = new HashSet<>();
	while (head != null && !set.contains(head)) {
		set.add(head);
		head = head.next;
	}
	return head != null;
}

环形链表 II**

题目:142. 环形链表 II - 力扣(LeetCode)

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

快慢指针 判断环 + 找环入口:

public ListNode detectCycle(ListNode head) {
	ListNode slow = head, fast = head;
	// 快慢指针判断是否有环
	while (fast != null && fast.next != null) {
		slow = slow.next;
		fast = fast.next.next;
		if (slow == fast) { // 有环, 找环的起始点
			fast = head; // 快指针从头开始
			while (fast != slow) {
				fast = fast.next;
				slow = slow.next;
			}
			return fast;
		}
	}
	return null;
}

哈希:

public ListNode detectCycle(ListNode head) {
	Set<ListNode> set = new HashSet<>();
	while (head != null && !set.contains(head)) {
		set.add(head);
		head = head.next;
	}
	return head;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【代码随想录】链表刷题 的相关文章

  • Python实现串口通信(pyserial)

    Python实现串口通信 pyserial pyserial模块封装了对串口的访问 兼容各种平台 安装 pip insatll pyserial 初始化 简单初始化示例 import serial ser serial Serial com
  • MySQL数据同步ES的4种方法,你能想到几种?

    大家好 我是老三 这期给大家分享一个电商中常见的场景 MySQL数据同步Elasticsearch 大家应该都在各种电商网站检索过商品 那么检索商品一般都是通过什么实现呢 搜索引擎Elasticsearch 那么问题来了 商品上架 数据一般
  • python爬虫系列1--方案概述

    爬虫技能树 爬虫进阶必须 http www yeayee com article 6569383 1 html 0 requests 模块 beautifulsoup模块 css选择器语法 re正则模块 http头编写 cookies js
  • Ansible自动化运维_超详细

    Ansible自动化运维 自动化运维工具简介 Puppet 自动运维工具特点 Saltstack 自动运维工具特点 Ansible 自动运维工具特点 Ansible 运维工具原理 Ansible 管理工具安装配置 Ansible 工具参数详
  • FastJson解析继承/解析多态/反序列化去解析json

    我这个是在和外围系统调用时用到的 所以我不可能让他们去改返回的报文内容 也就是有的方法里说用SerializerFeature WriteClassName 定义一个interface或者class 其实都一样 public interfa
  • vi 编辑器总结

    创建文件 vi 一 进入vi的命令 vi filename 打开或新建文件 并将光标置于第一行首 vi n filename 打开文件 并将光标置于第n行首 vi filename 打开文件 并将光标置于最后一行首 vi pattern f
  • springboot-2.3.x最新版源码阅读环境搭建-基于gradle构建(全网首发)

    springboot 2 3 x最新版源码阅读环境搭建 基于gradle构建 全网首发 文章目录 springboot 2 3 x最新版源码阅读环境搭建 基于gradle构建 全网首发 一 前言 二 环境准备 三 下载源码 四 开始构建 五
  • 堆排序详解

    堆排序是必须要会手写的 背景介绍 堆是一种非线性数据结构 大顶堆 每个结点的值都大于或等于其左右孩子结点的值 小顶堆 每个结点的值都小于或等于其左右孩子结点的值 原理 1 从最后一个非叶子结点开始 从左到右 从上到下 与父节点进行交换 构建

随机推荐

  • 【编程测试题】保卫方案

    题目描述 战争游戏的至关重要环节就要到来了 这次的结果将决定王国的生死存亡 小B负责首都的防卫工作 首都位于一个四面环山的盆地中 周围的n个小山构成一个环 作为预警措施 小B计划在每个小山上设置一个观察哨 日夜不停的瞭望周围发生的情况 一旦
  • java三种注释_java注释的三种形式分别是什么

    java注释的三种形式分别是 1 单行注释 如 单行注释 2 多行注释 如 多行注释 3 文档注释 如 author JAVA的注释共有三种形式 单行注释 多行注释 文档注释 推荐教程 java课程 1 单行注释public class o
  • vue项目第四天

    使用elementui tabplane组件实现历史访问记录组件的二次封装
  • 关于windows11安装vc6.0闪退解决问题

    前两天刚升级成为windows11用户 可是突然发现windows11安装了vc6 0打开就闪退 最后才发现是windows11不兼容 解决方法如下 1 打开安装包找到如下文件 2 找到下面文件 3 找到MSDEV EXE文件 4 将MSD
  • 浏览器Uncaught QuotaExceededError错误(localStorage超出限额)

    Web Storage网络存储 HTML5的Web Storage网络存储是指网络应用程序用于在网络浏览器存储方法和通讯协议 支持持久数据存储 类似于Cookie 以及window local存储 网络存储又分为localStorage本地
  • 数电基础一:原码、反码和补码

    一 原理和计算 1 原码 在数字电路中 我们用逻辑电路输出的高低电平表示二进制码1 0 我们有时候需要对正数和负数进行操作 但是在二进制逻辑电路中只有0和1 并没有负号 所以我们在数值的最高位添0表示正数 添1表示负数 这样的数就叫原码 2
  • nacos配置中心的命名空间&配置集&配置id&配置分组

    命名空间 配置集 配置id 配置分组 命名空间 用作配置隔离 一般每个微服务一个命名空间 默认public 默认新增的配置都在public空间下 开发 测试 开发可以用命名空间分割 properties每个空间有一份 也可以为每个微服务配置
  • 震源球(沙滩球)

    震源球的三个重要参数 走向 strike 倾角 dip 滑动角 rake 走向 strike 断层的走向是断层面和水平面的交线 它有两个方向 相差180 为了明确起见 规定选取站在上盘面对下盘向右看的方向为断层面走向 记作 其取值范围为 0
  • 使用vscode进行远程调试

    官方调试手册 vscode官方调试手册 1 安装python扩展 如果是远程连接的话 一定要在ssh上启用扩展 不然创建基于python的配置文件时就会提示 无python扩展 2 新建配置文件 并修改参数 点击左侧第四个按钮 运行与调试
  • 小破孩&小屁妮

    偶闻 小破孩放出限量版情侣衫 毫不犹豫地就订了一套 很PP的 周末穿着大街上走了一圈 嘿 怪吸引眼球的
  • 当当网图书分析系统

    当当网图书分析系统 此系统有详细的录屏 下面只是部分截图 需要看完整录屏联系博主 系统开发语言python 框架为django 数据库mysql 分为爬虫和可视化分析
  • 力扣75.颜色分类 && 用异或swap时的注意事项。

    问题描述 在做 力扣75 颜色分类时候遇到的问题 荷兰国旗问题 代码正常写 但最后提交出现多次错误 代码 class Solution public void swap int a int b a b b a a b void sortCo
  • ERROR: Cannot create variant 'android-lint' after configuration ':sdk:debugRuntimeElements' has been

    最近项目添加model的时报错 看着错误信息眼熟 在此记录一下解决方法 错误信息 ERROR Cannot create variant android lint after configuration sdk debugRuntimeEl
  • thinkphp5学习路程 六 实现分页功能

    实现分页的功能具体的就是这个 paginate paginate 10 20 代表的含义就是一页显示10条数据 显示20页 public function test 查询数据库 result Db table user gt where i
  • 疯狂的联邦学习!研究员年薪百万?

    码农不容易 我这十几年一直在学习 停都停不下来 因为互联网技术发展真的造化弄人 上学那会儿 老师说C 有前途 因为大多数的企业都用它来写服务器程序 过了两年突然原来这个世界是Java的 遂挑灯恶补Spring 然而 技术永远在诞生新的 概念
  • python进行rar、tar、unzip解压

    参考文章链接 https blog csdn net qq 22865879 article details 120849457 1 python进行rar解压 1 需要使用Python的rarfile工具包 下载地址 http sourc
  • 成功打破 GPT-4 上限,新版 Claude 横空出世!

    公众号关注 GitHubDaily 设为 星标 每天带你逛 GitHub 前 OpenAI 团队成员在离职后 创办了 Anthropic 公司 今年 3 月份的时候 该公司推出一款名为 Claude 的应用 试图与 ChatGPT 一争高下
  • 前端工程化之Webpack优化

    打不垮我的 将使我更加坚强 尼采 大家好 我是 柒八九 好久没更文了 其实这段时间 一直没闲着 在准备一些比较重要的东西 忙着跑步 忙着学习 忙着xx 总之就是 一直在忙着 从未停歇 虽然 这段时间 没有文章的发布 其实 在私底下 已经有不
  • [教程]AMD芯片用VirtualBox安装MacOS虚拟机

    您的赞 是小熊更新的动力 本教程非常的简单 只需要几个步骤即可轻松安装好 效果图片 目前 大部分教程都是使用intel的芯片 Vmware软件进行安装macos 但实际上 使用VirtualBox安装MacOS同样也是一件简单的事情 笔者使
  • 【代码随想录】链表刷题

    链表 理论基础 移除链表元素 设计链表 动态单链表 动态双向链表 静态单链表 反转链表 两两交换链表中的节点 删除链表的倒数第 N 个节点 链表相交 环形链表 快慢指针 环形链表 II 很多重复的题参考 代码随想录 双指针法刷题 理论基础