数据结构基础学习之(串与数组)

主要知识点学习

  • 串的基本概念及其抽象数据类型描述
  • 串的存储结构
  • 串的基本操作实现
  • 数组的定义、操作和存储结构
  • 矩阵的压缩存储

一、 串

字符串(串): 是由n(n>=0)各字符组成的有限序列

1. 串的抽象数据类型

public interface IString {
    void clear();//置空
    boolean isEmpty();//判空
    int length();//长度
    char charAt(int index);//获取指定下标的字符
    IString substring(int begin,int end);//截取子串
    IString insert(int offset,IString str);//插入
    IString delete(int begin,int end);//删除
    IString concat(IString str);//串连接
    int compareTo(IString str) ;//比较
    int indexOf(IString str,int begin);//子串定位
}

2. 顺序串及其实现

  1. 存储结构示意图
4.1顺序串存储结构.png
  1. 求子串操作
 /**
     * 截取子串
     *
     * @param begin 开始索引
     * @param end   结束索引
     * @return
     */
    @Override
    public IString substring(int begin, int end) {
        //1 判断开始截取位置是否合法
        if (begin < 0)
            throw new StringIndexOutOfBoundsException("起始位置不能小于0");

        //2 判断结束截取位置是否合法
        if (end > this.length)
            throw new StringIndexOutOfBoundsException("结束位置不能大于串的当前长度: end:" + end + "  length:" + length);

        //3. 开始位置不能大于结束位置
        if (begin > end)
            throw new StringIndexOutOfBoundsException("开始位置不能大于结束位置");

        if (begin == 0 && end == this.length) {
            return new SeqString(this);
        } else {
            //创建截取的字符数组
            char[] buffer = new char[end - begin];
            //复制字符
            for (int i = begin; i < end; i++) {
                buffer[i] = this.values[i];
            }
            return new SeqString(buffer);

        }
    }
  1. 插入操作
    public IString insert(int offset, IString str) {
        //判断偏移量是否合法
        if (offset < 0 || offset > this.length)
            throw new StringIndexOutOfBoundsException("插入位置不合法!");

        //获取插入串的长度
        int len = str.length();
        //计算所需的长度
        int newCount = this.length + len;
        //如果所需的长度大于串数组的容量
        if (newCount > this.values.length) {
            //扩充容量
            allocate(newCount);
        }

        //为插入的串腾出位置(往后移动len个位置)
        for (int i = this.length - 1; i >= 0; i--) {
            this.values[len + i] = this.values[i];
        }
        //复制
        for (int i = 0; i < len; i++) {
            this.values[offset + i] = str.charAt(i);
        }
        this.length = newCount;
        return this;
    }
  1. 删除操作
 public IString delete(int begin, int end) {
        //1 判断开始截取位置是否合法
        if (begin < 0)
            throw new StringIndexOutOfBoundsException("起始位置不能小于0");

        //2 判断结束截取位置是否合法
        if (end > this.length)
            throw new StringIndexOutOfBoundsException("结束位置不能大于串的当前长度: end:" + end + "  length:" + length);

        //3. 开始位置不能大于结束位置
        if (begin > end)
            throw new StringIndexOutOfBoundsException("开始位置不能大于结束位置");

        for (int i = 0; i < this.length - end; i++) {
            this.values[begin + i] = this.values[end + i];
        }
        this.length = this.length - (end - begin);
        return this;
    }
  1. 模式匹配操作

一、Brute-Force模式匹配算法

  • 操作过程示意图(网上搜索所得)
brute-force.gif
  • 代码实现
public int indexOf_BF(SeqString text, SeqString p, int begin) {
        //判断开始匹配的位置是否合法
        if (begin < 0 || begin > text.length - 1)
            throw new StringIndexOutOfBoundsException("判断开始匹配的位置错误: begin=" + begin);

        //标识主串text中的位置
        int i = begin;
        //标识子串p中的位置
        int j = 0;

        //主串的长度
        int textLen = text.length;
        //子串长度
        int pLen = p.length;

        while (i < textLen && j < pLen) {
            //匹配字符
            //如果匹配,则继续下一个字符
            if (text.charAt(i) == p.charAt(j)) {
                ++i;
                ++j;
            } else {
                //如果匹配不成功,则退回上次匹配首位的下一位
                i = i - j + 1;
                j = 0;
            }
        }

        //如果匹配成功,返回子串序号
        if (j >= pLen) {
            return i - pLen;
        }
        return -1;
    }
  • 时间复制度分析

二、KMP(Knuth-Morris-Pratt)模式匹配算法

  1. 示意图(图来自)

    KMP.gif

  2. 阅读文章

  1. 说明
  • Brute-Force算法无论在哪里失败,每次都从成功匹配的下一个位置开始从新匹配,非常浪费时间, 而KMP算法减少了不必要的回溯,提升了效率。
  • 那么现在的问题是,如何利用上次匹配失败的信息,推断出下一次开始匹配的位置?
  • 可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)
  1. 针对搜索词: ABCDABD计算部分匹配表
  • 相关公式
    - 1. 对应的部分匹配值 = 前缀字符 和 后缀字符 的 最长共有元素的长度
    - 2. 匹配失败移动的距离 = 已匹配的字符数 - 对应的部分匹配值

  • 最长共有元素的长度(对于:ABCDABD)

