| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> 栈和队列能熟练使用么?&怎么存储矩阵呢?(更少的引用,更多的思考) -> 正文阅读 |
|
[数据结构与算法]栈和队列能熟练使用么?&怎么存储矩阵呢?(更少的引用,更多的思考) |
一、前言? ? ?在上一篇《数据结构内功修炼之线性表》中,采用了中规中矩的方法。文章是很长的,但是认真看下来你会发现——真的长!不过也难怪嘛,线性表里面顺序表和链表本来就是一切的基石,所以很多基本问题自然是要仔仔细细看的,当然看严蔚敏老师的书也更好。所以这一片博客,让我们在规矩的结构上,大胆各种发挥一波。??? ? ? ?按照惯例给出本章的知识结构吧! ? ? ?你可能会对一些名词奇怪,例如什么是共享栈?什么是双端队列,这里不是说栈和队列吗,为什么谈到了数组?——别担心,让我们一起往下思考 二、栈
1、顺序栈的一些操作思考。 (1)关于初始化栈顶指针的问题? ? ? ?你有没有想过,利用顺序表来操作栈的话肯定是要有栈顶指针来执行栈顶元素的,方便我们实现出栈、入栈、判空等重要的操作。假如顺序栈的引用设为S,那么初始化栈头指针是S.top=-1还是S.top=0呢? ? ? ?如果这两者有其中之一比较好,那就不会两个都存在,但不管是S.top=-1还是S.top=0,性质都是一样的。可是呢,不同的定义往往会影响操作。那么我们来看看,对于两者到底有什么区别呢?
? ? ?在严蔚敏老师的课本中,S.top表示栈顶元素在顺序栈中的位置,并且她让栈顶元素初始化为S.top=0。为了用C语言描述时更加方便,所以也引入了S.base指针,也就是初始化就指向栈底,之后一直不变的指针。这样判空就为S.top=S.base。而初始化的代码为:
? ? ?而当我们初始化S.top=-1时,初始化代码可以简单的写出这样(省略了S = new SElemType[MAXSIZE]这样的代码):
? ? ?从这里是看不出区别的,但是我们往下看:
? ? ?S.top=0的情况
? ? ?S.top=-1的情况
? ? ?我们发现当S.top=0时,我们在入栈的时候直接先赋值,栈顶指针再加一;而S.top=-1时,我们先栈顶指针加一,再赋值。难道这有什么规律吗?—— ? ? ?不!没有任何规律,你只要画画图就能够明白,这个问题毫无价值。
? ? ?那么出栈呢?当S.top=0时,出栈不就是先让栈顶指针减一,然后把值拿出去;当S.top=-1时,出栈不就是直接拿出去,然后栈顶指针再减一吗?有图有真相:
? ? ?取栈顶元素指针当然不变啦,不同的是因为初始化栈顶指针的不同,所以对于S.top=0这样的初始化来说,x=S.data[S.top-1]就好,而对于S.top=-1这样的初始化来说,x=S.data[S.top]就好。 2、共享栈
? ? ?两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空(这里我们初始赋为-1类似的情况);仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满(例如0号栈到了2,1号栈到了3,不就满了吗?)。当0号栈进栈时top0先加一在赋值,1号栈进站时top1先减1在赋值;出站时刚好相反。 3、关于链栈的一些思考 ? ? ?这里我们采用没有头结点的链表来表示栈,入栈和出栈操作都在链表的表头进行。那么我们需要注意什么呢? (1)链栈的出栈 ? ? ?如果你熟悉链表的操作的话,你会知道入栈也就是在表头添加结点是很简单的(如果你还记得我招小弟的说法,也就是新插入的结点指向L,然后这里是将L的指针移到插入的结点即可),但是不管是从表头删除结点还是从其它的位置删除,我们都需要注意一件事情,你猜是什么? ? ? ?当然是保存要要出结点的指针,以备释放啦,所以会有类似出栈的代码如下:
? ? ?解读一下上面的代码,不就是先判断是否为空,然后得到值,利用P指针保存当前栈顶,将下一个结点变为首元结点,然后释放原栈顶元素的空间即可。 二、队列
? ? ?好的,现在我们手里有两个工具,数组和链表。需要实现上面特点的数据结构,你会怎么做呢? 1、队列的顺序存储 ? ? ?如果要利用顺序表去实现这个功能,那么队头和队尾肯定需要有指针来指示操作的,并且我们需要设定初始情况,入队、出队以及判空,判断是否队列已满等情况的代码。给出顺序存储结构的表如下:
? ? ?拥有了这样的结构,那么我们初始化front=rear=0,然后入队的时候只需要插入新元素,然后尾指针加一;出队的时候只需要将头指针front加一。因此在非空队列中,头指针始终指向队头元素,而尾指针始终指向队尾元素的下一个位置。但是会出现这样的情况!!! ? ? ? 我们发现如果再次添加就满了,判断是否满了如果用rear=MAXSIZE是可以组织上面的情况发生的(假溢出),但很明显我们空间还有很多,所以,我们肯定要想办法把空间利用起来,比如让rear指针添加的时候到第0个位置不就好了吗,那肯定是取模的方法可以办到。也就是说入队的时候我们指针变化有rear = (rear+1)%MAXSIZE;这样上面的情况就是rear = (3+1)%4 = 0,尾指针就到下面来了。 ? ? ?所以对于运算指针的变化依旧有:
? ? ?(1)我们用增设表示元素个数的数据成员,当Q.size=MAXSIZE就满了。 ? ? ?(2)但是大多采用的都是牺牲一个单元来区分队空和队满,入队时少用一个队列单元,约定“队头指针在队尾指针的下一位置作为队列满的标志”。这里很好理解,你想啊,现在我们的队尾指针一直都是指向队尾元素的下一个,也就是说它当前的位置是空的,而下一个位置如果是队头指针的话,当然为空啦! ? ? ?所以判断的表达式不就是(rear+1)%MAXSIZE = front用来判断队满吗,而队空一直没有变,就是front=rear。 2、链式队列
? ? ?我们在设置链栈时,采用的是不设置头结点的链表形式,这里我们需要采用带头结点的链表来实现,为什么呢?你想啊,我们总是在头结点删除,如果不设置头结点的话,总是需要去找到首元结点(也就是当前要出队删除的结点的下一个结点记住它的指针来实现),而有了头结点,直接头结点指向下一个结点就实现了,多方便呀)。所以关于链队,没有啥说的,但一定要注意链表的插入和删除操作,必要时画图帮助理解。 3、双端队列
? ? ?这里我们需要了解两个概念即可,其它的,画图都是可以解决的。 ? ? ?输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列称为输出受限的双端队列。 ? ? ?输入受限的双端队列:允许在一端进行插入和删除 ,但在另一端只允许删除的双端队列称为输入受限的双端队列。 三、数组(特殊矩阵的压缩存储)? ? ?在高级语言中,数组我们在熟悉不过。这里可以给出一个简短的定义:
? ? ?为什么在这里要提到数组,因为它其实也是线性表的推广。一维数组可以视为一个线性表;二维数组可以视为其元素也是定长线性表的线性表。 ? ? ?那,我们这里会介绍关于数据的小标存储关系吗?不会,这里要知道的是特殊举证的存储。 ? ? ?矩阵充满了现在计算机的应用,而矩阵的存储当然十分的重要。现在我们思考,除了一般的矩阵我们需要一一存储,特殊的矩阵如果在一一存储的话不就浪费了巨大的空间吗?所以我们需要了解如何去压缩存储。 1、对称矩阵 ? ? ?用线性代数的方法来说,对称矩阵就是矩阵转置之后等于本身的矩阵。但这显得抽象,而我们给出对一个n阶方阵A[1...n][1...n]中的任意一个元素aij都有aij = aji(1<=i,j<=n),则称为对称矩阵。这个时候你的脑中应该有一条对角线,线的两侧的元素都是对称的(例如y=x线对称)。 ? ? ?Why?对于对称矩阵来说,如果我们直接用二维矩阵存储元素,那么会浪费几乎一般的空间(大概是(n^2-n)/2空间)。所以我们可以选择只存储对角线及其上半部分或者下半部分的元素,这样看空间就节省下来了,那么怎么存储呢? ? ? ?首先我们需要一个一位数组空间B[n(n+1)/2]即可。 (1)存 ? ? ?存的时候我们可以直接利用双重的for循环把数据存到一位数组空间B中,例如代码:
? ? ?这样我们就将原本存储在二维数组a中的数据存到了B中,但是要怎么取呢?? (2)取 ? ? ?如果要从B中取a[i][j]这个元素,我们会发现有这样的下标关系: ? ? ?所以只需要带入i,j的值到B下标关系中,就可以取到相应的元素值(只需要推到一侧的,因为是对称矩阵,所以i<j时情况一致)。如果你自己手动的推到一下就会发现,这只是简单的等差数列求和的公式,即使记不住,现场推导也是可以的。 2、三角矩阵 ? ? ?定义:三角矩阵有下三角矩阵和上三角矩阵。 1)下三角矩阵 ? ? ?(1)存 ? ? ? ? ? 存的时候我们依旧可以利用上面的方法,将二维数组里面的元素存到一维数组B中。这里二维数组指的形式,不是真的先存到二维数组中再去转存到一维数组,这样空间节约在哪里呢?而是说我们有形如这样的二维数组,然后存到一维数组中就好,这样就压缩存储了。取出来的时候根据下标关系就可以了,也就实现了节省空间的做法。 ? ? ?(2)取 ? ? ?这里我省略推到的过程,因为下三角矩阵的推到关系和对称矩阵一致,不同的是之前在对称矩阵中我们申请的是B[n(n+1)/2]的数组,而下三角矩阵需要多一个来存储同一常量(例如0啥的),所以就有了上式。 2)上三角矩阵 ? ? ?(1)存:~~~~~~~~~~~~~~~~~~~~~·存进去即可,存到一个一维数组节省空间。 ? ? ?(2)取: ? ? ?这里我们可以一起来推到一下去aij的关系式情况,我们仅仅需要知道aij是在B数组中第几个就好,按行存储,所以: 当i<=j时,也就是上面的右上角部分的关系如下: 第一行:n个元素 第2行:n-1个元素 ... 第i-1行:n-i+2个元素 第i行:j-i个元素 所以等差数列求和不就有(i-1)(2n-i+2)/2+(j-i)。而i>j时,最后一个位置存放即可。 3、三对角矩阵 ? ? ?定义:对角矩阵也称为带状矩阵。对于n阶方阵A中的任一元素aij,当|i-j|<1时,有aij=0(1<=i,j<=n),则称为三对角矩阵,上图! ? ? ?(1)存:~同上 ? ? ?(2)取 ? ? ? ? ? 三对角矩阵取的话,我没有找到很好的规律,所以就给出结果吧,但有趣的是反取。也就是知道B数组中的位置,去找aij。 4、稀疏矩阵 ? ? ?至于稀疏矩阵的存储,我们可以采用三元组的方式,也可以采用十字链表法存储。这里不再赘述,给出一张三元组的图片就知道有多么便捷了。 总结:? ? ? 栈、队列和数组及其的重要,我们要熟悉它们的逻辑结构、存储结构和运算,冲冲冲! |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 16:34:39- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |