Java数据结构-线性表之静态链表

静态链表的定义:

节点由一个一维数组和一个指针域组成,数组用来存放数据元素,而指针域里面的指针(又称游标)用来指向下一个节点的数组下标。这样的链表称之为静态链表。

链表中的数组第一个和最后一个位置需要特殊处理,不存数据。第一个位置(即数组0下标)的节点的指针用来存放备用链表的第一个节点的数组下标。最后一个位置(即数组长度MaxSize-1下标)的节点的指针用来存放指向有数值的第一个数据元素的数组下标,类似于单链表的头结点。

静态链表的示例图:

下面举一个摘抄自《大话数据结构》的例子,来解释一下静态数据链表。

下面介绍静态链表的插入和删除操作:

这里我画了一张图,简单的描述了一下,相信应该容易理解,如下:

同样删除的示例图如下:

下面贴一下我用Java实现的代码,主要功能只实现了插入和删除操作:

package com.phn.datestructure;
/**
 * @author 潘海南
 * @Email [email protected]
 * @TODO 静态链表
 * @date 2015年7月19日
 */
public class FOStaticList<E> {
    //静态链表的长度
    private int size;
    //静态链表的容量,实际容量为capacity-2:capacity-头结点-备用头结点=capacity-2
    private int capacity;
    //备用链表的头结点
    private FOStaticNode<E> backNode= null;
    //备用链表在数组中的位置,默认为第一个,即为0
    private int backNodeIndex = 0;
    //静态链表的头结点,即静态链表数据元素链的头结点
    private FOStaticNode<E> headerNode = null;
    //静态链表的存储数据元素的数组
    private FOStaticNode<E>[] fosn = null;
    //默认容量
    private static final int DEFUALT_CAPACITY = 100;

