线性表——链表(java实现)

原创 2018年04月15日 16:39:28

一,链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表是顺序表在链式存储结构上的另一种实现。链表一般可分为单链表,双链表和循环列表。

单链表:由结点组成,每个结点包含两部分,数据域和指针域,数据域存储真正需要存储的数据部分,指针域是一个指向其后继结点(或前驱结点)的指针。
双链表:双链表也是由结点组成,不同的是除了数据域以外,它有两个指针域,一个指向其前驱结点,另一个指向其后继结点。
循环列表:循环列表一般指单循环列表,即只有一个指针域的链表,在与单链表不同的是,在单链表中其尾结点的指针通常为null,而循环列表其尾结点的指针指向其头节点,构成一个循环结构。


二, 链表实现

这里我们采用的是双链表,即链表结点包含数据域和两个指针域,一个指向前驱结点,一个指向后继结点。

1,定义一个类MyLinkedList实现接口MyList

MyList的定义见文章https://blog.csdn.net/YIXIANG0234/article/details/79901468

实现MyList接口并支持泛型

public class MyLinkedList<T> implements MyList<T> {}

2,成员变量

private Node<T> head;   //头节点,指向链表中的第一个结点
private Node<T> rear;   //尾结点,指向链表中的最后一个结点
private int length; //链表的长度

3,静态内部类——结点类

//私有的静态内部类,用于构建链表结点
private static class Node<T>
{
    public Node<T> prev;    //指向前驱结点
    public Node<T> next;    //指向后继结点
    public T data;  //存储数据
    public Node(Node<T> prev,Node<T>next,T data)
    {
        this.prev = prev;
        this.next = next;
        this.data = data;
    }
}

4,构造方法

//构造方法,初始化一个带有头尾结点的空链表
public MyLinkedList() {
    head = new Node<T>(null,null,null);
    rear = new Node<T>(head,null,null);
    head.next = rear;
    length = 0;
}

5,getNode方法,私有方法,方便链表操作的实现,查找指定位置的结点

private Node<T> getNode(int index)//获取指定位置的结点
{
    Node<T> node;
    if(index<0 || index>length-1)
        throw new RuntimeException("查找操作失败,下标越界:"+index);
    //分为两部分查找,下标小于length/2在前半部分查找
    if(index<length/2)
    {
        node = head.next;
        for(int i=0;i<index;i++)
            node = node.next;
    }
    else {      //下标大于length/2在后半部分查找
        node = rear.prev;
        for(int i=length-1;i>index;i--)
            node = node.prev;
    }
    return node;    
}

6,get方法,根据下标查找结点数据

@Override
public T get(int index) {   //查找指定位置的数据
    Node<T> node = getNode(index);
    if(node==null)
        return null;
    return node.data;
}

7,indexOf方法,查找某个数据所在结点的下标

@Override
public int indexOf(T data) {
    int index = 0;
    Node<T> node = head.next; //找到链表第一个节点
    while(index<length && node!=null)
    {
        if(node.data.equals(data))//找到即返回
            return index;
        node = node.next;   //循环查找下一个结点
        index++;
    }
    return -1;  //未找到以-1表示
}

8,size方法,返回链表的大小

@Override
public int size() { //返回此链表的长度
    return length;
}

9,isEmpty方法,判断链表是否为空

@Override
public boolean isEmpty() {      //判断链表是否为空
    return length == 0;
}

10,toString方法,给出链表的字符串表示

public String toString()
{
    StringBuilder sb = new StringBuilder();
    sb.append("{");
    for(int i=0;i<length-1;i++)
    {
        String s = get(i).toString();
        sb.append(s+",");
    }
    sb.append(get(length-1).toString()+"}");
    return sb.toString();
}

11,set方法,修改结点的数据

@Override
public boolean set(int index, T data) {
    Node<T> node = getNode(index);  //找到该结点,然后更新数据域
    node.data = data;
    return true;
}

12,remove方法,分别根据下标和数据移除结点

@Override
public T remove(int index) {
    Node<T> node = getNode(index);  //查找到该结点
    node.prev.next = node.next;     //调整结点间的前驱和后继关系
    node.next.prev = node.prev;
    T data = node.data;     //保存数据域
    node = null;        //将该节点的引用置为空,存储空间的处理由垃圾回收机制管理
    length--;
    return data;    //返回数据域
}

@Override
public T remove(T data) {
    int index = indexOf(data);      //查找到该数据在链表中的位置
    Node<T> node = getNode(index);  //找到表示该数据的结点
    if(node == null)
        return null;
    node.prev.next = node.next;     //调整前驱和后继关系
    node.next.prev = node.prev;
    T nodeData = node.data;
    node  = null;
    length--;
    return nodeData;    //返回数据
}

13,add方法,分别在末尾和指定位置插入结点

@Override
public boolean add(T data) {    //在链表结尾插入
    Node<T> newNode = new Node<T>(rear.prev,rear,data);
    newNode.prev.next = newNode;
    rear.prev = newNode;
    length++;
    return true;
}

@Override
public boolean add(int index, T data) { //在指定的位置插入结点
    Node<T> node = getNode(index);  //获取指定位置的结点
    if(node == null)
        return false;
    //在指定位置的结点前加入新节点,即调整各结点间的前驱和后继关系
    Node<T> newNode = new Node<T>(node.prev,node,data);
    newNode.prev.next = newNode;
    node.prev = newNode;
    length++;
    return true;
}

14,说明:
方法getNode,add,remove,indexOf,get等是链表实现的核心算法


