数据结构(五) -- 向量

所谓序列(Sequence),就是依次排列的多个对象。比如,每一计算机程序都可以看作一个序列,它由一系列依次排列的指令组成,正是指令之间的次序决定了程序的具体功能。因此,所谓序列,就是一组对象之间的后继与前驱关系。在实际问题中,序列可以用来实现很多种数据结构,因此被认为是数据结构设计的基础。
栈、队列以及双端队列,都可以看作带有附加限制的序列。

** 向量(Vector)和列表(List)都属于序列 **

一,向量

对数组结构进行抽象与扩展之后,就可以得到向量结构,因此向量也称作数组列表(Array list)。向量提供一些访问方法,使得我们可以通过下标直接访问序列中的元素,也可以将指定下标处的元素删除,或将新元素插入至指定下标。为了与通常数组结构的下标(Index)概念区分开来,我们通常将序列的下标称为秩(Rank)。

假定集合 S 由n 个元素组成,它们按照线性次序存放,于是我们就可以直接访问其中的第一个元素、第二个元素、第三个元素……。也就是说,通过[0, n-1]之间的每一个整数,都可以直接访问到唯一的元素e,而这个整数就等于S 中位于e 之前的元素个数——在此,我们称之为该元素的秩(Rank)。不难看出,若元素e 的秩为r,则只要e 的直接前驱(或直接后继)存在,其秩就是r-1(或r+1)。

支持通过秩直接访问其中元素的序列,称作向量(Vector)或数组列表(Array list)。实际上,秩这一直观概念的功能非常强大——它可以直接指定插入或删除元素的位置。


二,向量ADT(AbstractDataType)

操作方法 功能描述
getSize() 报告向量中的元素数目
输入:无
输出:非负整数
isEmpty() 判断向量是否为空
输入:无
输出:布尔值
getAtRank(r) 若0 ≤ r < getSize(),则返回秩为r 的那个元素 ;否则,报错
输入:一个整数
输出:对象
replaceAtRank(r, e) 若0 ≤ r < getSize(),则将秩为r 的元素替换为e,并返回原来的元素 ;否则,报错
输入:一个整数和一个对象
输出:对象
insertAtRank(r, e) 若0 ≤ r ≤ getSize(),则将e 插入向量中,作为秩为r 的元素(原秩不小于r 的元素顺次后移),并返回原来的元素 ;否则,报错
输入:一个整数和一个对象
输出:对象
removeAtRank(r) 若0 ≤ r < getSize(),则删除秩为r 的那个元素并返回之(原秩大于r 的元素顺次前移);否则,报错
输入:一个整数
输出:对象

三,基于数组的简单实现

向量接口:

package dsa.Vector;

/*
 * 向量接口
 */
public interface Vector {

    // 返回向量中元素数目
    public int getSize();

    // 判断向量是否为空
    public boolean isEmpty();

    // 取秩为r的元素
    public Object getAtRank(int r) throws ExceptionBoundaryViolation;

    // 将秩为r的元素替换为obj
    public Object replaceAtRank(int r, Object obj)
            throws ExceptionBoundaryViolation;

    // 插入obj,作为秩为r的元素;返回该元素
    public Object insertAtRank(int r, Object obj)
            throws ExceptionBoundaryViolation;

    // 删除秩为r的元素
    public Object removeAtRank(int r) throws ExceptionBoundaryViolation;
}

当作为参数的秩越界时,对应的意外错为ExceptionBoundaryViolation:

package dsa.Vector;

/*
 * 当作为参数的数组下标、向量的秩或列表的位置越界时,本意外将被抛出
 */

public class ExceptionBoundaryViolation extends RuntimeException {

    public ExceptionBoundaryViolation(String err) {
        super(err);
    }
}

基于数组,可以直接实现向量ADT:

package dsa.Vector;

/*
 * 基于数组的向量实现
 */
public class Vector_Array implements Vector {
    private final int N = 1024;// 数组的容量
    private int n = 0;// 向量的实际规模
    private Object[] A;// 对象数组
    // 构造函数

    public Vector_Array() {
        A = new Object[N];
        n = 0;
    }

    // 返回向量中元素数目
    public int getSize() {
        return n;
    }

