数据结构--原理论述

一、数据结构简介 数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。 数据元素:是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理,也被称为记录。 数据项:一个数据元素可以有若干个数据项组成。 数据结构:是互相之间存在一种或者多种特定关系的数据元素的集合。 逻辑结构与物理结构 1、逻辑结构:是指数据对象中数据元素之间的相互关系。
集合结构:集合结构中的数据元素除了同属于一个集合外,他们之间没有其他关系。
线性结构:线性结构中的数据元素之间是一对一的关系。
树形结构:树形结构中的数据元素之间存在一种一对多的层次关系。
图像结构:图形结构的数据元素是多对多的关系。 2、物理结构:是指数据的逻辑结构在计算机中的存储形式。主要有顺序存储和链式存储。
顺序存储结构:是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。 数据类型:是指一组性质相同的值的集合定义在此集合上的一些操作的总称。 原子类型:是不可再分解的基本类型; 结构类型:由若干个类型组合而成,是可以再分解的。

二、算法 算法是解决特定问题求解步骤的描述,在计算机中变现为指令的有限序列,并且每条指令表示一个或多个操作。 1、算法的特性:      输入输出:算法具有零个或多个输入;      有穷性:算法在执行有限的步骤之后,自动结束而不会出现无限循环;      确定性:算法的每一步骤都具有确定的 含义;      可行性:算法的每一步都必须是可行的,每一步都能够通过执行有限次数完成。 2、算法设计的要求:      正确性:算法的准确性是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题需求、能够得到问题的正确答案。      可读性:算法设计的另一目的是为了便于阅读、理解和交流。      健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果。      时间效率和存储量低:设计算法应该尽量满足时间效率高和存储量低的需求。 3、算法效率的度量方法:      事后统计方法:统计程序运行时间进行比较;      事前分析估算方法:在计算机程序编制前,依据统计方法对算法进行估计。           算法采用的策略、方法;           编译产生的代码质量;           问题的输入规模;           机器执行指令的速度。 4、函数的渐进增长 给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐进快于g(n)。 5、算法时间复杂度定义 在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间度量,记作:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率很f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。 推到大O阶方法 (1)用常数1取代运行时间中的所有加法常数; (2)在修改后的运行次数函数中,只保留最高阶项; (3)如果最高阶项存在且不是1,则去除与这个想相乘的常数。 得到的结果就是大O阶。 常见时间复杂度如表 常用的时间复杂度所耗费的时间从小到大依次是: O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n) 6、算法空间复杂度 算法的空间复杂度通过计算算法所需的存储框架实现,算法空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

三、线性表 1、线性表(List):零个或多个数据元素的有限序列。 若将线性表记为(a1,….,ai-1,ai,ai+1,….,an),则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。当i=1,2,…,n-1时,ai有且仅有一个直接后继,当i=2,3,….,n时,ai有且仅有一个直接前驱。 线性表元素的个数n(n>=0)定义为线性表的长度,当n=0是,称为空表。 2、线性标的抽象数据类型 ADT 线性表(List) Data      线性表的数据对象集合为{a1,a2,……,an},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱,除了最后一个元素an外,每个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。 3、线性表的顺序存储结构 顺序存储定义:线性表的顺序存储结构,指的是用一般地址连续存储单元依次存储线性表的数据元素。 线性表(a1,a2,….,an)的顺序存储示意图如下: 线性表是顺序存储结构,在存、读数据时,不管是哪个位置,时间复杂度都是O(1);而插入或删除时,时间复杂度都是O(n),线性表适合元素个数不太变化,而更多是存取数据的应用。 4、线性表顺序存储结构的优缺点 5、线性表的链式存储结构 n个节点(ai的存储映像)链结成为一个链表,即线性表(a1,a2,….,an)的链式存储结构,因为此链表的每个节点中只包含一个指针与,所以叫做单链表。 链表中第一个结点的存储位置叫做头指针。线性表的最后一个节点指针为“空”。 单链表的第一个节点前附设一个节点,称为头结点。 单链表中的节点有存放数据元素的数据域存放后继节点指针的指针域组成。 6、单链表的插入与删除 插入删除 单链表的插入与删除的时间复杂度是O(n),对于插入或删除数据越频繁的操作,单链表的效率优势就越明显。 单链表结构与顺序存储结构优缺点 经验性结论: 若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。若需要频繁插入和删除时,宜采用单链表结构。 当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑存储空间的大小问题。 7、静态链表:用数组描述的链表叫做静态链表。 8、循环链表 将单链表中终端节点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为循环单链表,检查循环链表(circular linked list)。 循环链表带有头结点的空链表 非空的循环链表 9、双向链表 双向链表(double linked list)是在单链表的每一个节点中,在设置一批指向其前驱节点的指针域。 双向链表的循环带头结点的空链表 非空的循环的带头结点的双向链表 双向链表中插入元素:假设存储元素e的结点为s,要实现将结点s插入到结点p和p->next之间需要如下几步。 s->prior=p  // 把p赋值给s的前驱 s->next=p->next  //把p->next赋值给s的后继 p->next ->prior=s  //把s赋值给p->next的前驱 p->next=s  //把s赋值给p的后继 双向链表插入元素: p->prior->next=p->next  // 把p->next赋值给p->prior的后继 p->next->prior=p->prior  //把p->prior赋值给p->next的前驱 free(p) 总结: 线性表是零个或多个相同类型的数据元素的有限序列,顺序存储结构指的是一段地址连续的存储单元依次存储线性表的数据元素(通常使用数组来实现)。由于顺序存储结构的插入很删除操作不方便,引出了链式存储结构,它具有不受固定的存储空间的限制,可以比较快捷的插入和删除操作的特点。

