顺序表的java实现

Posted 枸杞仙人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了顺序表的java实现相关的知识,希望对你有一定的参考价值。

顺序表

线性表

线性表是最基本、最简单、也是最常用的一种数据结构。一个线性表是n个具有相同特性的数据元素的有限序列。

  • 前驱元素:
    若A元素在B元素的前面,则称A为B的前驱元素
  • 后继元素:
    若B元素在A元素的后面,则称B为A的后继元素

线性表的特征

数据元素之间具有一种“一对一”的逻辑关系。

  1. 第一个数据元素没有前驱,这个数据元素被称为头结点;
  2. 最后一个数据元素没有后继,这个数据元素被称为尾结点;
  3. 除了第一个和最后一个数据元素外,其他数据元素有且仅有一个前驱和一个后继。

线性表的分类

线性表中数据存储的方式可以是顺序存储,也可以是链式存储,按照数据的存储方式不同,可以把线性表分为顺序表和链表

顺序表

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。

API设计

类名SequenceList
构造方法SequenceList(int capacity):创建容量为capacity的SequenceList对象
成员方法1.public void clear():空置线性表
2.publicboolean isEmpty():判断线性表是否为空,是返回true,否返回false
3.public int length():获取线性表中元素的个数
4.public T get(int i):读取并返回线性表中的第i个元素的值
5.public void insert(int i,T t):在线性表的第i个元素之前插入一个值为t的数据元素。
6.public void insert(T t):向线性表中添加一个元素t
7.public T remove(int i):删除并返回线性表中第i个数据元素。
8.public int indexOf(T t):返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1。
成员变量1.private T[] eles:存储元素的数组
2.private int N:当前线性表的长度

代码实现

SequenceList类:

public class SequenceList<T> 
    //存储元素数据
    private T[] eles;

    //记录当前元素个数
    private int N;

    //构造方法
    public SequenceList(int capacity)
        //初始化数组
        this.eles = (T[]) new Object[capacity];
        //初始化长度
        this.N = 0;
    

    //将线性表转置为空表
    public void clear()
        this.N = 0;
    

    //判断当前线性表是否为空
    public boolean isEmpty()
        return N==0;
    

    //获取线性表长度
    public int length()
        return N;
    

    //获取指定位置元素
    public T get(int i)
        //安全性检查
        if (i<0 || i>=N)
            throw new RuntimeException("当前元素不存在");
        
        return eles[i];
    

    //向线性表添加元素t
    public void insert(T t)
        //安全性检查
        if (N==eles.length)
            throw new RuntimeException("当前表已满");
        
        eles[N++] = t;
    

    //在i位置添加元素t
    public void insert(T t,int i)
        //安全性检查
        if (N==eles.length)
            throw new RuntimeException("当前表已满");
        
        if (i<0 || i>=N)
            throw new RuntimeException("插入位置不合法");
        
        //先把i索引处及其之后的元素往后移一位
        for (int index = N;index > i;index--)
            eles[index] = eles[index-1];
        
        //把i索引处的值修改为t
        eles[i] = t;
        N++;
    

    //删除指定索引处的值并返回该元素
    public T remove(int i)
        //安全性检查
        if (i<0 || i>=N)
            throw new RuntimeException("删除元素不存在");
        
        //记录该元素的值
        T current = eles[i];
        //把i后面的元素前移一位
        for (int index = i+1; index <N ; index++) 
            eles[index-1] = eles[index];
        
        //元素个数减一
        N--;
        return current;
    

    //查找t元素第一次出现的位置
    public int indexOf(T t)
        //安全性检查
        if (t==null)
            throw new RuntimeException("查找元素不合法");
        
        for (int i = 0; i < N; i++) 
            if (eles[i].equals(t))
                return i;
            
        
        return -1;
    

Test类:

