Java数据结构之顺序表及实现

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

1、线性表相关概念

前驱元素:一个A元素在B元素的前面,则称A是B的前驱元素。

后继元素:若B元素在A元素的后面,则称B为A的后继元素。

线性表的特征

  • 第一个数据元素没有前驱,这个数据元素被称为头结点
  • 最后一个数据元素没有后继,这个数据元素被称为尾结点
  • 除了第一个和最后一个数据元素外,其他的数据元素都有一个前驱和一个后继。

2、线性表的分类

线性表中的数据存储方式可以是顺序存储,也可以是链式存储,就可以分为顺序表链表

3、顺序表

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素

顺序表的实现

顺序表API设计

类名Squencelist
构造方法Squencelist (int capacity):创建容量为capacity的Squencelist对象
成员变量

private int N;当前线性表的长度

private T[] elements; 存储元素的数组

成员方法

 

public void clear() 清空线性表
public boolean isEmpty()获取线性表中元素的个数
public int length()获取线性表中元素的个数
public T get(int i)获取并返回线性表中的第i个元素的值
public void add(T t)向线性表中添加一个元素t
public void add(T t, int i)在线性表的第i个位置上添加元素t
public T remove(int i)删除并返回线性表的第i个位置的元素
public int indexof(T t)返回线性表中首次出现元素t的索引下标,若不存在,则返回-1

Java代码实现API

public class Squencelist<T>{
    private T[]elements;//保存
    private int N;//元素个数

    public Squencelist(int captatity) {
        elements = (T[])new Object[captatity];
        this.N=0;
    }
    /**
     * 清空列表
     */
    public void clear(){
        this.N=0;
    }
    /**
     * 判断线性表为空
     * @return
     */
    public boolean isEmpty(){
        return this.N==0;
    }
    /**
     * 获取线性表中元素个数
     * @return
     */
    public int length(){
        return this.N;
    }
    /**
     * 获取线性表的第i个元素的值
     * @param i
     * @return
     */
    public T get(int i){
        if (i<0 || i>=N){
            throw new RuntimeException("当前元素不存在!");
        }
        return elements[i];
    }

    /**
     * 向线性表添加元素T
     * @param t
     */
    public void add(T t){
        if(this.N==elements.length){
            throw new RuntimeException("当前表已满");
        }
        elements[N++]=t;
    }
    /**
     * 在i位置插入元素T
     * @param t
     * @param i
     */
    public void add(T t,int i){
        if (i==elements.length){
            throw new RuntimeException("当前表已满");
        }
        if (i<0 || i>N){
            throw new RuntimeException("插入的位置不合法");
        }
        for(int index=N;index>i;index--){
            elements[index]=elements[index-1];
        }
        elements[i] = t;
        this.N++;
    }
    /**
     * 删除第i个位置的元素
     * @param i
     * @return
     */
    public T remove(int i){
        if(i<0 || i>this.N){
            throw new RuntimeException("删除的位置不合法");
        }
        T t =elements[i];
        for(int index=i;index<this.N-1;index++){
            elements[index]=elements[index+1];
        }
        return t;
    }
    /**
     * 返回线性表中首次出现的指定 数据元素的位置,不存在就返回-1
     * @param t
     * @return
     */
    public int indexof(T t){
        for(int i=0;i<this.N;i++){
            if(elements[i].equals(t)){
                return i;
            }
        }
        return -1;
    }

}

顺序表的遍历

一般作为容器存储数据,都会向外提供遍历的方式。在Java中,遍历集合的方式一般都是用foreach循环,如果想让squenceList也可以支持foreach循环,则需要做下面的操作:

  1. 让SquenceList实现Iterable接口重写iterator方法
  2. 在SquenceList内部提供一个内部类者匿名内部类重写hasNext方法和next()方法(hasNext 容器中是否还有元素,next方法是返回当前对象

Java代码实现遍历

import java.util.Iterator;

public class Squencelist<T> implements Iterable<T> {
    private T[]elements;//保存
    private int N;//元素个数

    public Squencelist(int captatity) {
        elements = (T[])new Object[captatity];
        this.N=0;
    }
    /**
     * 清空列表
     */
    public void clear(){
        this.N=0;
    }
    /**
     * 判断线性表为空
     * @return
     */
    public boolean isEmpty(){
        return this.N==0;
    }
    /**
     * 获取线性表中元素个数
     * @return
     */
    public int length(){
        return this.N;
    }
    /**
     * 获取线性表的第i个元素的值
     * @param i
     * @return
     */
    public T get(int i){
        if (i<0 || i>=N){
            throw new RuntimeException("当前元素不存在!");
        }
        return elements[i];
    }
    /**
     * 向线性表添加元素T
     * @param t
     */
    public void add(T t){
        if(this.N==elements.length){
            throw new RuntimeException("当前表已满");
        }
        elements[N++]=t;
    }
    /**
     * 在i位置插入元素T
     * @param t
     * @param i
     */
    public void add(T t,int i){
        if (i==elements.length){
            throw new RuntimeException("当前表已满");
        }
        if (i<0 || i>N){
            throw new RuntimeException("插入的位置不合法");
        }
        for(int index=N;index>i;index--){
            elements[index]=elements[index-1];
        }
        elements[i] = t;
        this.N++;
    }
    /**
     * 删除第i个位置的元素
     * @param i
     * @return
     */
    public T remove(int i){
        if(i<0 || i>this.N){
            throw new RuntimeException("删除的位置不合法");
        }
        T t =elements[i];
        for(int index=i;index<this.N-1;index++){
            elements[index]=elements[index+1];
        }
        return t;
    }
    /**
     * 返回线性表中首次出现的指定 数据元素的位置,不存在就返回-1
     * @param t
     * @return
     */
    public int indexof(T t){
        for(int i=0;i<this.N;i++){
            if(elements[i].equals(t)){
                return i;
            }
        }
        return -1;
    }
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            private int current=0;
            @Override
            public boolean hasNext() {
                //容器中还是否还有元素
                return current<N;
            }

            @Override
            public T next() {
                return elements[current++];
            }
        };
    }
}

顺序表的容量可变

在我们使用SquenceList时,创建new SquenceList(10)创建一个对象,但是当我们添加元素时超过容量的大小呢??这时就会报错,就会出现数组越界错误

因此在添加元素时需要考虑容量的伸缩性,根据需要改变容量大小。

添加元素时:添加元素时,应该检查当前数组的大小是否可以容纳新的元素,不能就需要考虑扩容(思想:创建一个新的数组大小为现在的2倍的数组来存储

移除元素:可以考虑检查数组的大小是否太大,造成内存空间太大浪费。

代码实现:扩容和缩容

/**
* 扩容和缩容
* @param capacity
*/
public void reSize(int capacity){
    T []temp = elements;//保存原先数据
    elements =(T[])new Object[capacity];
    for(int i=0;i<temp.length;i++){
         elements[i]=temp[i];
    }
}
/**
* 向线性表添加元素T
* @param t
*/
public void add(T t){
    if(N >= elements.length){
         reSize(elements.length*2);
    }
    elements[N++]=t;
}
/**
* 在i位置插入元素T
* @param t
* @param i
*/
public void add(T t,int i){
   if(N >= elements.length){
      reSize(elements.length*2);
    }
    if (i<0 || i>N){
       throw new RuntimeException("插入的位置不合法");
     }
    for(int index=N;index>i;index--){
        elements[index]=elements[index-1];
    }
    elements[i] = t;
    this.N++;
}

public T remove(int i){
   //缩容
   if(N < elements.length/4){
       reSize(elements.length/2);
   }
   if(i<0 || i>this.N){
       throw new RuntimeException("删除的位置不合法");
    }
   T t =elements[i];
   for(int index=i;index<this.N-1;index++){
       elements[index]=elements[index+1];
   }
   return t;
}

Java中ArrayList实现

Java中的ArrayList集合的底层也是一种顺序表,使用数组实现,同样提供了增删改查以及扩容。

  • 使用数组实现
  • 也有扩容操作
  • 也提供了遍历操作

顺序表的时间复杂度

无论数据元素多大,get(i)只需要elements[i]就可以取到对应元素,所以时间复杂度为O(1),add(int i,T t)每一次插入元素,都要把i位置后面的元素移动一位,所以时间复杂度为O(n),remove(int i)移除元素都需要把i后面的位置往前移动一位,时间复杂度为O(n).

可以可以看出对于顺序表的增删时间复杂为O(n),而对于获取只需要O(1).

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值