单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。

链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素。由于不需要按顺序存储,链表在插入、删除数据元素时比顺序存储要快,但是在查找一个节点时则要比顺序存储要慢

使用链式存储可以克服顺序线性表需要预先知道数据大小的缺点,链表结构可以充分利用内存空间,实现灵活的内存动态管理。但是链式存储失去了数组随机存取的特点,同时增加了节点的指针域,空间开销较大。

下图就是最简单最一般的单向链表:

新增节点:

将值为element的新节点插入到第index的位置上。

首先要先找到索引为index-1的节点,然后生成一个数据为element的新节点newNode,并令index-1处节点的next指向新节点,新节点的next指向原来index处的节点。

删除节点:

删除第index个节点,第index节点是由index-1出的节点引用的,因此删除index的节点要先获取index-1处的节点,然后让index-1出节点的next引用到原index+1处的节点,并释放index处节点即可。

单向链表的Java实现

下面的程序分别实现了线性表的初始化、获取线性表长度、获取指定索引处元素、根据值查找、插入、删除、清空等操作。

package com.liuhao.algorithm;  
  
public class LinkList<T> {  
  
    // 定义一个内部类Node,代表链表的节点  
    private class Node {  
  
        private T data;// 保存数据  
        private Node next;// 指向下个节点的引用  
  
        // 无参构造器  
        public Node() {  
        }  
  
        // 初始化全部属性的构造器  
        public Node(T data, Node next) {  
            this.data = data;  
            this.next = next;  
        }  
    }  
  
    private Node header;// 保存头结点  
    private Node tail;// 保存尾节点  
    private int size;// 保存已含有的节点数  
  
    // 创建空链表  
    public LinkList() {  
        header = null;  
        tail = null;  
    }  
  
    // 已指定数据元素创建链表,只有一个元素  
    public LinkList(T element) {  
  
        header = new Node(element, null);  
        // 只有一个节点,header,tail都指向该节点  
        tail = header;  
        size++;  
    }  
  
    // 返回链表的长度  
    public int length() {  
        return size;  
    }  
  
    // 获取指定索引处的元素  
    public T get(int index) {  
        return this.getNodeByIndex(index).data;  
    }  
  
    //获取指定位置的节点  
    private Node getNodeByIndex(int index){  
        if(index < 0 || index > size-1){  
            throw new IndexOutOfBoundsException("索引超出线性表范围");  
        }  
          
        Node current = header;//从header开始遍历  
          
        for(int i=0; i<size && current!=null; i++,current=current.next){  
            if(i == index){  
                return current;  
            }  
        }  
          
        return null;  
    }  
      
    //按值查找所在位置  
    public int locate(T element){  
        Node current = header;  
          
        for(int i=0; i<size && current!=null; i++, current=current.next){  
            if(current.data.equals(element)){  
                return i;  
            }  
        }  
          
        return -1;  
    }  
      
    //指定位置插入元素  
    public void insert(T element, int index){  
      
        if(index < 0 || index > size){  
            throw new IndexOutOfBoundsException("索引超出线性表范围");  
        }  
          
        //如果是空链表  
        if(header == null){  
            add(element);  
        }  
        else{  
            //当index为0时,即在链表头处插入  
            if(0 == index){  
                addAtHead(element);  
            }  
            else{  
                Node prev = getNodeByIndex(index - 1);//获取前一个节点  
                //让prev的next指向新节点,新节点的next指向原来prev的下一个节点  
                prev.next = new Node(element, prev.next);  
                size++;  
            }  
        }  
    }  
  
      
    //在尾部插入元素  
    public void add(T element) {  
          
        //如果链表是空的  
        if(header == null){  
            header = new Node(element, null);  
              
            //只有一个节点,headwe,tail都该指向该节点  
            tail = header;  
        }  
        else{  
            Node newNode = new Node(element, null);//创建新节点  
            tail.next = newNode;//尾节点的next指向新节点  
            tail = newNode;//将新节点作为尾节点  
        }  
          
        size++;  
    }  
      
      
    //头部插入  
    public void addAtHead(T element){  
        //创建新节点,让新节点的next指向header  
        //并以新节点作为新的header  
        Node newNode = new Node(element, null);  
        newNode.next = header;  
        header = newNode;  
          
        //若插入前是空表  
        if(tail == null){  
            tail = header;  
        }  
          
        size++;  
    }  
      
    //删除指定索引处的元素  
    public T delete(int index){  
          
        if(index < 0 || index > size-1){  
            throw new IndexOutOfBoundsException("索引超出线性表范围");  
        }  
          
        Node del = null;  
          
        //若要删除的是头节点  
        if(index == 0){  
            del = header;  
            header = header.next;  
        }  
        else{  
            Node prev = getNodeByIndex(index - 1);//获取待删除节点的前一个节点  
              
            del = prev.next;//获取待删除节点  
              
            prev.next = del.next;  
              
            del.next = null;//将被删除节点的next引用置为空  
        }  
          
        size--;  
        return del.data;  
    }  
      
    //删除最后一个元素  
    public T remove(){  
        return delete(size - 1);  
    }  
      
    //判断线性表是否为空  
    public boolean isEmpty(){  
        return size == 0;  
    }  
      
    //清空线性表  
    public void clear(){  
        //将header,tail置为null  
        header = null;  
        tail = null;  
        size = 0;  
    }  
      
    public String toString(){  
          
        if(isEmpty()){  
            return "[]";  
        }  
        else{  
            StringBuilder sb = new StringBuilder("[");  
            for(Node current = header; current != null; current = current.next){  
                sb.append(current.data.toString() + ", ");  
            }  
              
            int len = sb.length();  
              
            return sb.delete(len-2, len).append("]").toString();  
        }  
          
    }  
}  

测试代码

package com.liuhao.test;  
  
import org.junit.Test;  
  
import com.liuhao.algorithm.LinkList;  
  
public class LinkListTest {  
  
    @Test  
    public void test() {  
  
        // 测试构造函数  
        LinkList<String> list = new LinkList("好");  
        System.out.println(list);  
  
        // 测试添加元素  
        list.add("ni");  
        list.add("没");  
        System.out.println(list);  
  
        // 在头部添加  
        list.addAtHead("五月");  
        System.out.println(list);  
  
        // 在指定位置添加  
        list.insert("摩卡", 2);  
        System.out.println(list);  
  
        // 获取指定位置处的元素  
        System.out.println("第2个元素是(从0开始计数):" + list.get(2));  
  
        // 返回元素索引  
        System.out.println("摩卡在的位置是:" + list.locate("摩卡"));  
        System.out.println("moka所在的位置:" + list.locate("moka"));  
  
        // 获取长度  
        System.out.println("当前线性表的长度:" + list.length());  
  
        // 判断是否为空  
        System.out.println(list.isEmpty());  
  
        // 删除最后一个元素  
        list.remove();  
        System.out.println("调用remove()后:" + list);  
  
        // 获取长度  
        System.out.println("当前线性表的长度:" + list.length());  
  
        // 删除指定位置处元素  
        list.delete(3);  
        System.out.println("删除第4个元素后:" + list);  
  
        // 获取长度  
        System.out.println("当前线性表的长度:" + list.length());  
  
        // 清空  
        list.clear();  
        System.out.println(list);  
  
        // 判断是否为空  
        System.out.println(list.isEmpty());  
    }  
  
}  

