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. hihoCoder 1041 国庆出游 最详细的解题报告

    题目来源:国庆出游 解题思路(下面是大神的写的): 把题目中的序列称作S,树称作T.那么对于S中的任意节点x,x的子孙节点如果在S出现的话,那么这个子孙节点的位置是有一定要求的:x的所有子孙节点在S中 ...

  2. JVM详解之:java class文件的密码本

    目录 简介 一个简单的class ClassFile的二进制文件 class文件的密码本 magic version 常量池 描述符 access_flags this_class和super_cla ...

  3. StringTable结构以及基本调优

    StringTable底层实现类似于HashTable,由数组和链表组成,数组又称为桶数组.比如有这样一段代码: public class Demo4 { public static void mai ...

  4. 第七章:Android动画深入分析

    7.1 View动画 View动画的作用对象是View,它支持四种动画效果,分别是平移动画,缩放动画,旋转动画和透明动画. 帧动画也属于View动画,但是帧动画的表现形式和上面的四种变换效果不太一样. ...

  5. “git pull” 强制覆盖本地文件

    放弃本地修改,使用服务器代码覆盖本地的Git命令如下: $ git fetch --all $ git reset --hard origin/master $ git pull 使用master分支 ...

  6. django admin后台管理功能的学习

    1.简要说明 用过Django框架的童鞋肯定都知道,在创建完Django项目后,每个app下,都会有一个urls.py文件,里边会有如下几行: from django.contrib import a ...

  7. 使用ImpromptuInterface反射库方便的创建自定义DfaGraphWriter

    在本文中,我为创建的自定义的DfaGraphWriter实现奠定了基础.DfaGraphWriter是公开的,因此您可以如上一篇文章中所示在应用程序中使用它,但它使用的所有类均已标记为internal ...

  8. 数据治理工具调研之DataHub

    1.项目简介 Apache Atlas是Hadoop社区为解决Hadoop生态系统的元数据治理问题而产生的开源项目,它为Hadoop集群提供了包括数据分类.集中策略引擎.数据血缘.安全和生命周期管理在 ...

  9. 细说websocket快速重连机制

    文|马莹莹 网易智慧企业web前端开发工程师 引言 在一个完善的即时通讯应用中,websocket是极其关键的一环,它为web应用的客户端和服务端提供了一种全双工的通信机制,但由于它本身以及其底层依赖 ...

  10. js:事件(注册、解绑、DOM事件流、事件对象、事件委托)

    1.注册事件 (1)传统方式注册事件 <body> <button id="b1">请点击</button> <script> va ...