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. Django框架04 /模板相关、别名/反向解析/路由分发

    Django框架04 /模板相关.别名/反向解析/路由分发 目录 Django框架04 /模板相关.别名/反向解析/路由分发 1. 语法 2. 变量/万能的点 3 . 过滤器 4. 标签Tags 5. ...

  2. 用Python演奏音乐

    目录 背景 准备 安装mingus 下载并配置fluidsynth 下载soundfont文件 分析 乐谱格式 乐谱解析 弹奏音乐 添加伴奏 保存音乐 完整程序 背景 笔者什么乐器也不会,乐理知识也只 ...

  3. 用matplotlib画简单折线图示例

    例1 import numpy as np import matplotlib.pyplot as plt from scipy import stats rx1 = np.array([54.52, ...

  4. Mybatis执行流程浅析(附深度文章推荐&面试题集锦)

    首先推荐一个简单的Mybatis原理视频教程,可以作为入门教程进行学习:点我 (该教程讲解的是如何手写简易版Mybatis) 执行流程的理解 理解Mybatis的简单流程后自己手写一个,可以解决百分之 ...

  5. 从连接器组件看Tomcat的线程模型——BIO模式

    在高版本的Tomcat中,默认的模式都是使用NIO模式,在Tomcat 9中,BIO模式的实现Http11Protocol甚至都已经被删除了.但是了解BIO的工作机制以及其优缺点对学习其他模式有有帮助 ...

  6. 《2020版Linux云计算学习图谱》帮你提升80%专业技能,在线免费领

    2亿人在家办公.视频会议的需求,给钉钉后台系统带来巨大压力.据了解,钉钉在通过阿里云紧急扩容1万台服务器后,再度扩容1万台云服务器. 受疫情影响,在家办公需求暴涨.从29号开始到2月6日,腾讯会议每天 ...

  7. C++语法小记---少见的语法之一

    很少用,列出来,便于理解和熟悉!!! // 1.单独使用位域限定符 ::xxx() //调用全局函数xxx // 2.全局重载new和delete T* tmp = (T*)(::operator n ...

  8. MySQL的权限赋予

    MySQL 赋予用户权限命令的简单格式可概括为:grant 权限 on 数据库对象 to 用户 一.grant 普通数据用户,查询.插入.更新.删除 数据库中所有表数据的权利. grant selec ...

  9. shell变量子串

    表达式 说明 ${parameter} 返回变量$parameter的内容 ${#parameter} 返回变量$parameter内容的长度(按字符),也适用于特殊变量 ${parameter:of ...

  10. Android中Fragment生命周期和基本用法

    1.基本概念 1. Fragment是什么? Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fra ...