day02 - 线性结构的实现与应用

 

 

一、线性结构的定义

    如果一个数据元素序列满足:

  • 除第一个和最后一个数据元素外,每个数据元素只有一个前驱和一个后继数据元素

  • 第一个数据元素没有前驱数据元素

  • 最后一个数据元素没有后继数据元素

二、线性表抽象数据类型

  • 线性表抽象数据类型主要包括两个方面:即数据集合 和 该数据集合上的操作集合

  • 数据集合可以表示为 a0,a1,a2,...an-1,每个数据元素的数据类型可以是任意的类型

  • 操作集合包括如下

    • 求元素个数

    • 插入

    • 删除

    • 查找

    • 判断是否为空

 

三、顺序表中有两种基本的存储:顺序结构、链式结构;

    顺序表接口代码

/**
*  线性表接口
*/
public interface List<E> {

    /**
     *  求元素个数
     * @return
     */
    int size();

    /**
     *  插入元素
     *  param index 插入的位置
     * @param e 插入的元素
     * @return
     */
    boolean add(int index, E e);

    /**
     *  删除元素
     * @param index 删除元素的下标
     * @return
     */
    boolean remove(int index);

    /**
     *  获取指定下标的元素
     * @param index 元素下标
     * @return
     */
    E get(int index);

    /**
     *  判断集合是否为空
     * @return
     */
    boolean isEmpty();

}

    3.1、使用顺序结构实现的线性表 -- 顺序表

/**
*  顺序表实现
*/
public class ArrayList<E> implements List<E> {

    // 元素集合
    private E[] arrayList;

    // 集合大小
    int size;

    // 集合最大大小
    private int MAX_SIZE = 50;

    public ArrayList() {
        arrayList = (E[]) new Object[MAX_SIZE];
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean add(int index, E o) {
        // 下标值是否正确
        if(index < 0 || index >= MAX_SIZE){
            throw new IndexOutOfBoundsException();
        }
        // 若集合容量已达最大值,则返回false,插入失败
        if(size >= MAX_SIZE - 1){
            return false;
        }
        // 把该下标及之后的元素全部往后移一位
        for(int i = size; i > index; i--){
            arrayList[i] = arrayList[i - 1];
        }
        arrayList[index] = o;
        size++;
        return true;
    }

    @Override
    public boolean remove(int index) {
        // 下标值是否正确
        if(index < 0 || index >= MAX_SIZE){
            throw new IndexOutOfBoundsException();
        }
        // 将该下标之后的值往前移一位,覆盖掉下标原来的值
        for(int i = index; i < size - 1; i++){
            arrayList[i] = arrayList[i + 1];
        }
        size--;
        return true;
    }

    @Override
    public E get(int index) {
        // 下标值是否正确
        if(index < 0 || index >= MAX_SIZE){
            throw new IndexOutOfBoundsException();
        }
        // 返回该下标的值
        return arrayList[index];
    }

    @Override
    public boolean isEmpty() {
        return size == 0? true:false;
    }
}

    3.2、使用链式结构实现顺序表 -- 单向链表

  • 注:链表类型:单向链表、单向循环链表、双向循环链表

  • 链表存储结构是基于指针实现的。我们把一个数据元素和一个指针称为节点。

  • 链式存储结构是用指针把相互直接关联的节点(即直接前驱节点或直接后继节点)链接起来。链式存储结构的线性表称为链表。

/**
*  单向链表实现
* @param <E>
*/
public class LinkedList<E> implements List<E> {

    // 链表元素
    class Node<E>{
        // 下一个节点
        Node next;
        // 节点值
        E e;
        public Node() {
        }
        public Node(E e){
            this.e = e;
        }
    }

    // 链表长度
    int size;

    // 头节点
    private Node head;

    // 尾节点
    private Node tail;

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean add(int index, E e) {
        // 若插入位置大于尾节点后两位,则插入失败
        if(index > size){
            return false;
        }
        // 创建一个新的节点
        Node<E> node = new Node<>(e);
        if(head == null){
            head = node;
            tail = head;
            size++;
            return true;
        }
        // 若 index == size,则直接插入到尾节点
        if(index == size){
            tail.next = node;
            tail = node;
            size++;
            return true;
        }

        // 把节点加入到第 index 位置下
        Node temp = head;
        // 找到 index 的前一个位置 temp
        for(int i = 1; i < index; i ++){
            temp = temp.next;
        }
        node.next = temp.next;
        temp.next = node;
        size++;
        return true;
    }

    @Override
    public boolean remove(int index) {
        // 判断节点位置
        if(index > size || index < 0){
            throw new IndexOutOfBoundsException();
        }
        // 删除该下标位置下的节点
        if(index == 0){ // 删除头节点
            head = head.next;
            size--;
            return true;
        }
        Node temp = head;
        // 找到下标节点并删除
        for(int i = 1; i < size; i ++){
            if(i == index){
                break;
            }
            temp = temp.next;
        }
        temp.next = temp.next.next;
        size--;
        return true;
    }

    @Override
    public E get(int index) {
        // 判断节点位置
        if(index > size || index < 0){
            throw new IndexOutOfBoundsException();
        }
        Node temp = head;
        // 找到下标节点
        for(int i = 1; i < size; i ++){
            temp = temp.next;
            if(i == index){
                break;
            }
        }
        return (E) temp.e;
    }

    @Override
    public boolean isEmpty() {
        return size == 0?true:false;
    }
}

 

    3.3、顺序表和单链表的比较

  • 顺序表的主要优点是支持随机读取,以及内存空间利用效率高;顺序表的主要缺点是需要预先给出数组的最大数据元素个数,而这通常很难准确作到。当实际的数据元素个数超过了预先给出的个数,会发生异常。另外,顺序表插入和删除操作时需要移动较多的数据元素。

  • 和顺序表相比,单链表的主要优点是不需要预先给出数据元素的最大个数。另外,单链表插入和删除操作时不需要移动数据元素。

  • 单链表的主要缺点是每个结点中要有一个指针,因此单链表的空间利用率略低于顺序表的。另外,单链表不支持随机读取,单链表取数据元素操作的时间复杂度为O(n);而顺序表支持随机读取,顺序表取数据元素操作的时间复杂度为O(1)。

 

 

 

源码地址 在 Astructure/day19_0121List01;包下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值