02-手撸链表

本篇是恋上数据结构第一季个人总结

借鉴https://juejin.im/post/6844904001478066183#heading-0

本人git https://github.com/bigeyes-debug/Algorithm

一丶链表定义

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

二丶链表设计

  • 创建LinkedList类,用来管理链表数据,其中的size属性记录存储数据的数量,first属性引用链表的第0个节点。

  • 创建私有类Node,其中的element属性用于存储节点,next属性用于指向链表中的下一个节点。

public class SIngleLinkedList<E>  extends
AbstractList<E>{
private Node<E> first;
private static class Node<E>{
E element;
Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}

接口

    // 节点的数量
int size();
// 是否为空
boolean isEmpty();
// 是否包含某个节点
boolean contains(E element);
// 添加节点到最后面
void add(E element);
// 返回index位置对应的节点
E get(int index);
// 设置index位置的节点
E set(int index, E element);
// 往index位置添加节点
void add(int index, E element);
// 删除index位置对应的节点
E remove(int index);
// 查看节点的位置
int indexOf(E element);
// 清除所有节点
void clear();

与动态数组的接口类似,我们可以抽取接口,然后实现,后面我们会提到

三丶链表的实现

3.1 构造方法

  • 链表的创建与动态数组不同,动态数组在构造时需要传入一个空间属性,来决定这个数组的容量。但链表节点是在添加时才创建的,内存地址不一定是连续的。所以链表不需要在单独设计构造方法,使用默认构造方法即可。

3.2 添加节点

  • 添加节点时候只需要到将最后节点的next指针指向新添加的节点
  • 当指定位置添加节点的时候,需要插入位置的前驱,
  • 然后用前驱的next指针指向新添加的节点,并且该结点next指向原来的结点
  • 需要特别注意的地方是当前插入位置是0时,因为他没没有前驱,需要做特殊处理



代码如下

    @Override
public void add(int index, E element) {
//检查越界 和动态数组类似
rangeCheck(index);
//特殊处理
if(index ==0) {
first=new Node<>(element, first);
}else {
// 获得前驱
Node <E>prev=node(index-1);
//上图的①②
prev.next=new Node<>(element, prev.next);
}
// TODO Auto-generated method stub
size++;
}
public void add(E element) {
// 节点添加到size位置, 即添加到最后面
add(size, element);
}

3.3 删除节点

  • 删除指定位置的节点,需要找到前驱
  • 用前驱next指向该位置节点的后继
  • 由于头结点没有前驱,所以我们仍需要对头结点的删除做特殊处理,只需要将头结点指向头结点的后继

   //这里我们用到了一个node函数,node是根据索引获取该节点的节点
public E remove(int index) {
rangeCheck(index);//检查越界
Node <E> node =first;
// TODO Auto-generated method stub
if(index==0) {
first=first.next;
}else {
//或得前驱
Node <E>prev=node(index -1);
//index位置的节点
node =prev.next;
//前驱的后继是index位置的后继
prev.next=prev.next.next;
}
size--;
return node.element;
}
    private Node<E> node(int index){
rangeCheck(index);
Node<E> node=first;
for(int i=0;i<index;i++) {
node=node.next;
}
return node;
}

3.4 修改节点

修改节点首先根据节点的索引找到节点,然后修改节点的元素,最后返回原来节点的元素的值

    public E set(int index, E element) {
Node <E> node=node(index);
E old=node.element;
node.element=(E) element;
return old;
// TODO Auto-generated method stub
}

3.5查找节点

3.5.1 根据下标查找

找到对应的节点, 取出元素即可。

public E get(int index) {
// TODO Auto-generated method stub
return node(index).element;
}

3.5.2 根据元素值查找

  • 查找指定元素的索引,需要遍历所有节点,找到节点对应的元素与执行元素相等即可。
  • 如果需要支持节点element为null,则需要分两种情况处理。

    一定要分开处理,,如果传入的element为null,调用equals方法会报错, 至于为什么用equlas方法,而不用==,自行百度Java基础
	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;
}

3.6 获取链表元素的个数

public int size() {
return size;
}

3.7 链表是否为空

public boolean isEmpty() {
return size == 0;
}

3.7 元素是否存在

public boolean contains(E element) {
return indexOf(element) != ELEMENT_ON_FOUND;
}

3.8 打印链表中存储的数据

@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size = ").append(size).append(", [");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(",");
}
string.append(node.element);
node = node.next;
}
string.append("]");
return string.toString();
}

四丶链表的复杂度

五丶代码优化

通过编写代码发现,链表和动态数组的接口一样,部分代码共用,他俩都属于线性表

我们可以优化代码,具体如下

实现list接口

package com.bigeyes;

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抽象类,实现list接口,并且共用代码在这实现