四、栈与队列 1、栈的定义: 栈(stack)是限定仅在表尾进行插入和删除的线性表。 允许插入很删除的一端称为栈顶(top),另一端称为栈低(bottom),不含任何数据元素的栈称为空栈。栈有称为后进先出(Last In First Out)的线性表,简称LIFO结构。 栈的插入操作,叫作进栈,也称压栈、入栈; 栈的删除操作,叫作出栈,也叫作弹栈。 注意:最先进栈的元素不一定是最后一个出栈的。 2、栈的顺序储存结构 栈的顺序存储是线性表顺序存储的简化,一般讲下标为0的一端作为栈底,因为首元素都在栈低,变化最小,所以让它作栈底。定义一个top变量来指示栈顶元素在数组中的位置,若存储栈的长度为StackSize,则栈顶位置top必须小于StackSize。当栈存在一个元素时,top等于0,因此通常把空栈的判定条件定位top等于-1. 3、栈的顺序存储结构 进栈操作 出栈操作 4、两栈共享空间 数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为栈的末端,即下标为数组长度n-1处。这样,两个栈如果增加元素,就是两端点向中间延伸。 5、栈的链式存储结构及实现 栈的链式存储结构,简称为链栈。栈顶放在单链表的头部,对于链栈来说,是不需要头结点的。 6、栈的链式存储结构 进栈操作:对于链栈的进栈push操作,假设元素值为e的新节点是s,top为栈顶指针。 出栈操作: 如果栈的使用过程中元素变化不可预料,有事很小,有事非常大,那么最好是用链栈,反之,如果他的变化在可控范围内,建议使用顺序栈会更好一些。 栈的应用—递归 菲波那切数列:前两项之和,构成了后一项。 递归定义:把一个直接调用自己或通过一系列的调用语句间接的调用自己的函数,称做递归函数。 每个递归定义必须至少有一个条件,满足时递归不再进行,即不再引用自身而是返回值退出。 7、栈的应用——四则运算表达式求值 后缀表达式:所有的符号都是在运算数字后面出现; 中缀表达式:标准四则运算表达式叫做中缀表达式。 计算机处理中缀表达式的步骤: (1)将中缀表达式转化为后缀表达式(栈用来进出运算的符号); (2)将后缀表达式进行运算得出结果(栈用来进出运算的数字)。 8、队列的定义 队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。 队列是一种先进先出(First In First Out)的线性表,简称FIFO,允许插入的一端称为队尾,允许删除的一端称为对头。 队列顺序储存的不足:出现假溢出的现象。 9、循环队列 为了解决假溢出的问题,把队列的头尾相接,这样的顺序存储结构称为循环队列。 若队列的最大尺寸为QueueSize,那么
队列满的条件是(rear+1)%QueueSize == front(取模“%”的目的是为了整合rear与front大小为一个问题)。 计算队列长度公式:(rear – front + QueueSize)% QueueSize 10、队列的链式存储结构及实现 队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。 空队列时,front和rear都指向头结点。 11、队列的链式存储结构 (1)入队操作:链表尾部插入结点; (2)出对操作:头结点的后继结点出队,将头结点的后继改为他后面的结点,若链表除头结点外只剩一个元素时,则需要rear指向头结点。 在确定队列长度最大值得情况下,建议用循环队列,如果无法预估队列的长度时,则用链队列。 12、总结 栈(stack)是限定仅在表尾进行插入和删除操作的线性表。 队列(queue)是指允许在一端进行插入操作,而在另一端进行删除操作的线性表。 对于栈来说,如果 是两个相同数据类型的栈,则可以用数组的两端做栈底的方法来让两个栈共享数据,这就可以最大化地利用数组的空间。 对于队列来说,为了避免数组插入和删除是需要移动数据,于是引入了循环队列的,使得对头和队尾可以在数组中循环变化。解决了移动数据的时间损耗,使得本来插入和删除是O(n)的时间负责度变成了O(1). 它们也可以通过链式存储结构来实现,实现原则上与线性表基本相同。