线性表的Java实现--链式存储(单向链表)的更多相关文章

  1. 线性表的Java实现--链式存储(双向链表)

    有了单向链表的基础,双向链表的实现就容易多了. 双向链表的一般情况: 增加节点: 删除节点: 双向链表的Java实现: package com.liuhao.algorithm;      publi ...

  2. 数据结构导论 四 线性表的顺序存储VS链式存储

    前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char nam ...

  3. 算法与数据结构(一) 线性表的顺序存储与链式存储(Swift版)

    温故而知新,在接下来的几篇博客中,将会系统的对数据结构的相关内容进行回顾并总结.数据结构乃编程的基础呢,还是要不时拿出来翻一翻回顾一下.当然数据结构相关博客中我们以Swift语言来实现.因为Swift ...

  4. 线性表的顺序存储和链式存储的实现(C)

    //线性表的顺序存储 #include <stdio.h>typedef int DataType;#define MaxSize 15//定义顺序表typedef struct { Da ...

  5. c数据结构 -- 线性表之 复杂的链式存储结构

    复杂的链式存储结构 循环链表 定义:是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环) 优点:从表中任一节点出发均可找到表中其他结点 注意:涉及遍历操作时,终止条件是判断 ...

  6. 线性表的顺序存储和链式存储c语言实现

    一.线性表的顺序存储 typedef int ElemType;typedef struct List { ElemType *data;//动态分配 ,需要申请空间 int length; }Lis ...

  7. Java实现链式存储的二叉查找树(递归方法)

    二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字: 2. 若右子树非空,则右子树上所有节点关键字值 ...

  8. Java实现链式存储的二叉树

    二叉树的定义: 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要 ...

  9. Java实现线性表-顺序表示和链式表示

    顺序表示和链式表示的比较: 1.读写方式:顺序表可以顺序存取,也可以随机存取:链表只能从表头顺序存取元素: 2.逻辑结构与物理结构:顺序存储时,逻辑上相邻的元素其对应的物理存储位置也相邻:链式存储时, ...

随机推荐

  1. Spring事务杂谈

    1. 什么是事务 事务就是以一种可控的方式,对资源进行的一组操作,保证了资源在事务前后,始终应处于被期待的正确的状态.比如不会受到宕机等原因的影响.事务本身,具有如下4种属性-ACID.(所以说事务是 ...

  2. basic algorithm- 20190416-20190425

    binary search 14.https://www.lintcode.com/problem/first-position-of-target/description 74.https://ww ...

  3. python怎么解决用matplotlib画图时无法显示中文的问题或者出现方框的问题

    在中文前面加上u 加上u以后如果还不可以显示中文显示了方框 就直接加上 from pylab import mpl mpl.rcParams['font.sans-serif']=['SimHei']

  4. 运维监控之zabbix(yum安装)

    简介 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系统管理员快速定位 ...

  5. PHP之string之addcslashes()函数使用

    addcslashes (PHP 4, PHP 5, PHP 7) addcslashes - Quote string with slashes in a C style addcslashes - ...

  6. JDK1.7新特性(3):java语言动态性之脚本语言API

    简要描述:其实在jdk1.6中就引入了支持脚本语言的API.这使得java能够很轻松的调用其他脚本语言.具体API的使用参考下面的代码: package com.rampage.jdk7.chapte ...

  7. 微信小程序(一)--简单的介绍

    转自:https://developers.weixin.qq.com/miniprogram/dev/index.html 响应的数据绑定 框架的核心是一个响应的数据绑定系统. 整个小程序框架系统分 ...

  8. 八、window搭建spark + IDEA开发环境

    本文将简单搭建一个spark的开发环境,如下: 1)操作系统:window os 2)IDEA开发工具以及scala插件(IDEA和插件版本要对应): 2-1)IDEA2018.2.1:https:/ ...

  9. 撩课-Java每天5道面试题第12天

    91.如何提升数据查询的效率? 1.首先检查表的结构是否合理, 因为采用多表查询的时候, 看主外键的引用关系是否适当. 如果不适当则重新设置表结构. 如果是应用中的系统, 则不需要更改表的字段, 只更 ...

  10. 撩课-Java每天10道面试题第3天

    21.final, finally, finalize的区别 1.final修饰符(关键字). 被final修饰的类, 就意味着不能再派生出新的子类, 不能作为父类而被子类继承. 因此一个类不能既被a ...