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

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

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

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

新增节点:

将值为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. hiho#1449 重复旋律6 求长度为k的串最大次数 后缀自动机

    题目传送门 题目大意:求长度为k的串的最大次数,把k从1到length的所有答案全部输出. 思路: 这道题放在$SAM$里就是求长度$k$对应的所有$right$集中最大的大小. 我们以$aabab$ ...

  2. OAuth机制原理(开放授权机制)

    1.简述 OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全.开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的. 国 ...

  3. 如何调用npm已经安装在全局位置的模块

    参考链接  https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders npm install xxx -g ...

  4. 解析Excel----ExcelHelper

    public static class ExcelHelper { /// <summary> /// 获取单元格的值 /// </summary> /// <param ...

  5. Nginx 基于客户端 IP 来开启/关闭认证

    前些日子帮助公司在搭建了一个内部资源的导航页面,方便公司员工访问各种常用的系统.因为这个页面包含一些敏感信息,我们希望对其做认证,但仅当从外网访问的时候才开启,当从公司内网访问的时候,则无需输入账号密 ...

  6. Hadoop学习笔记(10) ——搭建源码学习环境

    Hadoop学习笔记(10) ——搭建源码学习环境 上一章中,我们对整个hadoop的目录及源码目录有了一个初步的了解,接下来计划深入学习一下这头神象作品了.但是看代码用什么,难不成gedit?,单步 ...

  7. [Linux]C语言Linux系统编程创建进程

    1.进程ID 每一个进程都由一个唯一的标识符表示,即进程ID,简称pid.系统保证在某时刻每个pid都是唯一的. 1.1分配进程ID 缺省情况下,内核将进程ID的最大值限制为32768,可以在此处设置 ...

  8. mac obs直播软件 无法输出音频解决办法

    搜索大量的网页,确没有一个实用的设置教程,也正是speechless. 直接做个教程,方便大家的使用 1.安装 boom 2 到app store 上搜索boom 我安装的是正版的,需要128元. 你 ...

  9. yum卸载

    完全卸载依赖 -- 正常安装 yum install sl -- 列出操作 yum history list sl -- 根据显示install操作的id进行删除 yum history undo { ...

  10. SQL Server 2008各版本介绍区别(包含企业版 开发者版 标准版 Web版 工作组版 Express版 Compact版)

    SQL Server 2008分为SQL Server 2008企业版.标准版.工作组版.Web版.开发者版.Express版.Compact 3.5版,其功能和作用也各不相同,其中SQL Serve ...