数据结构学习(一)

来源:互联网 发布:如何安装管家婆数据库 编辑:程序博客网 时间:2024/06/06 10:59

数据结构学习(一)

参考文献:吴伟民 李小妹 刘添添 黄剑锋 苏庆 林志毅 李杨.数据结构(校内教材修订版) 广东工业大学计算机学院,2015.6

目录:

1、AOE网基本概念:

2、单链表中,增加一个头结点是为了:

3、单链表实现的栈,栈顶指针为top(仅仅是一个指针),入栈一个p节点时,其操作步骤为:

4、满二叉树与完全二叉树与森林:

5、拓扑排序:

6、消息队列:

7、循环队列元素个数判断:

8、后缀表达式:

9、二叉树遍历:

10、将一个递归算法改为对应的非递归算法:


1、AOE网基本概念:

答:(1)AOE网(Activity on Edge Network,边表示活动网):在一个带权有向图中,如果顶点表示事件,弧表示活动,弧上的权值表示活动持续的时间,则称该图为AOE网。

(2)源点:AOE网中唯一一个入度为零的顶点。

(3)汇点:AOE网中唯一一个出度为零的顶点。

(4)关键路径(Critical Path):从源点到汇点的最长路径(即权值之和最大的路径)。

(5)关键活动:关键路径上的活动。

(6)事件v[i]的最早发生时间:指从源点开始到顶点v[i]的最大路径长度。

(7)事件v[i]的最迟发生时间:指在不推迟整个工期的前提下,事件v[i]允许的最晚发生时间。

(8)活动a[k]的最早开始时间:若活动a[k]是由弧<v[i],v[j]>表示,则活动a[k]的最早开始时间等于事件v[i]的最早发生时间。

(9)活动a[k]的最迟开始时间:在不推迟整个工期的前提下,a[k]必须开始的最晚时间。

(10)一个项目可以有多个、并行的关键路径。

 

2、单链表中,增加一个头结点是为了:

答:(1)方便运算的实现。保证了处理首元素结点和后面的结点的方法一致。

(2)对带头结点的链表,在表的任何结点之前插入结点或删除表中任何结点,所要做的都是修改前一结点的指针域,因为任何元素结点都有前驱结点。若链表没有头结点,则首元素结点没有前驱结点,在其前插入结点或删除该结点时操作会复杂些。

 

3、单链表实现的栈,栈顶指针为top(仅仅是一个指针),入栈一个p节点时,其操作步骤为:

答:单链表的栈,栈顶就是第一个节点,这里可以有两种实现:

(1)top节点表示真实的栈顶节点;

(2)top节点只是一个标识,其next才是栈顶节点。

对于(1),入栈操作是在top前插入节点:p->next = top; top = p;

对于(2),入栈操作是在top后插入节点:p->next = top->next; top->next = p;

 

4、满二叉树与完全二叉树与森林:

答:(1)满二叉树:一棵深度为k且有2^k - 1个节点的二叉树。

(2)完全二叉树:深度为k且含n个节点的二叉树,如果其每个节点都与深度为k的满二叉树中编号从1至n的节点一一对应,则称为完全二叉树。

(3)森林:m棵互不相交的树的集合。

(4)二叉树转换成森林中树的个数,与该根节点一直往右遍历到叶子节点的节点数相同。

(5)高度为h的完全二叉树最右可能有h或h - 1个节点,对应的森林所含的树的个数可能是h或h - 1;

而高度为h的满二叉树对应的森林所含的树的个数一定是h。

 

5、拓扑排序:

答:(1)拓扑排序:在有向无环图(Directed Acyclic Graph,DAG)中,将所有顶点在不违反前后次序的前提下排成的序列称为拓扑有序序列,简称拓扑序列。构造拓扑序列的过程称为拓扑排序。

(2)图的邻接表由顶点数组和每个顶点的邻接链表组成。顶点数组含有存储顶点信息的data域,以及指向其邻接链表的头指针firstArc域。