    public FOStaticList(){
        this(DEFUALT_CAPACITY);
    }
    /**
     * @TODO 带参构造函数,用来初始化赋值静态链表,并进行相关容量和大小的设置
     * @param initialCapacity 静态链表的初始化容量
     */
    public FOStaticList(int initialCapacity) {
        this.init(initialCapacity);
        this.setCapacity(initialCapacity);
        this.setSize();
    }
    /**
     * @TODO 初始化赋值静态链表,并设置静态链表的头结点和备用链表的头结点
     * @param initialCapacity
     */
    private void init(int initialCapacity) {
        //判断给定的初始化参数是否合法
        if (initialCapacity < 0) {
            throw new RuntimeException("数组大小错误:" + initialCapacity);
        }
        fosn = new FOStaticNode[initialCapacity];
        //给静态链表赋值,内部的e=null,而游标设置为i+1
        for(int i = 0;i<initialCapacity-1;i++){
            fosn[i]  = new FOStaticNode<E>();
            fosn[i].setCursor(i+1);
        }
        fosn[initialCapacity-1] = new FOStaticNode<E>();
        //设置静态链表的头结点指向备用链表的数组下标,即initialCapacity-1的节点的游标为0
        fosn[initialCapacity-1].setCursor(backNodeIndex);
        //设置静态链表的头结点为headerNode
        this.setHeaderNode(fosn[initialCapacity-1]);
        //设置备用链表的头结点为backNode
        this.setBackNode(fosn[backNodeIndex]);
    }
    /**
     * @TODO 静态链表尾插法添加数据元素
     * @param e 要添加的数据元素
     * @return true
     */
    public boolean add(E e){
        //检查链表的容量,并进行相应的扩容
        this.ensureCapacity(size);
        //将备用链表头结点指向的游标(即备用链表的第一个位置)赋值给oldBackNodeCursor保存
        int oldBackNodeCursor = this.backNode.getCursor();
        if(size==0){
            /*若目前(即添加元素之前)静态链表没有数据元素,则将静态链表的头结点的游标指向
         *   “备用链表头结点指向的游标对应的位置”,即备用链表的第一个元素位置*/
            this.headerNode.setCursor(oldBackNodeCursor);
        }else{
            //将静态链表的头结点指向的游标赋值给tempNodeCursor
            int tempNodeCursor = this.headerNode.getCursor();
            //下面的循环用来找到静态链表(数据元素链)的最后一个元素节点lastNode
            FOStaticNode<E> lastNode = new FOStaticNode<E>();
            while(tempNodeCursor!=0){
                lastNode= this.fosn[tempNodeCursor];
                tempNodeCursor= this.fosn[tempNodeCursor].getCursor();
            }
            //将lastNode节点的指向游标设置值为备用链表的第一个位置
            lastNode.setCursor(oldBackNodeCursor);
        }
        //将备用链表的第一个位置设置数据元素为e
        this.fosn[oldBackNodeCursor].setE(e);
        //获取备用链表的第一个位置指向的游标,并将其赋值给newBackNodeCursor(作为新的备用链表头结点指向的游标)保存,
        int newBackNodeCursor = this.fosn[oldBackNodeCursor].getCursor();
        //设置备用链表的第一个位置(即目前作为静态链表数据元素链的最后一个元素)指向的游标为备用链表的头结点位置(默认0位置)
        this.fosn[oldBackNodeCursor].setCursor(backNodeIndex);
        //设置备用链表头结点指向的游标为新的备用链表头结点指向的游标newBackNodeCursor
        this.backNode.setCursor(newBackNodeCursor);
        //链表长度加1
        this.size++;
        return true;
    }
    /**
     * @TODO 根据提供的index来删除静态链表中的第index个元素
     * @param index 静态链表中的第index个元素
     * @return true or false
     */
    public boolean remove(int index){
        //判断给出的元素位置是否合法;this.capacity-2表示静态链表能够达到的最大长度:capacity-头结点-备用头结点=capacity-2
        if(index<1 || index>this.capacity-2){
            System.out.println("不存在此位置的元素");
            return false;
        }
        //声明变量preRemoveCursor用来作为将要删除的数据元素数组的下标,或者称为将要删除的数据元素的前一个节点指向的游标。
        int preRemoveCursor = this.headerNode.getCursor();
        //下面的循环用来找出删除的数据元素的前一个节点preRemoveNode和将要删除的数据元素的前一个节点指向的游标preRemoveCursor
        FOStaticNode<E> preRemoveNode = new FOStaticNode<E>();
        int tempCount = 0;
        while(tempCount!=index-1){
            preRemoveNode = this.fosn[preRemoveCursor];
            preRemoveCursor = preRemoveNode.getCursor();
            tempCount++;
        }
        //声明变量oldBackNodeCursor作为备用链表的头结指向的游标并赋值保存。
        int oldBackNodeCursor = this.backNode.getCursor();
        //设置备用链表的头结点指向的游标为 将要删除的数据元素数组的下标 preRemoveCursor
        this.backNode.setCursor(preRemoveCursor);
        //将将要删除的数据元素指向的游标赋值给removeCursor并保存
        int removeCursor = this.fosn[preRemoveCursor].getCursor();
        //将将要删除的数据元素指向的游标设置为备用链表的头结指向的游标oldBackNodeCursor
        this.fosn[preRemoveCursor].setCursor(oldBackNodeCursor);
        //将将要删除的数据元素设置为null,即删除
        this.fosn[preRemoveCursor].setE(null);
        //将将要删除的数据元素的前一个节点指向的游标设置为将要删除的数据元素指向的游标removeCursor
        preRemoveNode.setCursor(removeCursor);
        //长度减1
        this.size--;
        return true;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer("[ ");
        int currentCursor = this.headerNode.getCursor();
        if(currentCursor!=backNodeIndex){
            sb.append(currentCursor+""+this.fosn[currentCursor]);
            currentCursor = this.fosn[currentCursor].getCursor();
            while(currentCursor!=backNodeIndex){
                sb.append(", "+currentCursor+""+this.fosn[currentCursor]);
                currentCursor = this.fosn[currentCursor].getCursor();
            }
        }
        return sb.append(" ]").toString();
    }
    /**
     * @TODO 判断静态链表的容量是否超过,并进行相应的扩容操作。
     * 注意:这里最主要的是将当前静态链表数组下标最后一个位置的游标保存起来,即将头结点指向的游标保存起来;
     * 然后赋值给新的静态链表的头结点指向的游标。
     * @param currentSize 当前长度
     */
    private void ensureCapacity(int currentSize) {
        if(currentSize == this.capacity-2){
            int oldCapacity = this.capacity;
            //这里我是按照ArrayList的扩容来进行的,扩大约1.5倍左右。
            this.capacity = (this.capacity * 3) / 2 + 1;
            FOStaticNode<E>[] newData = new FOStaticNode[this.capacity];
            for (int i = 0; i < oldCapacity-1; i++) {
                newData[i] = this.fosn[i];
            }
            newData[capacity-1]  = new FOStaticNode<E>();
            newData[capacity-1].setCursor(this.fosn[oldCapacity-1].getCursor());
            for(int i = oldCapacity-1;i<this.capacity-1;i++){
                newData[i]  = new FOStaticNode<E>();
                newData[i].setCursor(i+1);
            }
            this.fosn = newData;
        }
    }
    /**
     * @return the size
     */
    public int size() {
        return size;
    }
    private void setSize() {
        this.size=0;
    }
    /**
     * @param backNode the backNode to set
     */
    private void setBackNode(FOStaticNode<E> backNode) {
        this.backNode = backNode;
    }
    /**
     * @param headerNode the headerNode to set
     */
    private void setHeaderNode(FOStaticNode<E> headerNode) {
        this.headerNode = headerNode;
    }
    /**
     * @param capacity the capacity to set
     */
    private void setCapacity(int capacity) {
        this.capacity = capacity;
    }

}

在举一个删除的示例图,请联系我的代码进行操作,

首先写个测试的方法

public static void main(String[] args) {
    FOStaticList<String> fosl = new FOStaticList<String>(6);
    fosl.add("元素1");
    System.out.println(fosl);
    fosl.add("元素2");
    fosl.add("元素3");
    System.out.println(fosl);
    fosl.add("元素4");
    System.out.println(fosl);
    System.out.println(fosl.size());
    fosl.remove(2);
    System.out.println(fosl);
}

运行测试方法,结合下图应该可以比较好的理解。

