《恋上数据结构与算法》第1季:双向链表实现(超详细笔记,图文并茂)

news2025/2/27 23:07:07

数据结构与算法的学习笔记目录:《恋上数据结构与算法》的学习笔记 目录索引

双向链表

  • 一、双向链表
  • 补充【List接口 和 AbstractList抽象类】
  • 二、设计双向链表
  • 三、双向链表的实现
    • 1. 查询节点
    • 2. 插入节点
    • 3. 删除节点
    • 4. 清空节点
  • 四、双向链表 vs 动态数组

一、双向链表

  1. 与单向链表不同的是,双向链表多了 从后向前的指向
  2. 使用双向链表可以加快节点的查询速度

补充【List接口 和 AbstractList抽象类】

List接口:

public interface List<E> {
	static final int ELEMENT_NOT_FOUND = -1;
	/**
	 * 清除所有元素
	 */
	void clear();

	/**
	 * 元素的数量
	 * @return
	 */
	int size();

	/**
	 * 是否为空
	 * @return
	 */
	boolean isEmpty();

	/**
	 * 是否包含某个元素
	 * @param element
	 * @return
	 */
	boolean contains(E element);

	/**
	 * 添加元素到尾部
	 * @param element
	 */
	void add(E element);

	/**
	 * 获取index位置的元素
	 * @param index
	 * @return
	 */
	E get(int index);

	/**
	 * 设置index位置的元素
	 * @param index
	 * @param element
	 * @return 原来的元素ֵ
	 */
	E set(int index, E element);

	/**
	 * 在index位置插入一个元素
	 * @param index
	 * @param element
	 */
	void add(int index, E element);

	/**
	 * 删除index位置的元素
	 * @param index
	 * @return
	 */
	E remove(int index);

	/**
	 * 查看元素的索引
	 * @param element
	 * @return
	 */
	int indexOf(E element);
}

AbstractList抽象类:

public abstract class AbstractList<E> implements List<E>  {
	/**
	 * 元素的数量
	 */
	protected int size;
	/**
	 * 元素的数量
	 * @return
	 */
	public int size() {
		return size;
	}

	/**
	 * 是否为空
	 * @return
	 */
	public boolean isEmpty() {
		 return size == 0;
	}

	/**
	 * 是否包含某个元素
	 * @param element
	 * @return
	 */
	public boolean contains(E element) {
		return indexOf(element) != ELEMENT_NOT_FOUND;
	}

	/**
	 * 添加元素到尾部
	 * @param element
	 */
	public void add(E element) {
		add(size, element);
	}
	
	protected void outOfBounds(int index) {
		throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
	}
	
	protected void rangeCheck(int index) {
		if (index < 0 || index >= size) {
			outOfBounds(index);
		}
	}
	
	protected void rangeCheckForAdd(int index) {
		if (index < 0 || index > size) {
			outOfBounds(index);
		}
	}
}

二、设计双向链表

  • 与单项链表不同的是,双向链表增加了 last 指针,同时节点增加了向前的指向 prev 属性
    在这里插入图片描述
public class DoubleLinkedList<E> extends AbstractList<E> {
	private Node<E> first;
	// 增加 last 节点
	private Node<E> last;

	private static class Node<E>{
		E element;
		Node<E> next;
		//增加上一个节点
		Node<E> prev;
		public Node(Node<E> prev, E element, Node<E> next){
			this.element = element;
			this.next = next;
			this.prev =prev;
		}
	}
}

三、双向链表的实现

  • 相较于单向链表,双向链表需要重写实现 查询节点 、 插入节点 、 删除节点 、 清空节点 这四个方法

1. 查询节点

  1. 因为是双向链表,所以查询节点可以从后向前查询
  2. 查询节点的方向可以根据节点总数的一半,即 size >> 1 为分隔,索引小于一半的从前向后查询,索引大于等于一半的从后往前查询
