(一)线性表的定义与实现

1 定义

线性表是由同一类型数据元素构成的有序序列的线性结构。
线性表中元素的个数被称为线性表的长度;
当一个线性表中没有元素时,称为空表;
表的起始位置称为表头,结束位置称为表尾;

线性表的抽象数据类型描述为:
类型名称:线性表(List)
数据对象集:线性表是n(n ≥ 0)个元素构成的有序序列(a1,a2,…,an),其中a1是表的第一个元素,表头。an是表的最后一个元素,表尾。
操作集:对于一个具体的线性表 L ∈ List,一个表示位序的整数i,一个元素X∈ElementType,线性表的基本操作有:
(1) List MakeEmpty():初始化一个新的空线性表;
(2) ElementType FindKth(List L, int i):根据指定位序i返回L中相应元素ai
(3) Position Find(List L, ElementType X):已知X,返回线性表L中与X元素相同的第一个元素的位置;若不存在返回错误信息;
(4) bool Insert(List L, ElementType X, int i):在L的指定位序i前插入一个新元素X;成功则返回true,否则返回false;
(5) bool Delete(List L, int i):从L中删除指定位序i的元素;成功返回true,失败返回false;
(6) int Length(List L):返回线性表L的长度。

2 线性表的顺序存储实现

考虑到线性表的运算有插人、删除等,即表的长度是动态可变的,因此,数组的容量需设计得足够大。
假设用Data[ MAXSIZE]来表示,其中MAXSIZE是一个根据实际问题定义的足够大的整数,
线性表中的数据从Data[0]开始依次顺序存放。由于当前线性表中的实际元素个数可能未达到MAXSIZE多个,
因此需用一个变量Last记录当前线性表中最后一个元素在数组中的位置,即Last 起一个指针(实际是数组下标)的作用
,始终指向线性表中最后一个元素。表空时Last=-1。
存储结构如下图所示:
在这里插入图片描述

2.1 线性表的插入

设我们要插入的位序为i(从1开始),插入的元素值为x,此时我们需要将ai和an整体
后移一位为新元素腾出位置,再将x写入i-1位置,最后修改Last指向线性表最后一个元素即可。

在这里插入图片描述

2.2 线性表的删除

设我们要删除的位序为i(从1开始),此时我们需要将ai+1和an整体
前移一位,再将Last指向最后一个元素即可。

在这里插入图片描述

2.3 代码实现

Java实现

import java.lang.reflect.Array;

/**
 * 线性表的顺序存储实现
 */
public class LinList01 {
    public static void main(String[] args) {
        System.out.println("LinList01 -> staring...");
        Test();
        System.out.println("LinList01 -> end");
    }

    public static void Test() {
        LNode<Integer> l = new LNode<Integer>(Integer.class);
        l.insert(1, 11);
        l.insert(2, 12);
        l.insert(3, 13);
        l.insert(4, 14);
        l.insert(5, 15);
        l.insert(18, 16);
        int i = 0;
        while (i <= l.get_last())
            System.out.print(String.format("%d,", l.get_data()[i++]));

        System.out.println();
        System.out.println(l.findKth(5));
        System.out.println(l.findKth(6));
        System.out.println(l.find(32));
        System.out.println(l.getLength());

        l.delete(4);
        System.out.println(l.getLength());
        i = 0;
        while (i <= l.get_last())
            System.out.print(String.format("%d,", l.get_data()[i++]));
        System.out.println();
    }
}


/**
 * 线性表的顺序存储实现
 */
class LNode<T> {
    private static final int MAX_SIZE = 100;
    private static final int ERROR = -1;
    private T[] _data;

    public T[] get_data() {
        return _data;
    }

    private int _last;

    public int get_last() {
        return _last;
    }

    /**
     * 初始化
     */
    public LNode(Class<T> clazz) {
        _data = (T[]) Array.newInstance(clazz, MAX_SIZE);

        _last = -1;
    }

    /**
     * 根据指定位序返回线性表中相应元素
     *
     * @param i 元素位序从1开始
     */
    public T findKth(int i) {
        if (i < 1 || i > this._last + 1) {
            System.out.println(String.format("位序%d不存在", i));
            return null;
        }
        return this._data[i - 1];
    }

