首页 > 编程语言 >【Java】链表

【Java】链表

时间:2024-08-01 22:28:11浏览次数:18  
标签:node head Java next 链表 element 节点

1. 含义

链表 是一种 链式存储 的线性表,所有元素的内存地址不一定是连续的

 2. 基本方法

1. size() : int        //返回链表长度

2. isEmpty() : boolean        // 判空

3. clear() : void        //清除所有元素

4. contains(E element) : boolean        //判断是否包含此元素

5. get(int index) : E        //返回该下标处的元素

6. set(int index, E) : E        //修改该下标处的元素

7. add(E element) : void        //末尾添加元素

8. add(int index, E) : void        //指定下标处添加元素

9. remove(int index) : E        //移除指定下标处的元素

10. indexof(E element) : int        //返回该元素的下标

3. 内部实现

1. Node节点设计
private static class Node<E> {
    // 存储元素
    E element;
    // 存储下一个节点
	Node<E> next;
    // 构造方法
	public Node(E element, Node<E> next) {
		this.element = element;
		this.next = next;
	}
}
2. 方法设计
 1. size()
    public int size() {
		return size;
	}
2. isEmpty()
	public boolean isEmpty() {
		 return size == 0;
	}
3. clear()
	public void clear() {
		size = 0;
		first = null;
	}
4. contains(E element)
	public boolean contains(E element) {
        // 返回下标如果不为常数-1则存在,反之不存在
		return indexOf(element) != ELEMENT_NOT_FOUND;
	}
5. get(int index)
	public E get(int index) {
		return node(index).element;
	}
    // 用于获取index位置对应的节点对象
	private Node<E> node(int index) {
        // 判断下标是否越界
		rangeCheck(index);
		Node<E> node = first;
		for (int i = 0; i < index; i++) {
			node = node.next;
		}
		return node;
	}
6. set(int index, E)
	public E set(int index, E element) {
		Node<E> node = node(index);
		E old = node.element;
		node.element = element;
		return old;
	}
7. add(E element)
	public void add(E element) {
		add(size, element);
	}
8. add(int index, E)
    public void add(int index, E element) {
        // 判断下标是否越界
	    rangeCheckForAdd(index);
	    if (index == size) { // 往最后面添加元素
	    	Node<E> next = node(index); 
	    	last = new Node<>(element, next);
	    	if (oldLast == null) { // 这是链表添加的第一个元素
		    	first = last;
		    } else {
		       	oldLast.next = last;
		    }
	    } else {
            Node<E> prev = node(index - 1);
		    Node<E> next = node(index); 
		    Node<E> node = new Node<>(element, next);		
		    if (prev == null) { // index == 0
			    first = node;
		    } else {
			    prev.next = node;
		    }
	    }	
	    size++;
    }
9. remove(int index)
    public E remove(int index) {
		rangeCheck(index);
        Node<E> node = first;
        // 将first指向下一个节点
        if(index == 0){
            first = first.next;
        } else {
            // 获取前一个节点
            Node<E> prev = node(index - 1);
            node = prev.next;
		    prev.next = node.next;
        }
		size--;
		return node.element;
	}
10. indexof(E element)
	public int indexOf(E element) {
		if (element == null) {
			Node<E> node = first;
			for (int i = 0; i < size; i++) {
				if (node.element == null) return i;
				node = node.next;
			}
		} else {
			Node<E> node = first;
			for (int i = 0; i < size; i++) {
				if (element.equals(node.element)) return i;
				node = node.next;
			}
		}
		return ELEMENT_NOT_FOUND;
	}

4. 练习题目

237. 删除链表中的节点 - 力扣(LeetCode)

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

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

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

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

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

        示例 1:

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

示例 2:

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

分析

因为链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。只需删除给定的节点,所以可以巧妙利用下一个值来覆盖要删除的节点,并把删除节点指向下个节点的下个节点,从而达到删除此节点的效果。

题解

class Solution {
    public void deleteNode(ListNode node) {
        // 让下一个节点的值覆盖掉要删除节点的值
        node.val = node.next.val;
        // 让要删除的节点指向下个节点个下个节点
        node.next = node.next.next;
    }
}
206. 反转链表 - 力扣(LeetCode)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

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

示例 2:

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

示例 3:

输入:head = []
输出:[]

分析

方法一:迭代法

        一直循环到head为空则结束

方法二:递归法

reverseList(head.next)实现了将后面节点反转的功能,再将head的下一个节点的下一个节点指向头节点head,最后将head节点的next指向空,实现反转。

题解

方法一:迭代法

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode newHead = null;
        while(head != null){
            // 临时节点指向下一个节点
            ListNode temp = head.next;
            // 将头节点的next指向新节点
            head.next = newHead;
            // 将新节点指向头节点
            newHead = head;
            // 最后将头节点指向临时节点
            // 实现了从头挨个把头节点拿出来放在新节点最后的翻转效果
            head = temp;
        }
        return newHead;
    }
}

 方法二:递归法

class Solution {
    public ListNode reverseList(ListNode head) {
        // 头节点为空或头节点的下一个指向为空,直接返回
        if(head == null || head.next == null) return head;
        else{
            // 将head后面的链表用递归法实现反转
            ListNode newHead = reverseList(head.next);
            // 将倒数第二个节点指向头节点
            head.next.next = head;
            // 将头节点的next指向空
            head.next = null;
            return newHead;
        }
    }
}
141. 环形链表 - 力扣(LeetCode)

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

分析

利用快慢指针,两指针初始位置不同,移动距离不同,如果快指针和慢指针相遇,则有环,如果快指针走到最后都没有相遇,则无环。

题解

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null) return false;
        // 定义快慢指针,初始位置不同
        ListNode slow = head;
        ListNode fast = head.next;
        // fast不为空且下一个不为空时循环
        while(fast != null && fast.next != null){
            // 快慢指针相遇
            if(slow == fast) return true;
            // 慢走一,快走二
            slow = slow.next;
            fast = fast.next.next;
        }
        return false;
    }
}

 

标签:node,head,Java,next,链表,element,节点
From: https://blog.csdn.net/2301_80395772/article/details/140855597

相关文章

  • 基于Java+SpringBoot+Vue的电竞交互管理系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • JavaScript (八)——JavaScript 作用域和事件
    目录JavaScript 作用域JavaScript局部作用域JavaScript全局变量JavaScript变量生命周期HTML中的全局变量JavaScript 事件HTML事件常见的HTML事件JavaScript可以做什么?JavaScript 作用域作用域是可访问变量的集合。在JavaScript中,作用域为可访问变......
  • JavaScript(十二)——JavaScript for 循环和while循环
    目录JavaScript for循环不同类型的循环For循环For/In循环JavaScript while循环while循环语法实例do/while循环语法实例比较for和whileJavaScript for循环循环可以规定代码块执行指定的次数。不同类型的循环JavaScript支持不同类型的循环:for......
  • java基础6—抽象类、接口、枚举
    1.抽象类1.1简介        由于继承这个显著特点,我们可以将子类设计的更加具体,而父类更加一般化,通用化。父类可以封装不同子类的共同特征或者共同行为。而有的时候,父类中封装的方法无法具体完成子类中需要的逻辑,因此我们可以将此方法设计成抽象方法,即使用关键字abstra......
  • dbnet crnn java中文ocr识别
    TableofContentsAboutGettingStartedResultContactAbout完整项目:https://github.com/jiangnanboy/dbnet_crnn_java本项目利用java,javacv,onnx以及djl矩阵计算等技术加载文本检测模型dbnet与文本识别模型crnn,完成ocr的识别推理。包含模型的完整项目请从右侧relea......
  • java的诞生
    java的诞生C语言C语言(1972诞生)优点:贴近硬件,运行极快,效率极高操作系统,编译器,数据库,网络系统等都使用C语言开发缺点:指针和内存管理C++C++(1982诞生)面向对象兼容C图形领域、游戏等javajava(1995诞生)简单性面向对象可移植性(writeonce,runanywhe)高性能(即时编译)分......
  • Java面试题合集(持续更新)
    1、redis缓存穿透,缓存击穿以及缓存雪崩问题和对应的解决方案缓存穿透:当客户端访问一个不存在的数据,这个数据在缓存和数据库中都不能命中,如果有大量的这种穿过缓存直接访问数据库的请求,就会给数据库带来很大压力。解决思路:缓存空对象:当发现数据库中也没有该数据时,我们把这......
  • 全网最强Java面试题 很详细了!!!
    一、缓存面试官:什么是缓存穿透 ?怎么解决?候选人:(穿透无中生有Key,布隆过滤NULL隔离)嗯~~,我想一下缓存穿透是指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致DB挂掉。这种情况大概率是遭到了攻......
  • Java/SpringCloud/RabbitMq/无感实现消息携带用户信息 --- 逻辑讲解、代码实现、图形
    一、需求:依据黑马商城hmall为例,用户下单创建订单后,交易服务trade-service向交换机topic发送消息,交换机topic路由到队列,购物车服务cart-service监听此队列,实现自动清空购物车。改造下单功能,将基于OpenFeign的清理购物车同步调用,改为基于RabbitMQ的异步通知:定义t......
  • Java基础知识分享(二)相关练习题
    写在前面大家前面的方法和数组学的怎么样了,快来看看这些题你能不能快速地说出答案,数组和方法在Java学习中还是非常重要的,快来检测你的薄弱点在哪,及时查漏补缺!填空题1.数组会在内存中开辟一块连续固定大小的空间,每个空间相当于之前的一个变量,称为数组的元素。数组的长度一经确定......