每个顶点的邻接链表存储邻接顶点信息,链表的节点有3个域,adjvex存储邻接顶点的位序,nextArc是指向下一个节点的指针,info存储边(弧)相关信息。

(3)对于n个顶点e条弧的有向无环图而言,若采用邻接表存储结构,计算每个顶点的入度需扫描邻接表中所有顶点,时间复杂度为O(n+e);建立入度为0的顶点序列,需扫描数组indegree,时间复杂度为O(n);同时,每个顶点需入队和出队一次,出队时,还需将每个顶点的邻接顶点的入度减1,即扫描顶点对应的邻接链表,时间复杂度为O(n+e)。因此,整个算法的时间复杂度为O(n+e)。

 

6、消息队列:

答:(1)消息队列:一个消息的链表。每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该消息队列的大量信息,包括消息队列键值、用户ID、组ID、消息队列中消息数目等等,甚至记录了最近对消息队列读写进程的ID。

(2)为什么要用Message Queue?

①解耦

在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息队列在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束

②冗余

有时在处理数据的时候处理过程会失败。除非数据被持久化,否则将永远丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。在被许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理过程明确的指出该消息已经被处理完毕,确保你的数据被安全的保存直到你使用完毕。

③扩展性

因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的;只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。

④灵活性和峰值处理能力

在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住增长的访问压力,而不是因为超出负荷的请求而完全崩溃。

⑤可恢复性

当体系的一部分组件失效,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

⑥送达保证

消息队列提供的冗余机制保证了消息能被实际的处理,只要一个进程读取了该队列即可。在此基础上,IronMQ提供了一个”只送达一次”保证。无论有多少进程在从队列中领取数据,每一个消息只能被处理一次。这之所以成为可能,是因为获取一个消息只是”预定”了这个消息,暂时把它移出了队列。除非客户端明确的表示已经处理完了这个消息,否则这个消息会被放回队列中去,在一段可配置的时间之后可再次被处理。

⑦顺序保证

在许多情况下,数据处理的顺序都很重要。消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。IronMO保证消息通过FIFO(先进先出)的顺序来处理,因此消息在队列中的位置就是从队列中检索他们的位置。

⑧缓冲

在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行—写入队列的处理会尽可能的快速,而不受从队列读的预备处理的约束。该缓冲有助于控制和优化数据流经过系统的速度。

⑨理解数据流

在一个分布式系统里,要得到一个关于用户操作会用多长时间及其原因的总体印象,是个巨大的挑战。消息系列通过消息被处理的频率,来方便的辅助确定那些表现不佳的处理过程或领域,这些地方的数据流都不够优化。

⑩异步通信

很多时候,你不想也不需要立即处理消息。消息队列提供了异步处理机制,允许你把一个消息放入队列,但并不立即处理它。你想向队列中放入多少消息就放多少,然后在你乐意的时候再去处理它们。

(3)常用Message Queue对比:

①RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP, SMTP, STOMP,也正因如此,它非常重量级,更适合于企业级的开发。同时实现了Broker构架,这意味着消息在发送给客户端时先在中心队列排队。对路由,负载均衡或者数据持久化都有很好的支持。

②Redis是一个基于Key-Value对的NoSQL数据库,开发维护很活跃。虽然它是一个Key-Value数据库存储系统,但它本身支持MQ功能,所以完全可以当做一个轻量级的队列服务来使用。对于RabbitMQ和Redis的入队和出队操作,各执行100万次,每10万次记录一次执行时间。测试数据分为128Bytes、512Bytes、1K和10K四个不同大小的数据。实验表明:入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如果数据大小超过了10K,Redis则慢的无法忍受;出队时,无论数据大小,Redis都表现出非常好的性能,而RabbitMQ的出队性能则远低于Redis。

