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. 深入浅出AQS源码解析

    最近一直在研究AQS的源码,希望可以更深刻的理解AQS的实现原理.虽然网上有很多关于AQS的源码分析,但是看完以后感觉还是一知半解.于是,我将自己的整个理解过程记录下来了,希望对大家有所帮助. 基本原 ...

  2. Gradle系列之构建脚本基础

    原文发于微信公众号 jzman-blog,欢迎关注交流. 前面两篇文章分别介绍了 Gradle 基础知识以及 Groovy 相关基础知识,这也是学习 Gradle 所必需了解的,文章链接如下:: Gr ...

  3. 绘图和可视化知识图谱-《利用Python进行数据分析》

    所有内容整理自<利用Python进行数据分析>,使用MindMaster Pro 7.3制作,emmx格式,源文件已经上传Github,需要的同学转左上角自行下载或者右击保存图片. 其他章 ...

  4. Burp Suite Decoder Module - 解码模块

    官方参考链接:https://portswigger.net/burp/documentation/desktop/tools/decoder 该模块主要进行编码和解码,支持编码方式有:Plain,U ...

  5. linux : 新服务器部署项目要做的事

    环境:阿里云服务器两台,一台web,一台db,系统centos7. 用户用外网访问web server ,web server 再去访问db server. 1 阿里云控制台进入系统2 SSH进入系统 ...

  6. @RequestMapping中的produces的作用和使用方式

    转发:原博客 1.他的作用是指定返回值类型和返回值编码 2.consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html; 一 ...

  7. 【Floyd算法+贪心】 travel 计蒜客 - 45275

    题目: 有 n 个景点,从一个景点 i 旅行到另一个景点 j 要花费 Ai,j=Aj,i(n≤100),现在给出由 m(≤1e5) 个景点的组成序列 A,求:在所有 "有子序列 A 的序列中 ...

  8. 从LocalDateTime序列化探讨全局一致性序列化

    日拱一卒无有尽,功不唐捐终入海. 楔子 前两周发了三篇SpringSecurity和一篇征文,这周打算写点简单有用易上手的文章,换换脑子,休息一下. 今天要写的是这篇:从LocalDateTime序列 ...

  9. js 从目标数组中过滤掉 一个数组元素,

    标题描述的有点僵硬,大概需求是,从目标数组中过滤掉我想要删除的元素集合,这里使用的是遍历+过滤器的组合,很方便,做个笔记! let old = ["AE_CN_SUPER_ECONOMY_G ...

  10. 关系数据可视化gephi

    表示对象之间的关系,可通过gephi软件实现,软件下载官方地址https://gephi.org/users/download/ 如何来表示两个对象之间的关系? 把对象变成点,点的大小.颜色可以是它的 ...