private Node<E> node(int index){
	rangeCheck(index);
	// 根据节点数量的一半进行区分
	if(index < (size >> 1)){
		Node<E> node =first;
		// 小于size >> 1的节点从前向后查询
		for (int i = 0; i < size; i++){
			node = node.next;
		}
		return node;
	}else {
		Node<E> node = last;
		// 大于等于 size >> 1 的节点从后向前查询
		for(int i = size-1; i > index; i--){
			node = node.prev;
		}
		return node;
	}
}

2. 插入节点

  1. 插入节点,就是在两个节点之间加入新的节点
  2. 首先要找到需要插入节点位置的原节点,原节点要成为新节点的下一个节点
  3. 原节点的上一个节点成为新节点的上一个节点
    在这里插入图片描述
public void add(int index,E element){
	// 新节点后的下一个节点, 就是原链表 index 位置的节点
	Node<E> next = node(index);
	// 新节点后的上一个节点, 就是原链表 index-1 位置的节点
	Node<E> prev = next.prev;
	// 创建新节点, 新节点的上一个节点时prev, 新节点的下一个节点是next
	Node<E> node = new Node<>(prev, element, next);
	// next的上一个节点是新节点
	next.prev = node;
	// prev的下一个节点是新节点
	prev.next = node;
}
  1. 当新插入的节点索引为 0 时, 需要做特殊处理,因为 first 引用着 0 节点
    在这里插入图片描述
public void add(int index,E element){
	// 新节点后的下一个节点, 就是原链表 index 位置的节点
	Node<E> next = node(index);
	// 新节点后的上一个节点, 就是原链表 index-1 位置的节点
	Node<E> prev = next.prev;
	// 创建新节点, 新节点的上一个节点时prev, 新节点的下一个节点是next
	Node<E> node = new Node<>(prev, element, next);
	// next的上一个节点是新节点
	next.prev = node;
	// 当next.prev == null时, 说明新添加节点的索引是0
	if (next.prev == null) {
		// 此时first应该指向引得索引
		first = node;
	}else {
		// prev的下一个节点是新节点
		prev.next = node;
	}
}
  1. 当插入的节点位置是链表的最后时,因为 last 属性引用最后一个节点,所以也需要做特殊处理

【已有元素:index = size && last != null】
在这里插入图片描述
【没有元素:index = size && last == null】
在这里插入图片描述

  1. 最终的插入方法如下代码,
public void add(int index,E element){
	rangeCheckForAdd(index);
	// 如果 index == size, 说明添加的索引是最后位置
	if (index == size) {
		Node<E> oldLast = last;
		// 新创建节点, 新节点的next = null, prev = 之前的最后节点
		last = new Node<>(oldLast, element, null);
		// 旧链表的最后节点的下一个节点是 新创建的节点
		oldLast.next = last;
	}else {
		// 添加新节点后的下一个节点
		Node<E> next = node(index);
		// 添加新节点后的上一个节点
		Node<E> prev = next.prev;
		// 创建新节点, 新节点的上一个节点时prev, 新节点的下一个节点是next
		Node<E> node = new Node<>(prev, element, next);
		// next的上一个节点是新节点
		next.prev = node;
		// 当next.prev == null时, 说明新添加节点的索引是0
		if (next.prev == null) {
			first = node;
		}else {
			// prev的下一个节点是新节点
			prev.next = node;
		}
	}
	size++;
}

3. 删除节点

  1. 删除节点, 只需要让被删除节点的前一个节点与后一个节点之间相互联系, 同时去掉所有对被删除节点引用即可
  2. 需要注意的是, 查出 第0个节点最后一个节点要特殊处理

【删除中间元素】
在这里插入图片描述
【删除头/尾节点元素】
在这里插入图片描述
在这里插入图片描述