public class SequenceListTest 
    public static void main(String[] args) 
        //创建顺序表对象
        SequenceList<String> s1 = new SequenceList<>(10);

        //测试插入
        s1.insert("zhangsan");
        s1.insert("lisi");
        s1.insert("wangwu");
        s1.insert("zhaoliu");

        //测试获取
        String getResult1 = s1.get(1);
        System.out.println("索引i处的值是:" + getResult1);

        //测试删除
        String removeResult = s1.remove(0);
        System.out.println("被删除的元素是:" + removeResult);

        //测试清空
        s1.clear();
        System.out.println("清空后线性表中元素的个数是:" + s1.length());
    

顺序表的遍历

一般作为容器存储数据,都需要向外部提供遍历的方式,因此我们需要给顺序表提供遍历方式。

在java中,遍历集合的方式一般都是用的是foreach循环,如果想让我们的SequenceList也能支持foreach循环,则需要做如下操作:

  1. 让SequenceList实现Iterable接口,重写iterator方法;
  2. 在SequenceList内部提供一个内部类SIterator,实现Iterator接口,重写hasNext方法和next方法;
public class SequenceList<T> implements Iterable<T> 

    //存储元素数据
    private T[] eles;

    //记录当前元素个数
    private int N;

    //构造方法
    public SequenceList(int capacity)
        //初始化数组
        this.eles = (T[]) new Object[capacity];
        //初始化长度
        this.N = 0;
    

    //将线性表转置为空表
    public void clear()
        this.N = 0;
    

    //判断当前线性表是否为空
    public boolean isEmpty()
        return N==0;
    

    //获取线性表长度
    public int length()
        return N;
    

    //获取指定位置元素
    public T get(int i)
        //安全性检查
        if (i<0 || i>=N)
            throw new RuntimeException("当前元素不存在");
        
        return eles[i];
    

    //向线性表添加元素t
    public void insert(T t)
        //安全性检查
        if (N==eles.length)
            throw new RuntimeException("当前表已满");
        
        eles[N++] = t;
    

    //在i位置添加元素t
    public void insert(T t,int i)
        //安全性检查
        if (N==eles.length)
            throw new RuntimeException("当前表已满");
        
        if (i<0 || i>=N)
            throw new RuntimeException("插入位置不合法");
        
        //先把i索引处及其之后的元素往后移一位
        for (int index = N;index > i;index--)
            eles[index] = eles[index-1];
        
        //把i索引处的值修改为t
        eles[i] = t;
        N++;
    

    //删除指定索引处的值并返回该元素
    public T remove(int i)
        //安全性检查
        if (i<0 || i>=N)
            throw new RuntimeException("删除元素不存在");
        
        //记录该元素的值
        T current = eles[i];
        //把i后面的元素前移一位
        for (int index = i+1; index <N ; index++) 
            eles[index-1] = eles[index];
        
        //元素个数减一
        N--;
        return current;
    

    //查找t元素第一次出现的位置
    public int indexOf(T t)
        //安全性检查
        if (t==null)
            throw new RuntimeException("查找元素不合法");
        
        for (int i = 0; i < N; i++) 
            if (eles[i].equals(t))
                return i;
            
        
        return -1;
    

    @Override
    public Iterator<T> iterator() 
        return new SIterator();
    

    private class SIterator implements Iterator
        private int cusor;
        public SIterator()
            this.cusor = 0;
        
        @Override
        public boolean hasNext() 
            return cusor<N;
        

        @Override
        public Object next() 
            return eles[cusor++];
        
    

Test类:

public class SequenceListTest 
    public static void main(String[] args) 
        //创建顺序表对象
        SequenceList<String> s1 = new SequenceList<>(10);

        //测试插入
        s1.insert("zhangsan");
        s1.insert("lisi");
        s1.insert("wangwu");
        s1.insert("zhaoliu");
        for (String s:s1) 
            System.out.println(s);
        
        System.out.println("-----------------------------");

        //测试获取
        String getResult1 = s1.get(1);
        System.out.println("索引i处的值是:" + getResult1);

        //测试删除
        String removeResult = s1.remove(0);
        System.out.println("被删除的元素是:" + removeResult);

        //测试清空
        s1.clear();
        System.out.println("清空后线性表中元素的个数是:" + s1.length());
    