    /**
     * 在线性表查找元素
     *
     * @param x 需要查找的元素
     */
    public int find(T x) {
        int i = 0;
        while (i <= _last && _data[i] != x)
            i++;

        if (i > _last)
            return ERROR;
        else
            return i;
    }

    /**
     * 在线性表指定位置插入元素
     *
     * @param i 插入位序从1开始
     * @param x 插入元素
     */
    public boolean insert(int i, T x) {
        int j;
        if (this._last == MAX_SIZE - 1) {
            System.out.println("表满");
            return false;

        }
        // 检查插入的位序是否合法
        if (i < 1 || i > this._last + 2) {
            System.out.println("位序不合法");
            return false;
        }
        // 位序i及以后的元素顺序向后移
        for (j = this._last; j >= i - 1; j--) {
            this._data[j + 1] = this._data[j];
        }
        // 新元素插入第i位序
        this._data[i - 1] = x;
        // last仍指向最后元素
        this._last++;

        return true;
    }

    /**
     * 删除指定位序元素
     *
     * @param i 删除位序从1开始
     */
    public boolean delete(int i) {
        int j;
        // 检查位序是否越界
        if (i < 1 || i > this._last + 1) {
            System.out.println(String.format("位序%d不存在", i));
            return false;
        }

        // 将位序i+1后面的元素顺序向前移,覆盖掉需要删除的位序i
        for (j = i - 1; j <= this._last; j++)
            this._data[j] = this._data[j + 1];
        // last仍指向最后元素
        this._last--;

        return true;
    }

    /**
     * 返回线性表长度
     */
    public int getLength() {
        return this._last + 1;
    }

}

3 线性表的链式存储实现

由于顺序表的存储特点是用物理上的相邻实现了逻辑上的相邻,它要求用连续的存储单元顺序存储线性表中各元素,
因此,对顺序表插入、删除时需要通过移动数据元素来实现,影响了运行效率。
本节介绍线性表链式存储结构,它不需要用地址连续的存储单元来实现,因为它不要求 逻辑上相邻的两个数据元素物理上也相邻 \color{red}{逻辑上相邻的两个数据元素物理上也相邻} 逻辑上相邻的两个数据元素物理上也相邻,它是通过“链”建立起数据元素之间的逻辑关系
,因此对线性表的插入、删除不需要移动数据元素,只需要修改“链"。
用链表结构可以克服数组表示线性表的缺陷。
下图为单向链表的图示表示形式,它有n个数据单元,每个数据单元由数据域和链接域两部分组成。
数据域用来存放数值.图中用a1,a2,…,an表示。链接域是线性表数据单元的结构指针,用一带箭头的线段表示
,线性表的顺序是用各结点上指针构成的指针链实现的。