    // 判断向量是否为空
    public boolean isEmpty() {
        return (0 == n) ? true : false;
    }

    // 取秩为r的元素
    public Object getAtRank(int r)// O(1)
            throws ExceptionBoundaryViolation {
        if (0 > r || r >= n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        return A[r];
    }

    // 将秩为r的元素替换为obj
    public Object replaceAtRank(int r, Object obj)
            throws ExceptionBoundaryViolation {
        if (0 > r || r >= n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        Object bak = A[r];
        A[r] = obj;
        return bak;
    }

    // 插入obj,作为秩为r的元素;返回该元素
    public Object insertAtRank(int r, Object obj)
            throws ExceptionBoundaryViolation {
        if (0 > r || r > n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        if (n >= N)
            throw new ExceptionBoundaryViolation("意外:数组溢出");
        for (int i = n; i > r; i--)
            A[i] = A[i - 1];// 后续元素顺次后移
        A[r] = obj;// 插入
        n++;// 更新当前规模
        return obj;
    }

    // 删除秩为r的元素
    public Object removeAtRank(int r) throws ExceptionBoundaryViolation {
        if (0 > r || r >= n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        Object bak = A[r];
        for (int i = r; i < n; i++)
            A[i] = A[i + 1];// 后续元素顺次前移
        n--;// 更新当前规模
        return bak;
    }
}

四,基于可扩充数组的向量实现

上面给出的向量实现,有个很大的缺陷——数组容量N固定,我们希望向量能够根据实际需要,动态地扩充数组的容量。当然,Java 语言本身并不支持这一功能,与多数程序语言一样,Java 中数组的容量都是固定的。我们的策略是,一旦数组空间溢出(即n≥N),insertAtRank()方法就会做如下处理:

  1. 开辟一个容量为2N的新数组B
  1. 将A[ ]中各元素搬迁至B[ ],即B[i]=A[i],i=0, …, N-1
  2. 将A替换为B,即令A=B
package dsa.Vector;

/*
 * 基于可扩充数组的向量实现
 */
public class Vector_ExtArray implements Vector {
    private int N = 8;// 数组的容量,可不断增加
    private int n;// 向量的实际规模
    private Object A[];// 对象数组
    // 构造函数

    public Vector_ExtArray() {
        A = new Object[N];
        n = 0;
    }

    // 返回向量中元素数目
    public int getSize() {
        return n;
    }

    // 判断向量是否为空
    public boolean isEmpty() {
        return (0 == n) ? true : false;
    }

    // 取秩为r的元素
    public Object getAtRank(int r) throws ExceptionBoundaryViolation {
        if (0 > r || r >= n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        return A[r];
    }

    // 将秩为r的元素替换为obj
    public Object replaceAtRank(int r, Object obj)
            throws ExceptionBoundaryViolation {
        if (0 > r || r >= n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        Object bak = A[r];
        A[r] = obj;
        return bak;
    }

    // 插入obj,作为秩为r的元素;并返回该元素
    public Object insertAtRank(int r, Object obj)
            throws ExceptionBoundaryViolation {
        if (0 > r || r > n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        if (N <= n) {// 空间溢出的处理
            N *= 2;
            Object B[] = new Object[N];// 开辟一个容量加倍的数组
            for (int i = 0; i < n; i++)
                B[i] = A[i];// A[]中内容复制至B[]
            A = B;// 用B替换A(原A[]将被自动回收)
        }
        for (int i = n; i > r; i--)
            A[i] = A[i - 1];// 后续元素顺次后移
        A[r] = obj;// 插入
        n++;// 更新当前规模
        return obj;
    }

    // 删除秩为r的元素
    public Object removeAtRank(int r) throws ExceptionBoundaryViolation {
        if (0 > r || r >= n)
            throw new ExceptionBoundaryViolation("意外:秩越界");
        Object bak = A[r];
        for (int i = r; i < n - 1; i++)
            A[i] = A[i + 1];// 后续元素顺次前移
        n--;// 更新当前规模
        return bak;
    }
}

Java本身也提供了与向量ADT功能类似的两个类:java.util.ArrayList和java.util.Vector。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容