③ZeroMQ号称最快的消息队列系统,尤其针对大吞吐量的需求场景。ZMQ能够实现RabbitMQ不擅长的高级/复杂的队列,但是开发人员需要自己组合多种技术框架,技术上的复杂度是对这MQ能够应用成功的挑战。ZeroMQ具有一个独特的非中间件的模式,你不需要安装和运行一个消息服务器或中间件,因为你的应用程序将扮演了这个服务角色。你只需要简单的引用ZeroMQ程序库,可以使用NuGet安装,然后你就可以愉快的在应用程序之间发送消息了。但是ZeroMQ仅提供非持久性的队列,也就是说如果down机,数据将会丢失。其中,Twitter的Storm中默认使用ZeroMQ作为数据流的传输。

④ActiveMQ是Apache下的一个子项目。 类似于ZeroMQ,它能够以代理人和点对点的技术实现队列。同时类似于RabbitMQ,它少量代码就可以高效地实现高级应用场景。

⑤Kafka是Apache下的一个子项目,是一个高性能跨语言分布式Publish/Subscribe消息队列系统,而Jafka是在Kafka之上孵化而来的,即Kafka的一个升级版。具有以下特性:快速持久化,可以在O(1)的系统开销下进行消息持久化;高吞吐,在一台普通的服务器上既可以达到10W/s的吞吐速率;完全的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现复杂均衡;支持Hadoop数据并行加载,对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka通过Hadoop的并行加载机制来统一了在线和离线的消息处理,Apache Kafka相对于ActiveMQ是一个非常轻量级的消息系统,除了性能非常好之外,还是一个工作良好的分布式系统。

 

7、在一个容量为25的循环队列中,若头指针front=18,尾指针rear=9,则该循环队列中共有___________个元素。

答:( maxSize + rear - front ) % maxSize = 16

 

8、后缀表达式:

答:(1)中缀表达式转化为后缀表达式的规则:

从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减),则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

如:9+(3-1)*3+10/2

1)初始化一空栈,用来对符号进出栈使用。

2)第一个字符是数字9,输出9,后面是符号“+”,进栈。

3)第三个字符是“(”,依然是符号,因其只是左括号,还未配对,故进栈。

4)第四个字符是数字3,输出,总表达式为9 3,接着是“-”进栈。

5)接下来是数字1,输出,总表达式为9 3 1,后面是符号“)”,此时,我们需要去匹配此前的“(”,所以栈顶依次出栈,并输出,直到“(”出栈为止。此时左括号上方只有“-”,因此输出“-”,总的输出表达式为9 3 1 -

6)接着是数字3,输出,总的表达式为9 3 1 - 3 。紧接着是符号“*”,因为此时的栈顶符号为“+”号,优先级低于“*”,因此不输出,进栈。

7)之后是符号“+”,此时当前栈顶元素比这个“+”的优先级高,因此栈中元素出栈并输出(没有比“+”号更低的优先级,所以全部出栈),总输出表达式为 9 3 1 - 3 * +.然后将当前这个符号“+”进栈。

8)紧接着数字10,输出,总表达式变为9 3 1-3 * + 10。

9)最后一个数字2,输出,总的表达式为 9 3 1-3*+ 10 2

10) 因已经到最后,所以将栈中符号全部出栈并输出。最终输出的后缀表达式结果为 9 3 1-3*+ 10 2/+

 

9、某二叉树的前序遍历序列为-+a*b-cd/ef,后序遍历序列为abcd-*+ef/-,问其中序遍历序列是_________。

答:1)根据前序遍历,- 为根节点,+为其左子树根节点

2)根据后序遍历,/为根节点的右子树的根节点

3)根据前序遍历的/ef,推断出/节点的左右子树的根节点分别为e、f

4)由此得出树为:

 

图9.1 树的层次图

所以中序遍历为a + b * c - d - e / f

注:中序遍历的结果为直观可辨识的表达式。

 

10、将一个递归算法改为对应的非递归算法:

答:使用栈来实现。递归之所以可以采用非递归方法实现是因为可以用栈的方式,当采用递归时,是由系统管理函数栈;而要写成非递归时必须由你自已来管理一个栈。


1 0
原创粉丝点击