已匹配字符 前缀 后缀 共有长度
A NULL NULL 0
AB [A] [B] 0
ABC [A,AB] [BC,C] 0
ABCD [A,AB,ABC] [BCD,CD,D] 0
ABCDA [A,AB,ABC,ABCD] [BCDA,CDA,DA,A] 1 {A}
ABCDAB [A,AB,ABC,ABCD,ABCDA] [BCDAB,CDAB,DAB,AB,B] 2 {AB}
ABCDABD [A,AB,ABC,ABCD,
ABCDA,ABCDAB]
[BCDABD,CDABD,DABD,ABD,BD,D] 0
  • 匹配表
搜索词 A B C D A B D
部分匹配值(Match Value) 0 0 0 0 1 2 0
移动距离(Move distance) 1 2 3 4 4 4 7
  1. 部分匹配表的代码实现
  • 代码实现
private int[] getNext(SeqString p) {
        //匹配串的长度
        int patternLength = p.length;
        //匹配表;用于匹配过程中,跳过不需要再进行匹配的字符
        int partial_match[] = new int[patternLength];
        //部分匹配表中的第一个赋值为0,
        //因为只有一个已匹配字符,它没有前缀和后缀
        partial_match[0] = 0;
        //前后缀共有元素的长度(即前缀字符的最后一个下标)
        int length = 0;
        //已匹配字符最后的下标(后缀字符的最后一个下标)
        int currentIndex = 1;

        while (currentIndex < patternLength) {
            if (p.charAt(currentIndex) == p.charAt(length)) {
                //发现匹配
                //共有长度加一
                length = length + 1;
                //记录跳过字符数
                partial_match[currentIndex] = length;
                currentIndex = currentIndex + 1;
            } else {
                //没有匹配
                if (length != 0) {
                    //以AAACAAAA为例子 , 个人理解
                    //假设当前匹配的字符串为 AAAC , 前缀为AAA,AA,A  后缀为 AAC,AC,C
                    //则length = 2 (是当串为AAA时得到的最长前后缀公共字符长度), currentIndex = 3, 所以前缀AAA != AAC
                    //那么length = partial_match[1] = 1, AA != AC
                    //length = partial_match[0] = 0, A!=C
                    length = partial_match[length - 1];
                } else {
                    //length ==0 ,表示串AAAC没有最长前后缀公共字符
                    //赋值为0
                    partial_match[currentIndex] = 0;
                    //继续匹配下一个串 AAACA
                    currentIndex = currentIndex + 1;
                }
            }
        }
        return partial_match;
    }
  1. KMP算法实现
private int index_KMP(SeqString text, SeqString p) {

        int textLength = text.length;
        int patternLength = p.length;

        //计算部分匹配表
        int partial_match[] = getNext(p);

        int currentIndexText = 0;
        int currentIndexPattern = 0;

        while (currentIndexText < textLength && currentIndexPattern < patternLength) {
            if (text.charAt(currentIndexText) == p.charAt(currentIndexPattern)) {
                //匹配
                //转到下一个字符
                currentIndexPattern = currentIndexPattern + 1;
                currentIndexText = currentIndexText + 1;
            } else {
                if (currentIndexPattern != 0) {
                    // if no match and currentIndexPattern is not zero we will
                    // fallback to values in partial match table
                    // for match of largest common proper suffix and prefix
                    // till currentIndexPattern-1
                    currentIndexPattern = partial_match[currentIndexPattern - 1];
                } else {
                    // if currentIndexPattern is zero
                    // we increment currentIndexText for fresh match
                    currentIndexText = currentIndexText + 1;
                }
            }
        }
        //判断已匹配串的下标currentIndexPattern 是否大于 模式串的长度
        if (currentIndexPattern >= patternLength) {
            //是,则返回匹配模式串的开始位置
            return currentIndexText - patternLength;
        }
        return -1;
    }
  1. SeqString 完整代码

二、数组

1. 概念
  1. 数组: 是n(n>=1)个具有相同类型的数据元素a0,a1,a2,a3,...,an-1构成的有限序列
  2. 一维数组:可以看成一个顺序存储结构的线性表
  3. 二维数组(矩阵):其数据元素为一维数组的线性表
4.11二维数组的矩阵表示.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 字符串匹配KMP算法详解 1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有...
    张晨辉Allen阅读 2,402评论 0 3
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,806评论 0 13
  • 概述 串:字符串的简称,串是一种特殊的线性表,特殊在其数据元素都是一个字符。 数组和广义表可以看做是线性表的扩充,...
    Lost_Robot阅读 889评论 0 2
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,197评论 0 3
  • 一次国际夏令营,我参团印度旅游,和团里的一名中国孩子和一名美国孩子相谈甚欢,有一天的午餐是牛肉咖喱饭,一阵狼吞虎咽...
    原同学阅读 338评论 0 0