package com.bigeyes;

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); // System.out.println(siArrayList.javaze); } 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); } } }

02-java实现单链表的更多相关文章

  1. JAVA数据结构——单链表

    链表:一. 顺序存储结构虽然是一种很有用的存储结构,但是他有如下几点局限性:1. 因为创造线性表的时候已经固定了空间,所以当需要扩充空间时,就需要重新创建一个地址连续的更大的存储空间.并把原有的数据元 ...

  2. Java实现单链表的各种操作

    Java实现单链表的各种操作 主要内容:1.单链表的基本操作 2.删除重复数据 3.找到倒数第k个元素   4.实现链表的反转   5.从尾到头输出链表 6.找到中间节点 7.检测链表是否有环 8.在 ...

  3. java实现单链表的增删功能

    JAVA 实现单链表的增删功能 package linked; class LinkedTable{ } public class LinkedTableTest { public static vo ...

  4. 使用java实现单链表(转载自:https://www.cnblogs.com/zhongyimeng/p/9945332.html)

    使用java实现单链表----(java中的引用就是指针)转载自:https://www.cnblogs.com/zhongyimeng/p/9945332.html ? 1 2 3 4 5 6 7 ...

  5. 用Java实现单链表的基本操作

    笔试题中经常遇到单链表的考题,下面用java总结一下单链表的基本操作,包括添加删除节点,以及链表转置. package mars; //单链表添加,删除节点 public class ListNode ...

  6. java实现单链表常见操作

    一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashM ...

  7. Java实现单链表的快速排序和归并排序

    本文描述了LeetCode 148题 sort-list 的解法. 题目描述如下: Sort a linked list in O(n log n) time using constant space ...

  8. 数据结构——Java实现单链表

    一.分析 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.链表中的数据是以结点来表示的,每个结点由元素和指针构成.在Java中,我们可以将单链表定义成一个类,单链表的基 ...

  9. Java实现单链表翻转

    单链表翻转比方有例如以下链表: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3NoaXp0eQ==/font/5a6L5L2T/fontsize ...

  10. 用java实现单链表

    对于一个单链表来说,要求有最基本的数据节点以及一些重要的方法. 方法应该有增删改查.定位.输出.获取链表长度.排序.链表读入.链表输出.下面是我用java写的单链表 public class List ...

随机推荐

  1. python 面向对象专题(二):类的空间问题、类与对象之间的关系、类与类之间的关系

    https://www.cnblogs.com/liubing8/p/11308127.html 目录 Python面向对象02/类的空间问题.类与对象之间的关系.类与类之间的关系 1. 类的空间问题 ...

  2. POJ 1047 Round and Round We Go 最详细的解题报告

    题目链接:Round and Round We Go 解题思路:用程序实现一个乘法功能,将给定的字符串依次做旋转,然后进行比较.由于题目比较简单,所以不做过多的详解. 具体算法(java版,可以直接A ...

  3. 移除hmtl5 input type=“number”的step验证

    设置 step="any"例如:<input type="number" step="any" value=""& ...

  4. 008.Nginx静态资源

    一 Nginx静态资源概述 1.1 静态资源类型 Nginx作为静态资源Web服务器部署配置, 传输非常高效, 常常用于静态资源处理,请求以及动静分离.通常非服务器动态运行生成的文件属于静态资源. 类 ...

  5. Oracle数据库服务器更改计算机名称,导致监听服务打不开解决办法

    1.修改listener.ora和tnsnames.ora文件 文件路径为:C:\Oracle\Instanclient_11_2\network\admin # listener.ora Netwo ...

  6. node.js day01学习笔记:认识node.js

    Node.js(JavaScript,everywhere) 1.Node.js 介绍 1.1. 为什么要学习Node.js 企业需求 + 具有服务端开发经验更好 + front-end + back ...

  7. STL Stack(栈)学习笔记 + 洛谷 P1449 后缀表达式

    稍微看了看刘汝佳的白皮书,“实用主义”的STL实在是香到我了,而且在实验室大佬的推荐下我开始了stl的学习. 每篇附带一个题目方便理解,那行,直接开始. 毕竟是实用主义,所以就按照给的题目的例子来理解 ...

  8. 04 . Filebeat简介原理及配置文件和一些案例

    简介 Beats轻量型数据采集器 Beats 平台集合了多种单一用途数据采集器.它们从成百上千或成千上万台机器和系统向 Logstash 或 Elasticsearch 发送数据. Beats系列 全 ...

  9. 给我半首歌的时间,给你说明白Immutable List

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章. ...

  10. 解释Crypto模块,No module named "Crypto"

    1.pip install pycryptodome 2.Python\Python38\Lib\site-packages,找到这个路径,下面有一个文件夹叫做crypto,将小写c改成大写C就ok了 ...