第一章数据结构与算法.docx
- 文档编号:24538171
- 上传时间:2023-05-28
- 格式:DOCX
- 页数:16
- 大小:27.76KB
第一章数据结构与算法.docx
《第一章数据结构与算法.docx》由会员分享,可在线阅读,更多相关《第一章数据结构与算法.docx(16页珍藏版)》请在冰豆网上搜索。
第一章数据结构与算法
第一节.算法
一.算法的基本概念
所谓算法是指解题方案的准确而完整的描述。
算法不等于程序,也不等于计算方法。
通常,程序的编制不可能优于算法的设计。
1.算法的基本特征:
作为一个算法,一般应具有以下几个基本特征
(1)可行性(effectiveness)
针对实际问题设计的算法,人们总是希望能够得到满意的结果。
但一个算法又总是在某个特定的计算工具上执行的。
因此,算法在执行过程中往往要受到计算工具的限制,使执行结果产生偏差。
(2)确定性(definiteness)
算法的确定性,是指算法中的每一个步骤都必须是有明确定义的,不允许有模棱两可的解释,也不允许有多义性。
(3)有穷性(finiteness)
算法的有穷性,是指算法必须能在有限的时间内做完,即算法必须能在执行有限个步骤之后终止。
(4)拥有足够的情报
一个算法是否有效,还取决于为算法所提供的情报是否足够。
通常,算法中的各种运算总是要施加至各个运算对象上,而这些运算对象又可能具有某种初始状态,这是算法执行的起点或是依据。
因此,一个算法执行的结果总是与输入的初始数据有关,不同的输入将会有不同的结果输出。
当输入不够或输入错误时,算法本身也就无法执行或导致执行有错。
一般来说,当算法拥有足够的情报时,算法才是有效的,而当提供的情报不够时,算法可能无效。
综上所述,所谓算法,是一组严谨地定义运算顺序的规则,并且每一个规则都是有效的,且是明确的,些顺序将在有限的次数下终止。
2.算法的基本要素
一个算法通常由两种基本要素组成:
一是对数据对象的运算和操作,二是算法的控制结构。
(1)算法中对数据的运算和操作:
每个算法实际上是按解题要求从环境能进行的所有操作中选择合适的操作所组成的一组指令序列。
因些,计算机算法就是计算机能处理的操作所组成的指令序列。
基本运算和操作有以下四类:
1)算术运算:
主要包括加、减、乘、除等运算。
2)逻辑运算:
主要包括“非”、“与”、“或”等运算。
3)关系运算:
主要包括“大于”、“小于”、“等于”、“不等于”等运算。
4)数据输出:
主要包括赋值、输入、输出等操作。
(2)算法的控制结构
一个算法的功能不仅取决于所选用的操作,而且还与各操作之间的执行顺序有关。
算法中各操作之间的执行顺序称为算法的控制结构。
而一个算法一般都可以用顺序、选择、循环三种基本控制结构组合而成。
3.算法设计的基本方法(即针对不同的问题,可以采用不同的方法):
计算机解题的过程实际上是在实施某种算法,这种算法称为计算机算法。
计算机算法不同于人工处理的方法。
(1)列举法:
列举法的基本思想是,根据提出的问题,列举所有可能的情况,并且问题中给定的条件检验哪些是需要的,哪些是不需要的。
其意思是指把算法可能涉及到的情况都一一列举出来进行相应的处理。
例如求等差数列从1加到100的和,用列举法来做就是把100个数都列举出来,然后一个一个的相加。
(2)归纳法:
归纳法的基本思想是,通过列举少量的特殊情况,经过分析,最后找出一般的关系。
显然归纳法要比列举法更能反映问题的本质,并且可以解决列举量为无限的问题。
从本质上讲,所谓归纳就是指通过观察一些简单而特殊的情况,最后总结出一般性的结论(即把实际问题抽象为数学模型)。
(3)递推:
所谓递推,是指从已知的初始条件出发,逐次推出所要求的各中间结果和最后结果。
其中初始条件或是问题本身已经给定,或是通过对问题的分析与化简而确定。
其意思是指根据已知条件一步一步的推导,最后得出证明结论。
例如数学上的几何证明题。
(4)递归
人们在解决一些复杂问题时,为了降低问题的复杂程度(如问题的规模等),一般总是将问题逐层分解,最后归结为一些最简单的问题。
这种将问题逐层分解的过程,实际上并没有对问题进行求解,而只是当解决了最后那些最简单的问题后,再沿着原来分解的逆过程逐步进行综合,这就是递归的基本思想。
其意思是指我们在处理一个复杂的问题时,往往是把这个复杂的问题一层一层的往外剥,剥到最里面时,发现最里面的问题是很好解决的,就先处理最里面的“心”的问题,再处理外面包着的一层一层的叶的问题。
例如求10!
可以归纳为10*9!
,再分为10*9*8!
……,10*9*8……*2*1,最后再按原路返回。
递归分直接递归和间接递归两种。
如果一个算法调用自己本身的算法就称为直接递归。
如果算法P调用另一个算法Q,而算法Q又调用算法P,则称为间接递归调用。
(5)减半递推技术
实际问题的复杂程度往往与问题的规模有着密切的联系。
因此,利用分治法解决这类实际问题是有效的。
所谓分治法,就是对问题分而治之。
工程上常用的分治法是减半递推技术。
所谓“减半”,是指将问题的规模减半,而问题的性质不变 ;所谓“递推”,是指重复“减半”的过程
例如“二分法查找”,指在一个数列里边查找一个数,先和中间的数做比较问题的规模得以不断地减半。
(6)回溯法
前面讨论的递推和递归算法本质上是对实际问题进行归纳的结果,而减半递推技术也是归纳法的一个分支。
在工程上,有些实际问题很难归纳出一组简单的递推公式或直观的求解步骤,并且也不能进行无限的列举。
对于这类问题,一种有效的方法是“试”。
通过对问题分析,找出一个解决问题的线索,然后沿着这个线索逐步试探,对于每一步的试探,若试探成功,就得到问题的解,若试探失败,就逐步回退,换别的线路再进行试探。
这种方法称为回溯法。
回溯法在处理复杂数据结构方面有着广泛的应用。
(其本质就是不断的“找路子试!
”)
二.算法复杂度
算法的复杂度主要包括时间复杂度和空间复杂度。
1.算法的时间复杂度
所谓算法的时间复杂度,是指执行算法所需要的计算工作量。
它不是时间的描述,而是计算工作量的描述。
算法的工作量是用算法的基本运算次数来度量,而算法所执行的基本运算次数是问题规模的函数,即算法工作量=f(n),其中n是指运算次数。
在同一个问题规模下,如果算法执行所需的基本运算次数取决于某一特定输入时,可以用以下两种方法来分析算法的工作量:
(1)平均性态(AverageBehavior)
所谓平均性态分析,是指用各种特定输入下的基本运算次数的加权平均值来度量算法的工作量。
(2)最坏情况复杂性(Worst-CaseComplexity)
所谓最坏情况分析,是指在规模为n时,算法所执行的基本运算的最大次数。
2.算法的空间复杂度
一个算法的空间复杂度,一般是指执行这个算法所需要的内存空间。
第二节.数据结构的基本概念
数据结构作为计算机的一门学科,主要研究和讨论以下3个方面的问题:
①数据集合中各数据元素之间所固有的逻辑关系,即数据的逻辑结构;
②在对数据进行处理时,各数据元素在计算机中的存储关系,即数据的存储结构;
③对各种数据结构进行的运算。
讨论以上问题的主要目的是为了提高数据处理的效率,所谓提高数据处理的效率主要包括两个方面:
一是提高数据处理的速度,二是尽量节省在数据处理过程中所占用的计算机存储空间。
一.什么是数据结构:
数据结构是指反映数据元素之间关系的数据元素集合的表示,更通俗地说,数据结构是指带有结构的数据元素的集合。
而所谓结构实际上就是指数据元素之间的前后件关系。
由上所述,一个数据结构应包含以下两个方面的信息:
A.表示数据元表的信息。
B.表示数据元素之间的前后件关系。
1.数据的逻辑结构:
在数据结构中,数据元素之间的前后件关系是指它们的逻辑关系,而与它们在计算机中的存储位置无关。
所谓数据的逻辑结构,是指反映数据元素之间逻辑关系的数据结构。
(即反映数据元素之间的前后件关系,其本质是一个按时间先后划分的顺序关系)。
数据的逻辑结构是对数据元素之间的逻辑关系的描述,它可以用一个数据元素的集合和定义在此集合中的若干关系来表示。
由前面的叙述可以知道,数据的逻辑结构有两个要素:
一是数据元素的集合,通常记为D(DATA);二是D上的关系,它反映了数据元素之间的前后件关系,通常记为R(RALATION)。
即一个数据结构可以表示成:
B=(D,R)
其中,B表示数据结构。
为了反映D中各数据元素之间的前后件关系,一般用二元组来表示。
例如,假设A与B是D中的两个数据,则二元组(A,B)表示A是B的前件,B是A的后件。
这样,在D中的每两个元素之间的关系都可以用这种二元组来表示。
例1.5(见教材第10页)一年四季的数据结构可表示成:
B=(D,R)
D={春,夏,秋,冬}
R={(春,夏),(夏,秋),(秋,冬)}
例1.6(见教材系10页)家庭成员数据结构可以表示成:
B=(D,R)
D={父亲,儿子,女儿}
R={(父亲,儿子),(父亲,女儿)}
常见的数据结构,后面会讲到包括线性结构,树结构,还有图结构。
3.数据的存储结构:
数据的逻辑结构在计算机存储空间中的存放形式称为数据的存储结构(也称数据的物理结构)。
由于数据元素在计算机存储空间中的位置关系可能与逻辑关系不同,因此,为了表示存放在计算机存储空间中的各数据元素之间的逻辑关系(即前后件关系),在数据的存储结构中,不仅要存放各数据元素的信息,还需要存放各数据元素之间的前后件关系的信息。
常见的存储结构有顺序、链式、索引等存储结构。
那么数据的逻辑结构和数据存储结构之间的关系是怎样的呢?
一种逻辑结构可以用多种存储结构来表示,反过来一种存储结构也可以表示为多种逻辑结构。
顺序存储方式主要用于线性的数据结构,它把逻辑上相邻的数据元素存储在物理上相邻的存储单元里,结点之间的关系由存储单元的邻接关系来体现。
链式存储结构就是在每个结点中至少包含一个指针域,用指针来体现数据元素之间逻辑上的联系。
二.数据结构的图表示:
一个数据结构除了用二元关系表示外,还可以直观地用图形表示。
在数据结构的图形表示中,对于数据集合D中的每一个数据元素用中间标有元素的方框表示,一般称之为数据结点,并简称为结点;为进一步表示各数据元素之间的前后件关系,对于关系R中的每一个二元组,用一条有向线段从前件结点指向后件结点。
例如,一年四季的数据结构可以用左图所示的图形来表示。
又如,反映家庭成员间辈分关系的数据结构可以用右图所示的图形不表示。
注:
方框表示数据的元素值,称为结点。
有向线段的箭头从前件结点指向后件结点,表示为各数据元素之间的前后件关系,其中没有前件的结点称为根结点,没有后件的结点称为终端结点(也称为叶子结点)。
三.线性结构和非线性结构:
如果在一个数据结构中一个数据元素都没有,则称该数据结构为空的数据结构。
在一个空的数据结构中插入一个新的元素后就变为非空;在只有一个数据元素的数据结构中,将该元素删除后就变为空的数据结构。
根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:
线性结构与非线性结构。
(1)如果一个非空的数据结构满足下列两个条件:
1有且只有一个根结点;
2每一个结点最多有一个前件,也最多有一个后件。
则称该数据结构为线性结构。
线性结构又称线性表。
在一个线性结构中插入或删除任何一个结点后还应是线性结构。
栈、队列等都为线性结构。
如果一个数据结构不是线性结构,则称之为非线性结构。
如前面例1.6中反映家庭成员间辈分关系的数据结构,它不是线性结构,而是属于非线性结构。
显然,在非线性结构中,各数据元素之间的前后件关系要比线性结构复杂。
常见的非线性结构有树、二叉树、图等。
线性结构与非线性结构都可以是空的数据结构。
而一个空的数据结构究竟是属于线性结构还是属于非线性结构,还要根据具体情况来确定。
如果对该数据结构的运算是按线性结构的规划来处理的,则属于线性结构;否则属于非线性结构。
(2)线性表的顺序存储结构具有以下两个基本特点:
1线性表中所有元素所占的存储空间是连续的;
2线性表中各数据元素在存储空间中是按逻辑顺序依次存放的。
元素ai的存储地址为:
ADR(ai)=ADR(a1)+(i-1)k,ADR(a1)为第一个元素的地址,k代表每个元素占的字节数。
(3)顺序表的运算有查找、插入、删除3种。
1.3栈
1.栈的基本概念
栈(stack)是一种特殊的线性表,是限定只在一端进行插入与删除的线性表。
在栈中,一端是封闭的,既不允许进行插入元素,也不允许删除元素;另一端是开口的,允许插入和删除元素。
通常称插入、删除的这一端为栈顶,另一端为栈底。
当表中没有元素时称为空栈。
栈顶元素总是最后被插入的元素,从而也是最先被删除的元素;栈底元素总是最先被插入的元素,从而也是最后才能被删除的元素。
栈是按照“先进后出”或“后进先出”的原则组织数据的。
例如,枪械的子弹匣就可以用来形象的表示栈结构。
子弹匣的一端是完全封闭的,最后被压入弹匣的子弹总是最先被弹出,而最先被压入的子弹最后才能被弹出。
2.栈的顺序存储及其运算
栈的基本运算有3种:
入栈、退栈与读栈顶元素。
①入栈运算:
在栈顶位置插入一个新元素;
②退栈运算:
取出栈顶元素并赋给一个指定的变量;
3读栈顶元素:
将栈顶元素赋给一个指定的变量。
1.4队列
1.1.队列的基本概念
队列是只允许在一端进行删除,在另一端进行插入的顺序表,通常将允许删除的这一端称为队头,允许插入的这一端称为队尾。
当表中没有元素时称为空队列。
队列的修改是依照先进先出的原则进行的,因此队列也称为先进先出的线性表,或者后进后出的线性表。
例如:
火车进遂道,最先进遂道的是火车头,最后是火车尾,而火车出遂道的时候也是火车头先出,最后出的是火车尾。
若有队列:
Q=(q1,q2,…,qn)
那么,q1为队头元素(排头元素),qn为队尾元素。
队列中的元素是按照q1,q2,…,qn的顺序进入的,退出队列也只能按照这个次序依次退出,即只有在q1,q2,…,qn-1都退队之后,qn才能退出队列。
因最先进入队列的元素将最先出队,所以队列具有先进先出的特性,体现“先来先服务”的原则。
队头元素q1是最先被插入的元素,也是最先被删除的元素。
队尾元素qn是最后被插入的元素,也是最后被删除的元素。
因此,与栈相反,队列又称为“先进先出”(FirstInFirstOut,简称FIFO)或“后进后出”(LastInLastOut,简称LILO)的线性表。
2.队列运算
入队运算是往队列队尾插入一个数据元素;退队运算是从队列的队头删除一个数据元素。
队列的顺序存储结构一般采用队列循环的形式。
循环队列s=0表示队列空;s=1且front=rear表示队列满。
计算循环队列的元素个数:
“尾指针减头指针”,若为负数,再加其容量即可。
1.5链表
在链式存储方式中,要求每个结点由两部分组成:
一部分用于存放数据元素值,称为数据域;另一部分用于存放指针,称为指针域。
其中指针用于指向该结点的前一个或后一个结点(即前件或后件)。
链式存储方式既可用于表示线性结构,也可用于表示非线性结构。
(1)线性链表
线性表的链式存储结构称为线性链表。
在某些应用中,对线性链表中的每个结点设置两个指针,一个称为左指针,用以指向其前件结点;另一个称为右指针,用以指向其后件结点。
这样的表称为双向链表。
在线性链表中,各数据元素结点的存储空间可以是不连续的,且各数据元素的存储顺序与逻辑顺序可以不一致。
在线性链表中进行插入与删除,不需要移动链表中的元素。
线性单链表中,HEAD称为头指针,HEAD=NULL(或0)称为空表。
如果是双项链表的两指针:
左指针(Llink)指向前件结点,右指针(Rlink)指向后件结点。
线性链表的基本运算:
查找、插入、删除。
(2)带链的栈
栈也是线性表,也可以采用链式存储结构。
带链的栈可以用来收集计算机存储空间中所有空闲的存储结点,这种带链的栈称为可利用栈。
1.6二叉树
1.6.1二叉树概念及其基本性质
1.二叉树及其基本概念
二叉树是一种很有用的非线性结构,具有以下两个特点:
①非空二叉树只有一个根结点;
②每一个结点最多有两棵子树,且分别称为该结点的左子树和右子树。
在二叉树中,每一个结点的度最大为2,即所有子树(左子树或右子树)也均为二叉树。
另外,二叉树中的每个结点的子树被明显地分为左子树和右子树。
在二叉树中,一个结点可以只有左子树而没有右子树,也可以只有右子树而没有左子树。
当一个结点既没有左子树也没有右子树时,该结点即为叶子结点。
例如,一个家族中的族谱关系如图1-1所示:
A有后代B,C;B有后代D,E;C有后代F。
典型的二叉树如图1-1所示:
详细讲解二叉树的基本概念,见表1-2。
图1-1二叉树图
表1-2二叉树的基本概念
父结点(根)
在树结构中,每一个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的根结点,简称树的根,例如,在图1-1中,结点A是树的根。
子结点和叶子结点
在树结构中,每一个结点可以有多个后件,称为该结点的子结点。
没有后件的结点称为叶子结点。
例如,在图1-1中,结点D,E,F称为叶子结点。
度
在树结构中,一个结点所拥有的后件个数称为该结点的度,所有结点中最大的度称为该树的度。
例如,在图1-1中,根结点A和结点B的度为2,结点C的度的为1,叶子结点D,E,F的度为0。
所以该树的度为2。
深度
定义一棵树的根结点所在层次为1,其它结点所在的层次等于它的父结点所在层次加1。
树的最大层次称为树的深度。
例如,在图1-1中,根结点A在第1层,结点B,C在第2层,结点D,E,F在第3层。
该树的深度为3。
子树
在树中,以某结点的一个子结点为根构成的树称为该结点的一棵子树。
2.二叉树基本性质
二叉树具有以下几个性质:
性质1:
在二叉树的第k层上,最多有(k≥1)个结点。
性质2:
深度为m的二叉树最多有个结点。
性质3:
在任意一棵二叉树中,度为0的结点(即叶子结点)总是比度为2的结点多一个。
性质4:
具有n个结点的二叉树,其深度至少为[log2n]+1,其中[log2n]表示取log2n的整数部分。
3.满二叉树与完全二叉树
满二叉树是指这样的一种二叉树:
除最后一层外,每一层上的所有结点都有两个子结点。
在满二叉树中,每一层上的结点数都达到最大值,即在满二叉树的第k层上有2k-1个结点,且深度为m的满二叉树有2m-1个结点。
完全二叉树是指这样的二叉树:
除最后一层外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。
对于完全二叉树来说,叶子结点只可能在层次最大的两层上出现:
对于任何一个结点,若其右分支下的子孙结点的最大层次为p,则其左分支下的子孙结点的最大层次或为p,或为p+1。
完全二叉树具有以下两个性质:
性质1:
具有n个结点的完全二叉树的深度为[log2n]+1。
性质2:
设完全二叉树共有n个结点。
如果从根结点开始,按层次(每一层从左到右)用自然数1,2,……,n给结点进行编号,则对于编号为k(k=1,2,……,n)的结点有以下结论:
1若k=1,则该结点为根结点,它没有父结点;若k>1,则该结点的父结点编号为INT(k/2);
2若2k≤n,则编号为k的结点的左子结点编号为2k;否则该结点无左子结点(显然也没有右子结点);
3若2k+1≤n,则编号为k的结点的右子结点编号为2k+1;否则该结点无右子结点。
1.6.2二叉树的遍历
在遍历二叉树的过程中,一般先遍历左子树,再遍历右子树。
在先左后右的原则下,根据访问根结点的次序,二叉树的遍历分为三类:
前序遍历、中序遍历和后序遍历。
在遍历二叉树的过程中,一般先遍历左子树,再遍历右子树。
在先左后右的原则下,根据访问根结点的次序,二叉树的遍历分为三类:
前序遍历、中序遍历和后序遍历。
(1)前序遍历
先访问根结点,然后遍历左子树,最后遍历右子树;并且在遍历左、右子树时,仍需先访问根结点,然后遍历左子树,最后遍历右子树。
例如,对图1-1中的二叉树进行前序遍历的结果(或称为该二叉树的前序序列)为:
A,B,D,E,C,F。
(2)中序遍历
先遍历左子树、然后访问根结点,最后遍历右子树;并且,在遍历左、右子树时,仍然先遍历左子树,然后访问根结点,最后遍历右子树。
例如,对图1-1中的二叉树进行中序遍历的结果(或称为该二叉树的中序序列)为:
D,B,E,A,C,F。
(3)后序遍历
先遍历左子树、然后遍历右子树,最后访问根结点;并且,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后访问根结点。
例如,对图1-1中的二叉树进行后序遍历的结果(或称为该二叉树的后序序列)为:
D,E,B,F,C,A。
1.7查找
1.7.1顺序查找
查找是指在一个给定的数据结构中查找某个指定的元素。
从线性表的第一个元素开始,依次将线性表中的元素与被查找的元素相比较,若相等则表示查找成功;若线性表中所有的元素都与被查找元素进行了比较但都不相等,则表示查找失败。
例如,在一维数组[21,46,24,99,57,77,86]中,查找数据元素99,首先从第1个元素21开始进行比较,比较结果与要查找的数据不相等,接着与第2个元素46进行比较,以此类推,当进行到与第4个元素比较时,它们相等,所以查找成功。
如果查找数据元素100,则整个线性表扫描完毕,仍未找到与100相等的元素,表示线性表中没有要查找的元素。
在下列两种情况下也只能采用顺序查找:
1如果线性表为无序表,则不管是顺序存储结构还是链式存储结构,只能用顺序查找;
2即使是有序线性表,如果采用链式存储结构,也只能用顺序查找。
1.7.2二分法查找
二分法查找,也称拆半查找,是一种高效的查找方法。
能使用二分法查找的线性表必须满足用顺序存储结构和线性表是有序表两个条件。
“有序”是特指元素按非递减排列,即从小到大排列,但允许相邻元素相等。
下一节排序中,有序的含义也是如此。
对于长度为n的有序线性表,利用二分法查找元素X的过程如下:
步骤1:
将X与线性表的中间项比较;
步骤2:
如果X的值与中间项的值相等,则查找成功,结束查找;
步骤3:
如果X小于中间项的值,则在线性表的前半部分以二分法继续查找;
步骤4:
如果X大于中间项的值,则在线性表的后半部分以二分法继续查找。
例如,长度为8的线性表关键码序列为:
[6,13,27,30,38,46,47,70],被查元素为38,首先将与线性表的中间项比较,即与第4个数据元素30相比较,38大于中间项30的值,则在线性表[38,46,47,70]中继续查找;接着与中间项比较,即与第2个元素46相比较,38小于46,则在线性表[38]中继续查找,最后一次比较相等,查找成功。
顺序查找法每一次比较,只将查找范围减少1,而二分法查找,每比较一次,可将查找范围减少为原来的一半,效率大大提高。
对于长度为n的有序线性表,在最坏情况下,二分法查找只需比较log2n次,而顺序查找需要比较n次。
1.8排序
1.交换类排序法
(1)冒泡排序法
首先,从表头开始往后扫描线性表,逐次比较相邻两个元素的大小,若前面的元素大于后面的元素,则将它们互换,不断地将两个相邻元素中的大者往后移动,最后最大者到了线性表的最后。
然后,从后到前扫描剩下的线性表,逐次比较相邻两个元素的大小,若后面的元素小于前面的元素,则将它们互换,不断地将两个相邻元素中的小者往前移动,最后最小者到了线性表的最前面。
对剩下的线性表重复上述过程,直到剩下的线性表变空为止,此时已经排好序。
在最坏的情况下,冒泡排序需要比较次数为n(n-1)/2。
(2)快速排序法
任取待排序序列中的某个元素作为基准(一
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第一章 数据结构与算法 数据结构 算法