数据结构与算法(六)--链表的遍历,查询和修改,删除操作

2023-09-22 11:54:41

一、前言

上篇文章我们了解了链表的概念以及链表底层的搭建以及向链表中添加元素的操作。本次我们继续学习链表剩余的操作:遍历,查询和修改、删除操作。

二、链表查询以及遍历

①获得链表的第index(0-based)个位置的元素(不常用,仅做练习)

和add不一样的是,add我们是要找到第Index的前一个元素,所以,我们起点从dummyHead开始,然后遍历index次。而get不同,get就是要第Index上的元素,所以我们可以直接从dummyHead.next开始,其实就是"索引"为0的位置开始,遍历index次,这样就可以找到第Index个位置的元素了。

	//获取index索引上的元素
	public T get(int index) {
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("get failed;index should >= 0 or index < size");
		}
		Node cur = dummyHead.next;
		for (int i = 0; i < index; i++) {
			cur = cur.next;
		}
		return cur.data;
	}

那么同理有了get(),我们就可以添加更方便的一些方法,例如getFirst()和getLast():

    public T getFirst(){
		return get(0);
	}
	public T getLast(){
		return get(size - 1);
	}

②、查找链表中是否有元素e

这个就有点特殊了,因为我们现在不知道具体要在哪个“索引”操作,所以我们需要从头开始遍历
那么我们可以使用for循环,和之前一样,从"索引"为0的位置开始找,然后遍历size次,这种方式是可行的。但是这里呢我们采用while循环来遍历链表,因为这种方式我们后面会用到很多,那么代码非常的简单,如下所示,这其实就是链表的遍历:

	public boolean contains(T t){
		Node cur = dummyHead.next;
		//只要cur不为NULL,其实就是到最后一个节点
		while (cur != null){
			if(cur.data == t){
				return true;
			}
			cur = cur.next;
		}
		return false;
	}

其实用for循环不使用size变量也是可以做到的,思路和上面的while循环大同小异:

for(Node cur = dummyHead.next; cur != null; cur = cur.next){...}

类似的,我们也可以在toString()中去遍历我们的链表:

	@Override
	public String toString(){
		StringBuilder stringBuilder = new StringBuilder();
		Node cur = dummyHead.next;
		while (cur != null){
			stringBuilder.append(cur + "->");
			cur = cur.next;
		}
		stringBuilder.append("NULL");
		return stringBuilder.toString();
	}

三、链表的更新(不常用,仅做练习)

更新操作其实和上面的get操作非常类似,只是将返回元素变为了直接更新,这里不做赘述,代码如下:

	public void set(int index, T t){
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("get failed;index should >= 0 or index < size");
		}
		Node cur = dummyHead.next;
		for (int i = 0; i < index; i++) {
			cur = cur.next;
		}
		cur.data = t;
	}

四、链表元素的删除

那么有了之前的基础,我们删除元素是很简单的,我们仍然需要用到虚拟头结点。
例如,我想要删除"索引"为2位置上的元素:
在这里插入图片描述
那么对于删除链表元素来说,我们和添加元素是一样的,我们需要找到被删除元素的前一个元素,所以我们仍然需要prev,而prev.next就是我们要删除的节点delNode了:
在这里插入图片描述
然后 我们要做的就是将prev.next = delNode.next:,这样做完之后,我们链表顺着这个next走。1的next就是3,3的next就是4,从某种意义来说等同于将2这个节点删除了。
在这里插入图片描述
当然为了方便我们java回收这个空间,我们应该手动将2这个删除节点的next指向null,delNode.next =null;

然后我们便可以开始进行代码设计了,删除代码非常的简单:

	public T remove(int index){
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("get failed;index should >= 0 or index < size");
		}
		Node prev = dummyHead;
		for(int i = 0;i<index;i++){
			prev = prev.next;
		}
		Node ret = prev.next;
		prev.next = ret.next;
		ret.next = null;
		size --;
		return ret.data;
	}

那么remove完成后,我们同理可以设计一些方便的remove方法,即removeFirst,removeLast等:

	public T removeFirst(){
		
		return remove(0);
	}	
	
	public T removeLast(){
		return remove(size - 1);
	}

我们可以试试:

	public static void main(String[] args) {
		LinkedList<Integer> integerLinkedList = new LinkedList<>();
		for (int i = 0; i < 5; i++) {
			integerLinkedList.addFirst(i);
			System.out.println(integerLinkedList);
		}
		integerLinkedList.add(666,2);
		System.out.println(integerLinkedList);
		System.out.println(integerLinkedList.remove(2));
		System.out.println(integerLinkedList.removeLast());
		System.out.println(integerLinkedList);
	}

在这里插入图片描述
五、链表的时间复杂度分析

添加操作

  • addLast(e) ----------- O(n)
  • addFirst(e) ---------- O(1)
  • add(index,e) -----------O(n/2)=O(n)

所以综上来看,链表的添加操作是O(n)的

删除操作

  • removeLast(e) ----------- O(n)
  • removeFirst(e) ---------- O(1)
  • remove(index,e) -----------O(n/2)=O(n)

所以综上来看,链表的删除操作也是O(n)的

修改操作

  • set(index,e) -------O(n)

查找操作

  • get(index) ----------- O(n)
  • contains(e) ----------- O(n)

但是如果查询的是链表头元素的时候,此时时间复杂度是O(1)

所以综上来看,链表的查找操作也是O(n)的