三,测试

针对MyLinkedList类中的方法编写测试类TestMyLinkedList

import static java.lang.System.*;
public class TestMyLinkedList {
public static void main(String[] args) {
    MyLinkedList<String> list = new MyLinkedList<String>();//测试构造方法
    list.add("常州"); //测试add方法,在链表末尾添加
    list.add("北京");
    list.add("China");
    list.add("SuZhou");
    out.println("添加插入操作:");
    out.println(list.toString());//测试头String方法
    list.add(1,"南京");   //测试ad方法,在指定位置添加
    out.println(list.toString());
    out.println("--------------------------------------------------");
    list.remove(1); //测试remove方法,删除指定位置元素
    out.println("删除操作:");
    out.println(list.toString());
    list.remove("China");
    out.println(list.toString());
    out.println("--------------------------------------------------");
    out.println("其他操作:");
    list.set(2, "苏州");
    String data = list.get(2);
    out.println(data);
    out.println(list.toString());
    int index = list.indexOf("北京");
    out.println("北京的下标是:"+index);
    out.println("链表list的长度为:"+list.size());
    out.println("list是否为空:"+list.isEmpty());
}
}

程序运行结果:

添加插入操作:
{常州,北京,China,SuZhou}
{常州,南京,北京,China,SuZhou}
--------------------------------------------------
删除操作:
{常州,北京,China,SuZhou}
{常州,北京,SuZhou}
--------------------------------------------------
其他操作:
苏州
{常州,北京,苏州}
北京的下标是:1
链表list的长度为:3
list是否为空:false

从运行结果可以看出,我们的链表得到了较好的实现,如有bug,望各位斧正。


四,顺序表和链表的优缺点

顺序表:
1,优点:

  • 方法简单,存储结构是顺序结构,可以采用数组实现
  • 无须为表示结点间的逻辑关系而增加额外的存储空间
  • 顺序表具有按元素随机访问的特点

2,缺点:

  • 在顺序表中进行插入删除操作时,由于数据在内存中顺序存储,所以需要移动元素,平均移动表中大约一般的元素,n较大时效率低。
  • 需要预先分配足够大的存储空间,若估计过大造成内存浪费,若分配过小会溢出,造成顺序表内部实现对数组元素的移动和复制。

链表:
与顺序表相反,对元素的插入删除效率高,而不具有对元素随机访问的特点,所以访问元素的效率很低。

顺序表和链表的选择
如何选择顺序表和链表呢?结合以上的优缺点可以看出,如果对数据基本不存在改动的操作,即很少对数据进行增删操作,反而常做查询的操作,那么选用顺序表。如果需要对数据进行频繁的增删改,查询操作较少,那么选择链表。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YIXIANG0234/article/details/79949967

java数据结构与算法-线性表与链表(2)

上一会我们讲到了在java中线性表和链表的创建,数据的添加和删除所消耗的代价。...
  • u013930163
  • u013930163
  • 2015-05-11 12:44:33
  • 634

用链表实现线性表java

  • 2010年07月02日 09:53
  • 2KB
  • 下载

线性表(顺序表,链表的表示和实现)

本篇文章将从以下几点进行讲解:  1.线性表的类型定义  2.1线性表的顺序表示和实现  2.2线性表的链式表示和实现 1.线性表的类型定义: 接下来介绍下什么是线性表,学习每一样东...
  • u011706736
  • u011706736
  • 2016-03-06 16:55:38
  • 3583

【C++】c++实现线性表、链表

C++
  • SuLiJuan66
  • SuLiJuan66
  • 2015-10-02 23:22:14
  • 944

线性表的基本操作实现(基于链表)

用C++链表方式实现了线性表的一些基本操作,包括插入元素,删除元素,反转线性表等.#include #include #define ERROR NULL typedef int ElementTy...
  • qq_23849183
  • qq_23849183
  • 2015-12-09 22:01:24
  • 630

[java]——深入理线性表与线性链表

map 链表 是一种数据结构(我觉得java底层代码就是数据结构和设计模式的实现...),在物理内存里的存储是非连续的,非顺序的。这怎么理解呢?数组的存储模型是这样的,我们在声明数组的时候,需要指定数...
  • bless2015
  • bless2015
  • 2015-06-06 10:37:46
  • 996

线性表--顺序表及链表

本文包含以下内容: 一、线性表的介绍以及抽象ADT 二、顺序表及顺序表的实现 三、链表及链表的实现 四、顺序表和链表的比较 五、总结 参考书目:《数据结构与算法分析》【美】Clifford A.S...
  • bujuan827
  • bujuan827
  • 2016-08-15 07:24:53
  • 731

线性表的两种实现 -- 顺序表和链表(C++)

线性表两种实现方法的比较 空间方面: 顺序表的缺点:大小事先固定。虽然便于分配空间,但是元素只有少数的几个时造成空间的浪费。 链表的优点:只有实际在链表中的对象需要空间,只要存在可用的内存空间分...
  • y396397735
  • y396397735
  • 2016-05-27 10:57:52
  • 2047

线性表的数组实现和链表实现

线性表的顺序存储实现 //线性表用数组来实现 typedef struct PolyNode *Polynomial; struct PolyNode{ int coef;//系数 int ...
  • SpadgerZ
  • SpadgerZ
  • 2017-03-23 00:10:51
  • 607
收藏助手
不良信息举报
您举报文章:线性表——链表(java实现)
举报原因:
原因补充:

(最多只允许输入30个字)