为了访问链表,必须先找到链表的第一个数据单元, 因此实际应用中常用一个称为“表头( H e a d e r )”的指针指向链表的第一个单元,并用他表示一个具体的链表 \color{red}{因此实际应用中常用一个称为“表头(Header)”的指针指向链表的第一个单元, 并用他表示一个具体的链表} 因此实际应用中常用一个称为表头(Header的指针指向链表的第一个单元,并用他表示一个具体的链表

在这里插入图片描述

3.1 插入

设我们要在线性表的位序为i(从1开始)的位置插入一个元素值为x。
由于链表的特性是由一个指针链来管理元素间的逻辑关系,那么当我们在线性表新增一个元素时我们需要知道这个元素的
前一个元素称为Prev和后一个元素称为Next就可以轻松在他们之间插入一个元素了。

如下图所示:

在这里插入图片描述

注意这里有一种特殊情况当我们插入的位序位置为1时,将新元素的Next设置为头指针,再将头指针指向新元素即可(要确保头指针始终指向链表的第一个节点位置)

3.2 删除

设我们要删除线性表的位序为i(从1开始)的元素值,此时我们只需要找到位序为i-1的元素Prev,修改指向关系为i+1的元素即可。
如下图所示:

在这里插入图片描述

注意这里有一种特殊情况当我们删除的位序位置为1时,仅需要将头指针指向位序为2的元素即可。

3.3 实现

Java实现

/**
 * 线性表的链式存储实现
 */
public class LinList02 {
    public static void main(String[] args) {
        System.out.println("LinList02 -> staring...");
        Test();
        System.out.println("LinList02 -> end");
    }

    public static void Test() {
        PNodeList<Double> l = new PNodeList<Double>();
        l.insert(1, 11.12);
        l.insert(2, 12.2);
        l.insert(3, 13.5);
        l.insert(4, 14.6);
        l.insert(5, 15.12);
        l.insert(18, 16.1);

        PNode tmp = l.get_head();
        while (tmp != null) {
            System.out.print(String.format("%f,", tmp.get_data()));
            tmp = tmp.get_next();
        }

        System.out.println();
        System.out.println(l.findKth(5));
        System.out.println(l.findKth(6));
        System.out.println(l.find(15.12));


        l.delete(1);
        l.delete(3);
        System.out.println(l.getLength());

        tmp = l.get_head();
        while (tmp != null) {
            System.out.print(String.format("%f,", tmp.get_data()));
            tmp = tmp.get_next();
        }
        System.out.println();
    }
}

class PNodeList<T> {
    private static final int ERROR = -1;
    private PNode<T> _head;

    public PNode get_head() {
        return _head;
    }

    public PNodeList() {
        _head = null;
    }

    /**
     * 查找指定位序元素
     *
     * @param k 位序从1开始
     */
    public T findKth(int k) {
        PNode<T> p = _head;
        int counter = 1;

        while (p != null && counter < k) {
            p = p.get_next();
            counter++;
        }

        if (counter == k && p != null)
            return p.get_data();
        else
            return null;
    }

    /**
     * 查找指定元素
     *
     * @param x 需要查找的元素
     */
    public PNode<T> find(T x) {
        PNode<T> p = _head;

        while (p != null && p.get_data() != x)
            p = p.get_next();

        return p;
    }

    /**
     * 在指定位序插入元素
     *
     * @param i 位序,从1开始
     * @param x 需要插入的元素
     */
    public boolean insert(int i, T x) {
        PNode<T> newNode;
        PNode<T> prev;

        // 生成新节点
        newNode = new PNode();
        newNode.set_data(x);

        // 插入位序为表头处理
        if (i == 1) {
            newNode.set_next(_head);
            _head = newNode;
            return true;
        }
        // 查找插入位序前一个元素 i-1
        prev = FindPrev(i);
        if (prev == null) return false;

        // 插入元素
        newNode.set_next(prev.get_next());
        prev.set_next(newNode);
        return true;
    }

    private PNode FindPrev(int i) {
        PNode prev = _head;
        int counter = 1;
        while (counter < i - 1 && prev != null) {
            prev = prev.get_next();
            counter++;
        }

        if (prev == null || counter != i - 1) {
            System.out.println("插入位序不合法");
            return null;
        }

        return prev;
    }

    /**
     * 删除指定位序节点
     *
     * @param i 位序
     */
    public boolean delete(int i) {
        PNode tmp;
        PNode prev;

        // 删除的位序为1
        if (i == 1) {
            tmp = _head;
            _head = tmp.get_next();
            tmp.set_next(null);
            return true;
        }
        // 查找Prev元素
        // 查找插入位序前一个元素 i-1
        prev = FindPrev(i);
        if (prev == null) return false;

        // 删除元素
        tmp = prev.get_next();
        prev.set_next(tmp.get_next());
        tmp.set_next(null);

        return true;
    }

    public int getLength() {
        PNode p = _head;
        int counter = 0;

        while (p != null) {
            counter++;
            p = p.get_next();
        }

        return counter;
    }


}

/**
 * 线性表链式存储实现
 */
class PNode<T> {

    private T _data;
    private PNode _next;

    public T get_data() {
        return _data;
    }

    public PNode get_next() {
        return _next;
    }

    public void set_data(T _data) {
        this._data = _data;
    }

    public void set_next(PNode _next) {
        this._next = _next;
    }
}

4. 链式存储和顺序存储比较

1、顺序存储实现简单,对元素查找速度快,单对于增删来说效率较低且表长固定,适合静态管理的数据。
2、链式存储实现,增删元素效率很高,查找效率偏低,适合频繁增删节点,且表长有较大变化的情况。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值