public E remove(int index){
	// 需要删除的节点
	Node<E> node = node(index);
	// 删除节点的前一个节点
	Node<E> prev = node.prev;
	// 删除节点的后一个节点
	Node<E> next = node.next;
	// 删除节点, 只需要去掉对这个节点的引用即可
	// 如果prev == null, 说明删除的是第一个节点
	if (prev == null) {
		first = next;
	}else {
		prev.next = next;
	}
	// 如果next == null, 说明删除的是最后一个节点
	if (next == null) {
		last = prev;
	}else {
		next.prev = prev;
	}
	size--;
	return node.element;
}

4. 清空节点

  • 清空节点,需要将 first 和 last 的引用全部断开

在这里插入图片描述

public void clear() {
	size = 0;
	first = null;
	last = null;
}

四、双向链表 vs 动态数组

在这里插入图片描述

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

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

相关文章

JUC包(java.util.concurrent)下的常用子类

文章目录前言一、对象锁juc.locks包二、原子类三、四个常用工具类3.1 信号量 Semaphore3.2 CountDownLatch总结前言 博主个人社区&#xff1a;开发与算法学习社区 博主个人主页&#xff1a;Killing Vibe的博客 欢迎大家加入&#xff0c;一起交流学习~~ 一、对象锁juc.locks包 …

单元测试入门篇

一、单元测试是什么&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。在测试金字塔模型中处于最底层&#xff1a; 整个金字塔模型代表着越上层的测试集成度越高&#xff0c;执行速度越慢&#xff0c;越下层…

2014-2020年国有大型商业银行和全国股份制商业银行绿色信贷数据

数据集名称&#xff1a;国有大型商业银行和全国股份制商业银行绿色信贷数据 时间范围&#xff1a;2014-2020年 数据来源&#xff1a;商业银行历年业绩报告和社会责任报告 相关说明&#xff1a;绿色金融是指为支持环境改善、应对气候变化和资源节约高效利用的经济活动&#x…

C语言练习之递归实现n的k次方

文章目录前言一、思路二、代码以及运行截图1.代码2.运行截图总结前言 使用C语言递归计算N的k次方 一、思路 求n的k次方的原理就是&#xff1a; n^k nn……*n&#xff08;k个n进行相乘&#xff09; 可以得到一个公式&#xff1a; f(k){1k0n∗f(k)k>0f(k) \left\{\begin{…

利用Redis来实现分布式锁

Redis命令 SET 命令有个 NX 参数可以实现「key不存在才插入」&#xff0c;可以用它来实现分布式锁&#xff1a; 如果 key 不存在&#xff0c;则显示插入成功&#xff0c;可以用来表示加锁成功&#xff1b;如果 key 存在&#xff0c;则会显示插入失败&#xff0c;可以用来表示…

PLC中ST编程的自定义功能块

右键单击——添加对线——程序组织单元 弹出对话框 修改名称&#xff0c;选择功能块&#xff1b; VAR_INPUT&#xff1a;输入变量&#xff1b;VAR_OUTPUT:输出变量&#xff1b;VAR&#xff1a;局部变量&#xff1b; 创建一个闪烁功能块&#xff0c;可输入亮和灭的时间&#xff…

基于SSM的高校共享单车管理系统【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86468380 主要使用技术 SpringSpringMVCMybatisEasyUIJqueryMysql 功能介绍 系统用户管理&#xff1a; 用户管理&#xff1a;可以添加、修改、删除、检索用户信息&#xff08;头像、用户账…

PSO粒子群算法微电网优化调度(微电网孤岛运行优化调度)matlab程序

PSO粒子群算法微电网优化调度&#xff08;微电网孤岛运行优化调度&#xff09;matlab程序 【含风电、光伏、微型燃机、储能蓄电池、燃料电池】 参考文献&#xff1a;基于改进粒子群算法的微电网优化调度 摘 要&#xff1a;当今全球普遍面临着能源危机和环境污染的加重&#xf…

全国工企专利匹配数据(1998-2014)