五、串 串(string)是由零个或多个字符组成的有限序列,又叫字符串。一般记为s=“a1a2…..an”(a>=0),其中,s是串的名称,用双引号括起来的字符序列是串的值。串中的字符数目n称为串的长度。零个字符的串称为空串(null string)。 1、给定两个串:s=“a1a2…..an”,t=“b1b2…..bm”,当满足一下条件之一时,s<t. (1)n<m,且ai=bi(i=1,2,…..,n); (2)存在某个k=<min(m,n),使得ai=bi(i=1,2,…..,k-1),ak<bk. 2、串的存储结构 (1)顺序存储:串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区。 (2)串的链式存储结构:一个节点对应一个字符,就会存在很大的空间浪费,因此,一个节点可以存放一个字符,也可以考虑存放多个字符,最后一个节点若是未被占满时,可以用“#”或其他非串值字符补全。 3、
朴素的模式匹配算法 子串的定位操作通常称做串的模式匹配。 4、
KMP模式匹配算法 KMP算法可以大大避免重复遍历的情况。 KMP模式匹配算法原理 j值得多少取决于当前字符之前的串的钱后缀的相似度。 串各个位置的j值得变化定义为一个数组next,那么next的长度就是串的长度。

六、树 1、树的定义 树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一棵非空树中:(1)有且仅有一个特定的称为根(Root)的结点; (2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、…..、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。 2、结点分类 结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支结点。除根节点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。 3、结点的关系 结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)。 同一个双亲的孩子之间互称兄弟(Sibling)。 结点的祖先是从根到该结点所经分支上的所有结点。 以某结点为根的子树中的任一结点都成为该结点的子孙。 4、树的其他相关概念 结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层。双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度(Depth)或高度。 如果将树中结点的各子树看成从左到右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。 森林(Forest)是m(m>=0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。 5、树的存储结构 双亲表示法、孩子表示法、孩子兄弟表示法。 (1)双亲表示法 在每个结点中,附设一个指示器指示器双亲结点到链表中的位置。 data是数据域,存储结点的数据信息,parent是指针域,存储该结点的双亲在数组中的下标。 (2)孩子表示法 把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。 将双亲表示法跟孩子表示法结合起来,叫做双亲孩子表示法,该方法的优点是可以快速确定结点的双亲。 (3)孩子兄弟表示法 任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我们设置两个指针,分别指向该节点的第一个孩子和此节点的右兄弟。 6、二叉树的定义 二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空(称为空二叉树),或者由一个根节点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。 7、二叉树的特点 (1)每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。 (2)左子树和右子树是有顺序的,次序不能任意颠倒。 (3)即使书中某结点只有一棵子树,也要区分它是左子树还是右子树。 8、二叉树具有五种基本形态 (1)空二叉树 (2)只有一个根节点 (3)根节点只有左子树 (4)根节点只有右子树 (5)根节点既有左子树又有右子树 9、特殊的二叉树 (1)斜树:所有的结点都只有左子树的二叉树叫做左斜树。所有结点都是右子树的二叉树叫做右斜树。 (2)满二叉树:在一个二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

特点:        叶子只能出现在最下一层,出现在其他层就不可能达成平衡;        非叶子结点的度一定是2;        在同样深度的二叉树中,满二叉树的结点个数最多,叶子树最多。 (3)完全二叉树 对一棵具有n个结点的二叉树按层序编号,如果编号i(1=<i=<n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。 满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的。 特点:        叶子结点只能出现在最下两层;        最下层的叶子一定集中在左部连续位置;        倒数二层,若有叶子结点,一定都在右部连续位置;        如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况;        同样节点数的二叉树,完全二叉树的深度最小。 10、二叉树的性质 性质1:在二叉树的第i层上至多有2^(i-1)个结点(i>=1) 性质2:深度为k的二叉树至多有2^k -1个结点(k>=1) 性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1 11、二叉树的存储结构 二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现节点之间的逻辑关系。 将上面二叉树存入数组 12、二叉链表 二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,我们称这样的链表叫做二叉链表。 其中data是数据域,lchild和rchild都是指针域,分别存放指向左孩子和右孩子的指针。 13、遍历二叉树 二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序一次访问二叉树中所有结点,使得每一个结点被访问一次且仅被访问一次的。 二叉树的遍历方法: (1)前序遍历:规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。 遍历顺序是:ABDGHCEIF (2)中序遍历:规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后访问根结点,最后终须遍历右子树。 遍历顺序:GDHBAEICF (3)后序遍历:规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点。 遍历顺序:GHDBIEFCA (4)层序遍历:规则是若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对接点逐个访问。 遍历顺序:ABCDEFGHI 14、线索二叉树 指向前驱和后继的指针称为线索,加上线索的二叉树称为线索链,相应的二叉树就称为线索二叉树。 15、树、森林与二叉树的转换 (1)将树转换为二叉树的步骤如下:           加线,在所有兄弟结点之间加一条连线。           去线,对树中每一个结点,只保留他与第一个孩子结点的连线,删除与其他孩子结点之间的连线。           层次调整,以树的根节点为轴心,将整棵树旋转一定的角度,使之结构层次分明。注意第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结点的右孩子。 (2)森林转换为二叉树           把每棵树转换为二叉树           第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。 (3)二叉树转换为树           步骤如下:           加线,若某结点的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点….都作为此节点的孩子。将该节点与这些右孩子结点用线连接起来。           去线,删除原二叉树中所有结点与其右孩子结点的连线。           层次调整,使之结构层次分明。 (4)二叉树转换为森林      一棵二叉树的根结点有右孩子,就是森林,没有就是一棵树。      转换步骤如下:      从根结点开始,若右孩子存在,则吧与右孩子结点的连线删除,在查看分离后的二叉树,肉右孩子存在,则连线删除。直到所有右孩子连线都删除为止,得到分离的二叉树。      再讲每棵分离的二叉树转换为树即可。 16、树与森林的遍历 树的遍历:      先根遍历      后根遍历 森林的遍历:      前序遍历      后序遍历

七、图 1、图的定义 图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。 图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的。 2、各种图的定义 (1)无向边:若顶点vi到vj之间的边没有方向,则称这条边为无向边(Edge),用无序偶对(vi,vj)来表示。如果图中任意两个顶点之间的边都是无向边,则称该图为无向图。 (2)有向边:若从顶点vi到vj的边有方向,则称这条边为有向边,也称为弧。连接顶点A到D的有向边是弧,A是弧尾,D是弧头,<A,D>表示弧。 在图中,若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图。 在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。 在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称改图为有向完全图。 与图的边或互相关的数叫做权,这种带权的图通常称为网。 假设有两个图G=(V,{E})和G’=(V’,{E’}),如果V’属于V且E’属于E,则称G’为G的子图。 3、图的顶点与边间关系 对于无向图G=(V,{E}),如果边(v,v’)属于E,则称顶点v和v’互为邻接点,即v和v’相邻接。边(v,v’)依附于顶点v和v’,或者说(v,v’)与顶点v和v’相关联。顶点v的度是和v相关联的边的数目,记为TD(V)。 对于有向图G=(V,{E}),如果弧<v,v’>属于E,则称顶点v邻接到顶点v’,顶点v’邻接自顶点v。弧<v,v’>和顶点v,v’相关联。以顶点v为头的弧的数目称为v的入度,记为ID(v);以v为尾的弧度的数目称为v的出度,记为OD(v);顶点v的度为TD(v)=ID(v)+OD(v).

点赞