那么综上分析,链表的CRUD操作时间复杂度全是O(n)的,确实在时间复杂度的角度上分析,它确实整体不如数组,因为数组具有随机访问的能力,但是链表底层上就不支持。但是我们发现如果只对链表头进行增删操作,时间复杂度均为O(1),所以我们的链表它适合做的事情是不去修改,而对于查也不能去查任意的元素,可以只查链表头的元素.并且在增加和删除的时候,只能对链表头进行操作。且链表本身就是动态的数据结构,是非常节省内存空间的。

更多推荐

在电脑上怎么分类管理笔记?支持分类整理的电脑云笔记软件

对于大多数上班族而言,在使用电脑办公时,随手记录工作笔记是一个非常常见的场景。无论是会议纪要、工作总结还是项目计划,记录下每一次思考和灵感是提高工作效率的关键。然而,随着时间的推移,电脑上记录的笔记内容逐渐增多,如何高效地管理这些笔记就成为了一项重要的任务。那么,在电脑上如何分类管理笔记呢?支持分类整理的电脑云笔记软件

gateway之过滤器(Filter)详解

文章目录什么是过滤器过滤器的种类局部过滤器代码示例全局过滤器代码示例总结什么是过滤器在SpringCloud中,过滤器(Filter)是一种关键的组件,用于在微服务架构中处理和转换传入请求以及传出响应。过滤器位于服务网关或代理中,并通过拦截请求和响应流量来提供各种功能。过滤器在请求的不同生命周期阶段执行特定的操作,例如

全套办公软件Office 2019 mac专业版功能

Microsoftoffice2019BetaforMac是一款办公软件套装,它包含常用的办公应用程序,如Word、Excel、PowerPoint和Outlook等。office2019Beta版本是一个测试版本,旨在让用户提前体验下一个版本的office套件,以便用户可以了解并评估新功能和改进。office2019

【新品发布】洛微科技全新工业级高性能 D系列 TOF相机D3重磅上线!

近日,洛微科技对外发布新款高性能D系列TOF相机D3,这是一款专为工业环境中高性能操作设计的3DTOF智能相机。D3基于行业领先的SonyDepthSense®像素技术开发,具有毫米级测量精度、VGA深度分辨率、抗环境光能力强、软/硬件多触发方式、HDR适配多种复杂场景等特性,结合独有的点云过滤以及图像处理算法,实时输

人机交互——对话管理

​人机交互中的对话管理主要是指在人机交互过程中,对交互的对话内容和流程进行管理,以实现自然、流畅、高效的交互效果。对话管理包括对话状态追踪、对话策略优化等多个方面。对话状态追踪是指对当前对话的状态进行跟踪,例如对用户输入的语句进行语义理解和分析,从而判断出用户的意图和需求,并据此进行相应的回应和交互。对话状态追踪可以帮

220V转12V芯片-交流45v-265v输入,固定12v输出峰值电流600MA

标题:220V转12V芯片,实现宽电压输入和固定12V输出摘要:本文介绍了一款具备宽电压输入范围(45V-265V)和固定12V输出的220V转12V芯片。该芯片内置了650V高压MOS管,并通过CS电阻调节输出电流,最大输出电流峰值可达600mA。该芯片采用SOP-8封装,具有较小的尺寸和方便的焊接特性。在电子设备中

电工-三极管主要参数(直流、交流、极限)

三极管主要参数(直流、交流、极限)三极管的主要参数分为三种,即直流参数、交流参数和极限参数,下面分别介绍:直流参数·共发射极直流放大倍数β=Ic/Ib·集电极—基极反向截止电流Icbo,Ic=0时,基极和集电极间加规定反向电压时的集电极电流。Icb越小,说明三极管的集电结质量越好。·集电极—发射极反向截止电流Iceo(

webpack常用配置与性能优化插件

webpack是一个流行的前端项目构建工具(打包工具),可以解决当前web开发中所面临的困境。提供了友好的模块化支持,以及代码压缩混淆、处理js兼容问题、性能优化等强大的功能,从而让程序员把工作的重心放到具体的功能实现上,提高开发效率和项目的可维护性。直接代码加注释/***各个webpack版本之间存在一定差异,经常报

ADB底层原理

介绍adb的全称为AndroidDebugBridge,就是起到调试桥的作用。通过adb我们可以在Eclipse/AndroidStudio中方便通过DDMS来调试Android程序,说白了就是debug工具。adb是androidsdk里的一个工具,用这个工具可以直接操作管理android模拟器或者真实的androi

Sftp服务安全评估

1认识SFTPFTP(SSH文件传输协议)和FTP(文件传输协议)是两种用于文件传输的协议,它们在工作原理、安全性和配置方面有很大的差异。1)工作原理:FTP:FTP使用两个独立的连接(控制连接和数据连接)来传输文件。控制连接用于发送命令和处理身份验证,而数据连接用于传输文件内容。SFTP:SFTP是通过SSH协议进行

安全基础 --- nodejs沙箱逃逸

nodejs沙箱逃逸沙箱绕过原理:沙箱内部找到一个沙箱外部的对象,借助这个对象内的属性即可获得沙箱外的函数,进而绕过沙箱前提:使用vm模块,实现沙箱逃逸环境。(vm模式是nodejs中内置的模块,是nodejs提供给使用者的隔离环境)目的:拿到process模块实现沙箱逃逸,拿到目标(1)Function构造函数实现源

热文推荐