1、数据来源&#xff1a;国家统计局&#xff08;工业企业数据&#xff09;、专利数据来源于国家知识产权局。 2、时间跨度&#xff1a;1998-2014 3、区域范围&#xff1a;全国 4、指标说明&#xff1a; 包含以下指标&#xff1a; 公开&#xff08;公告&#xff09;日、申请…

基于SSM的毕业设计管理系统【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86469261 主要使用技术 SpringSpringMVCMybatisBootstrapJqueryMysql 功能介绍 本系统的用户可以分为三种&#xff1a;管理员、教师、学生。 管理员&#xff1a;导师管理、学生管理&#x…

【雷达通信】合成孔径雷达地面运动目标检测技术研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

第九章 持续集成CI:基于GitHub的Action回归验证

第九章 持续集成CI&#xff1a;基于GitHub的Action回归验证 持续集成可以认为是一种优秀的开发实践&#xff0c;它可以在代码变更的时候及时反映代码状态。持续集成需要服务器的支持&#xff0c;可以考虑通过 gitlib ci 或者 jenkins 自己搭建持续集成服务器&#xff0c;更好的…

基于SSM的地方文创特产在线商城【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86468623 主要使用技术 SpringSpringMVCMybatisBootstrapMysql 功能介绍 前台&#xff1a; 注册登录&#xff1a;普通用户可进行注册登录&#xff1b; 商品显示&#xff1a;游客可查看今日…

在CentOS 7.7 x86_64上为python 2.7.5安装pip的靠谱方法

我的虚拟机是CentOS 7.7 x86_64系统&#xff0c;对应的python默认版本是2.7.5&#xff0c;但是没有安装pip&#xff0c;不方便安装第三方模块。 我想为为它安装pip工具&#xff0c;发现现有的安装方法都行不通了&#xff0c;比如先安装easy_install&#xff0c;再通过easy_inst…

矩阵分解算法

文章目录0 前言1. 矩阵分解原理1.1 LFM公式推导LFM损失函数算法关键代码实现1.2 BiasSVD1.3 SVD参考0 前言 在协同过滤算法中 我们知道近邻协同过滤算法的显著缺点&#xff1a; 没有充分利用物品本身的属性信息处理稀疏矩阵能力很弱&#xff0c;泛化能力很弱 为了解决以上问…

Flink-窗口概念以及窗口API使用

6.3 窗口 6.3.1 窗口的概念 存储桶 水位线只是用来推动窗口的关闭&#xff0c;但不决定数据分到哪个窗口 6.3.2 窗口的分类 按照驱动类型分类 时间窗口计数窗口 按照窗口分配数据的规则分类 滚动窗口&#xff1a;参数为窗口的大小 滑动窗口&#xff1a;参数为窗口大小&am…

[CVPR2022] Debiased Learning from Naturally Imbalanced Pseudo-Labels

Debiased Learning from Naturally Imbalanced Pseudo-Labels 要点&#xff1a; 1、伪标签&#xff1a;由经过标记源数据训练的分类器&#xff0c;对未标记目标数据做出的置信预测&#xff0c;被广泛应用于使模型适应未标记数据&#xff0c;例如半监督学习 2、由于固有的数据…

bat批处理脚本大全

目录 1、echo 2、注释 3、常见cmd命令 4、参数与变量 5、for循环 6、函数 7、数组 在windows上编程或者制作一些小工具&#xff0c;少不了使用批处理脚本&#xff0c;而且在各种开发环境搭建中我们经常会看到批处理脚本。批处理脚本以cmd命令为基础&#xff0c;增加一些变量和参…

【学生毕业设计】基于web学生信息管理系统网站的设计与实现(13个页面)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

【计算机毕业设计】50.课程设计管理系统

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 网络的广泛应用给生活带来了十分的便利。所以把课程设计管理与现在网络相结合&#xff0c;利用JSP技术建设课程设计管理系统&#xff0c;实现课程设计管理的信息化。则对于进一步提高课程设计管理发展&#x…