最后说说静态链表的优缺点。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-08 23:49:45

Java数据结构-线性表之静态链表的相关文章

Java数据结构-线性表之单链表LinkedList

线性表的链式存储结构,也称之为链式表,链表:链表的存储单元可以连续也可以不连续. 链表中的节点包含数据域和指针域,数据域为存储数据元素信息的域,指针域为存储直接后继位置(一般称为指针)的域. 注意一个头结点和头指针的区别: 头指针: 指向链表的第一个节点的指针,若链表有头结点,则是指向头结点的指针: 头指针具有标识作用,所以常用头指针作为链表的名字: 不论链表是否为空,头指针都不为空: 是链表的必要元素. 头结点: 头结点是为了操作的统一和方便而设立的,放在第一个元素节点的前面,其数据域一般无意

Java数据结构-线性表之队列

队列(Queue)的定义:只允许在一端进行插入另一端进行删除操作的线性表.允许插入的一端称为队尾(rear) ,允许删除的一端称为队头(front). 具有"先进先出"特点. 队列也是线性表,所以也存在顺序结构和链式结构. 顺序队列: 对于队列,入队操作的解释为: (是在队尾追加一个元素,不需要移动任何元素,因此时间复杂度为0(1).) 判断队列是否已满: 如果没满则先给队尾元素赋值; 然后将队尾指针后移一位(对队尾指针赋值,Q->rear = Q->rear+1 ). 出

[大话数据结构]线性表之单链表结构和顺序存储结构

线性表定义: 零个或者多个数据元素的有限序列.元素之间是有顺序的,如果元素存在多个,则第一个元素无前驱,最后一个元素无后继.其他每个元素都有且只有一个前驱和后继.并且数据元素的类型要相同. 线性表的抽象数据类型: ADT 线性表(List) Data 线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为DataType. 其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素. 数据元素之间的关系是一对一的关系.

数据结构与算法(三)-线性表之静态链表

前言:前面介绍的线性表的顺序存储结构和链式存储结构中,都有对对象地引用或指向,也就是编程语言中有引用或者指针,那么在没有引用或指针的语言中,该怎么实现这个的数据结构呢? 一.简介 定义:用数组代替指针或引用来描述单链表,即用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法: 上面的静态链表图有两个数组游标和数据,其中数据数组存储数据,而游标数组存储同下标为数据的下一个数据的下标值,简单模拟一下静态链表遍历的过程: 先查看下标为999的游标数组值:1: 根据游标数组值1,查找下标为1的数据:

Java数据结构-线性表之栈(顺序栈和链栈)

栈的定义:(特殊的线性表) ??仅在表的一端进行插入和删除的线性表.允许插入.删除的这一端称为栈顶,另一端称为栈底.表中没有元素时称为空栈. ??被称为后进先出的线性表(Last In First Out),简称 LIFO表,或被称为先进后出的线性表(First In Last Out),简称 FILO表. ??栈更具存储方式的不同分为两种:顺序栈和链栈. 顺序栈: 和顺序表一样,顺序栈也采用数组来存放数据元素: 为了保证栈底位置的不变,采用数组下标为0的位置作为顺序栈的栈底. 而栈顶指针的最大

Java数据结构-线性表之顺序表ArrayList

线性表的顺序存储结构,也称为顺序表,指用一段连续的存储单元依次存储线性表中的数据元素. 根据顺序表的特性,我们用数组来实现顺序表,下面是我通过数组实现的Java版本的顺序表. package com.phn.datestructure; /** * @author 潘海南 * @Email [email protected] * @TODO 顺序表 * @date 2015年7月16日 */ public class FOArrayList<E> { // 顺序表长度 private int

Java数据结构-线性表之链表应用-检测链表是否有环

??如何检测一个链表是否有环?这个是一个出现频率较高的面试题. ??如下是一个含有环的链表. (图片来自http://www.nowamagic.net/librarys/veda/detail/2245 一个有很多关于数据结构的文章的网站,还有其他的资料,可以看看) 我这里解题的方法有三种: 快慢指针方法:两个速度不一样的指针遍历总会相遇: 利用环的顶点数和边相等的关系: 两个指针遍历判断步数是否相等. ??为了实现检查链表是否含有环的情况,我们需要先构建出一个含有环的链表. ??于是乎我在之

数据结构-线性表之单向链表--一点一滴

单向链表 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 单向链表的数据结构可以分为两部分:数据域和指针域,数据域存储数据,指针域指向下一个储存节点的地址.分为动态单向链表和静态单向链表.单向链表也可以根据是否带头节点分为带头节点结构和无带头节点结构.我们把指向单链表的指针为头指针.头指针所指的不存放数据元素的第一个节点称作头节点.存放数据元素的节点成为第一个数据元素节点. 注:第一个数据元素节点在带头节点单链表中是第二个节点:而在不带头节

[数据结构]线性表之单链表的类模板实现

类的具体实现如下: ///////////////////////// #include"LinearList.h" #include <iostream> #include <cstdlib> using namespace std; template<class T> struct LinkNode //链表节点类 { T data; LinkNode<T>* link; LinkNode(LinkNode<T>* ptr