顺序表容量的可变

在之前的实现中,当我们使用SequenceList时,先new SequenceList(5)创建一个对象,创建对象时就需要指定容器的大小,初始化指定大小的数组来存储元素,当我们插入元素时,如果已经插入了5个元素,还要继续插入数据,则会报错,就不能插入了。这种设计不符合容器的设计理念,因此我们在设计顺序表时,应该考虑它的容量的伸缩性。

考虑容器的容量伸缩性,其实就是改变存储数据元素的数组的大小,那我们需要考虑什么时候需要改变数组的大小?

  1. 添加元素时:
    添加元素时,应该检查当前数组的大小是否能容纳新的元素,如果不能容纳,则需要创建新的容量更大的数组,我们这里创建一个是原数组两倍容量的新数组存储元素。

  2. 移除元素时:
    移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存
    空间的浪费,应该创建一个容量更小的数组存储元素。如果我们发现数据元素的数量不足数组容量的1/4,则创建
    一个是原数组容量的1/2的新数组存储元素。

代码中,我们通过定义resize方法,在插入和删除元素时进行检验是否需要改变容量,然后进行对应操作:

public class SequenceList<T> implements Iterable<T> 

    //存储元素数据
    private T[] eles;

    //记录当前元素个数
    private int N;

    //构造方法
    public SequenceList(int capacity)
        //初始化数组
        this.eles = (T[]) new Object[capacity];
        //初始化长度
        this.N = 0;
    

    //将线性表转置为空表
    public void clear()
        this.N = 0;
    

    //判断当前线性表是否为空
    public boolean isEmpty()
        return N==0;
    

    //获取线性表长度
    public int length()
        return N;
    

    //获取指定位置元素
    public T get(int i)
        //安全性检查
        if (i<0 || i>=N)
            throw new RuntimeException("当前元素不存在");
        
        return eles[i];
    

    //向线性表添加元素t
    public void insert(T t)
        //安全性检查
        if (N==eles.length)
            //扩容
            resize(2*eles.length);
        
        eles[N++] = t;
    

    //在i位置添加元素t
    public void insert(T t,int i)
        //安全性检查
        if (N==eles.length)
            //扩容
            resize(2*eles.length);
        
        if (i<0 || i>=N)
            throw new RuntimeException("插入位置不合法");
        
        //先把i索引处及其之后的元素往后移一位
        for (int index = N;index > i;index--)
            eles[index] = eles[index-1];
        
        //把i索引处的值修改为t
        eles[i] = t;
        N++;
    

    //删除指定索引处的值并返回该元素
    public T remove(int i)
        //安全性检查
        if (i<0 || i>=N)
            throw new RuntimeException("删除元素不存在");
        
        //记录该元素的值
        T current = eles[i];
        //把i后面的元素前移一位
        for (int index = i+1; index <N ; index++) 
            eles[index-1] = eles[index];
        
        //元素个数减一
        N--;

        //缩容
        if (N<eles.length/4)
            resize(eles.length/2);
        

        return current;
    

    //查找t元素第一次出现的位置
    public int indexOf(T t)
        //安全性检查
        if (t==null)
            throw new RuntimeException("查找元素不合法");
        
        for (int i = 0; i < N; i++) 
            if (eles[i].equals(t))
                return i;
            
        
        return -1;
    

    @Override
    public Iterator<T> iterator() 
        return new SIterator();
    

    private class SIterator implements Iterator
        private int cusor;
        public SIterator()
            this.cusor = 0;
        
        @Override
        public boolean hasNext() 
            return

以上是关于顺序表的java实现的主要内容,如果未能解决你的问题,请参考以下文章

双向链表的原理与实现

数据结构之线性表之顺序存储Java实现

线性表之顺序存储结构实现(上)

线性表的概念与实现

数据结构——线性表

浅析线性表的原理及简单实现