数据结构入门

数据结构的概述

定义

我们如何把现实中大量而反复的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应的操作,这个相应的操作也叫做算法。
  数据结构 = 个体+个体的关系
  算法 = 对存储数据的操作

狭义:

数据结构是专门研究数据存储的问题
数据的存储包含两方面:个体的存储 + 个体关系的存储

广义:

数据结构既包含数据的存储也包含数据的操作
对存储数据的操作就是算法


算法

狭义:

算法是和数据的存储方式密切相关

广义:

算法和数据的存储方式无关,这就是泛型思想

衡量算法的标准:

(1) 时间复杂度
大概程序要执行的次数,而并非是执行的时间(因为同一程序在不同机器上执行的时间是不一样的,有差异)
(2) 空间复杂度
算法执行过程中大概所占用的最大内存
(3) 难易程度
(4) 健壮性

数据结构的地位:

数据结构是软件中最核心的课程
程序 = 数据的存储 + 数据的操作 + 可以被计算机执行的语言


泛型

对于同一种逻辑结构,无论该逻辑结构的物理存储是什么样子的,我们可以对它执行相同的操作。


数据的存储有几种:

  1. 线性:
  • 连续存储:【数组】

元素类型相同,大小相等(数组传参,只要传进去首地址和长度就行)
 优点
  存取速度快
 点:
  事先必须知道数组的长度
  插入删除元素很慢
  空间通常是有限的
  需要大块连续的内存块

  • 离散存储【链表】

n个节点离散分配
彼此通过指针相连
每个节点只有一个前驱节点,每个节点只有一个后续节点
首节点没有前驱节点,尾节点没有后续节点。
 优点:
  空间没有限制
  插入删除元素很快
 缺点:
  存取速度很慢
 概念:
  首节点:第一个有效节点
  尾节点:最后一个有效节点
  头节点:头结点的数据类型和首节点的类型一样没有存放有效数据,最最前面的,是在首节点之前的,主要是为了方便对链表的操作。
  头指针:(指向头)指向头节点的指针变量
  尾指针:指向尾节点的指针
 分类:
   单链表:每一个节点有一个指针域
   双链表:每一个节点有两个指针域
   循环链表:能通过任何一个节点找到其他所有的节点
   非循环链表:非循环链表尾指针为空

线性结构的两种常见应用:
栈和队列是一种特殊的线性结构,是连续存储或离散存储的一种应用

定义:
  一种可以实现“先进后出“的存储结构,类似于箱子
 分类:
  静态栈
  动态栈
 算法:
  出栈
  压栈
 应用:
  缓冲处理,表达式求值,中断,内存分配,迷宫,函数调用

int main(void)
{
  int p;                          
  int * m = (int *)malloc(100);
}

// 如静态变量p和m是在栈中分配,有操作系统自动分配和释放。而(int *)malloc(100);执行后,将在堆中分配一块100字节的内存,由程序员手动分配。

栈的示意图

数据结构入门_第1张图片
image.png

队列

定义:
  一种可以实现“先进先出”的存储结构
 分类:
  链式队列: -----用链表实现(比较简单)
  静态队列: -----用数组实现 静态队列通常都必须是循环队列
 应用:
  所有和时间有关的事件都有队列的影子
 队列算法:
  入队
  出队
循环队列的讲解
  (1)静态队列为什么必须是循环队列

数据结构入门_第2张图片
image.png
    现在如果一个数组里面存了四个元素,那么front就只想第一个有效元素,而real指向最后一个元素的下一个元素,当增加元素时,只能在rear一端增加,即rear向上移。删除元素时,只能在front一端删除元素,即front向上移。但是如果一直增增删删,那么就会造成rear端溢出,而front端浪费,所以对于这种情况,可以采用循环队列的形式,即当rear已经指向数组最后一个元素时,那么就可以转而将rear指向数组的第一个空出来的空间。
  (2)循环队列需要几个参数来确定
     需要2个参数来确定:front 和 rear
  (3)循环队列各个参数的含义:
     2个参数在不同场合有不同的含义
     - 队列初始化
      front和rear的值都为零
     - 队列非空
      front代表的是队列的第一个元素
      rear代表的是队列的最后一个有效元素的下一个元素
     - 队列为空
      front和real的值相等,但不一定为零
  (4)循环队列入队伪算法讲解:
     两步完成:
      - 将值存入rear所代表的位置
      - 错误的写法 :rear = rear+1;
     正确的写法是:rear = (rear+1)%数组的长度
  (5)循环队列出队伪算法讲解
      Front =(front +1)%数组的长度
  (6) 如何判断循环队列是否为空
     如果front与rear的值相等,则该队列就一定为空
  (7)如何判断循环队列是否已满
     因为front的值可能比rear大,也可能比他小,也可能相等
     所以有两种方式:
      -多增加一个标识是否满的参数
      -少用一个元素【通常用此种方式】
     如果front和rear的值相差1,且front>rear,则证明队列已满。
     用C语言伪算法表示为:

       if ((rear+1)%数组长度==front)
           已满
       else
           未满

递归:

知识点一:函数的调用
 当在一个函数的运行期间调用另一个函数时,在运行被调函数之前,系统需要完成三件事:
    将所有的实际参数,返回地址等信息传递给被调函数。
    为被调函数的局部变量(也包括形参)分配存储空间
    将控制转移到被调函数的入口
 从被调函数返回主调函数之前,系统也要完成三件事:
    保存被调函数的返回结果
    释放被调函数所占的存储空间
    依照被调函数保存的返回地址将控制转移到调用函数

当有多个函数相互调用时,按照“后调用先返回”的原则,上述函数之间信息传递和控制转移必须借助“栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,将在栈顶分配一个存储区,进行压栈操作,每当一个函数退出时,就释放它的存储区,就进行出栈操作,当前运行的函数永远都在栈顶位置。
A函数调用A函数和A函数调用B函数在计算机看来是没有任何区别的,只不过用我们日常的思维方式比较怪异而已。

知识点二:递归必须满足的三个条件
    递归必须得有一个明确的终止条件
    该函数所处理的数据规模必须在递减
    这个转化必须是可解的
知识点三:递归和循环的优缺点比较
  递归:
    易于理解
    速度慢
    存储空间大
  循环:
    不易理解
    速度快
    存储空间小
知识点四:递归的应用
    树和森林就是以递归的方式定义的
    树和图的很多算法都是以递归来实现的
    很多数学公式就是以递归的方式定义的
斐波拉契序列:1 2 3 5 8 13 21 34

  1. 非线性:

定义:
 专业定义:
  1. 有且只有一个称为根的节点
  2. 有若干个互不相交的子树,这些子树本身也是一棵树。
 通俗的定义:
  1. 树是由节点和边组成。
  2. 每个节点只有一个父节点但可以有多个子节点
  3. 但有一个节点例外,该节点没有父节点,此节点称为根节点
树相关的专业术语:

  • 节点
  • 根节点
  • 父节点
  • 子节点
    有直接父子关系的才能叫子节点
  • 子孙节点
  • 堂兄弟节点
     其父节点是兄弟节点的为堂兄弟节点
  • 深度
     从根节点到对底层节点的层数称之为深度根节点是第一层
  • 叶子节点
     没有子节点的节点
  • 非终端节点
     实际就是非叶子节点

  •  子节点的个数称为度,整个树的度就是所有节点的度数中,度数最大的那个为整个树的度数
    树的分类:
  • 一般树
     任意一个节点的子节点的个数都不受限制,子节点的顺序可以更改也可以不能更改,能更改的树为无序一般树,不能更改的为有序一般树
  • 二叉树
     任意一个节点的子节点个数最多两个,且子节点的位置不可更改,即左子树和右子树的位置不可更改。

二叉树分类:

  1. 一般二叉树
  2. 满二叉树
      在不增加树的层数的前提下,无法再多添加一个节点的二叉树就是满二叉树,及所有的节点都是两个度数(两个子节点)
  3. 完全二叉树
      如果只是删除了满二叉树最底层最右边的连续若干个节点,这样形成的二叉树就是完全二叉树。满二叉树只是完全二叉树的一个特例。
  • 森林
    n个互不相交的树的集合
    树的存储:
  1. 二叉树的存储
  • 连续存储 用数组存储【适用于完全二叉树,不是完全二叉树的树补充为完全二叉树】
      优点:查找某个节点的父节点和子节点(也包括判断有没有子节点)方便快速
      缺点:耗用内存空间过大
  • 链式存储 两个指针域分别指向两个子节点,没有子节点的为空
      优点:耗用内存空间小
      缺点:查找父节点不方便
  1. 一般树的存储
  • 双亲表示法


    数据结构入门_第3张图片
    image.png
  • 孩子表示法


    数据结构入门_第4张图片
    image.png
  • 双亲孩子表示法


    数据结构入门_第5张图片
    image.png
  • 二叉树表示法
    先把一个普通树转换为二叉树,再存储二叉树。
     具体转换方法:
      设法保证任意一个节点的
        左指针域指向它的第一个孩子
       右指针域指向它的下一个兄弟
      只要能满足条件,就可以把一个普通树转化为二叉树。
      一个普通树转化成的二叉树一定没有右子树
  1. 森林的存储
    先把森林转化为二叉树,再存储二叉树
     具体转换方法:
      将森林中的每个树的节点当做兄弟存储;
      设法保证任意一个节点的
       左指针域指向它的第一个孩子,
       右指针域指向它的下一个兄弟
      只要满足条件,就可以把一个森林转化为二叉树


    数据结构入门_第6张图片
    image.png

二叉树的遍历:
 先序遍历【先访问根节点】
  方法:(1)先访问根节点
     (2)再先序访问左子树
     (3)再先序访问右子树

数据结构入门_第7张图片
image.png

 中序遍历【中间访问根节点】
 方法:(1)中序遍历左子树
    (2)再访问根节点
    (3)再中序遍历右子树
数据结构入门_第8张图片
image.png

 后序遍历【最后访问根节点】
 方法:(1)先中序遍历左子树
    (2)在中序遍历右子树
    (3)再访问根节点
数据结构入门_第9张图片
image.png

已知两种遍历序列求原始二叉树
 (1)****先序,中序和后序三种遍历中,只知道其中任意一个,是无法还原其原始的树结构的。
  (2)****通过先序和中序****或者****中序和后序****两种方式我们可以还原原始的二叉树。但是通过先序和后序是无法还原原始的二叉树的。
 换种说法只有通过先序和中序****或者****通过中序和后序我们才能唯一的确定一个二叉树的。

已知先序和中序求后序例子
 示例****1****:
  先序:****ABCDEFGH****(排在前面的为根节点)
  中序:****BDCEAFHG
  求后序:****DECBHGFA
 示例****2****:
  先序:****ABDGHCEFI
  中序:****GDHBAECIF
  求后序:****GHDBEIFCA

已知后序和中序求后序例子
 示例****1****:
  中序:****BDCE A FHG
  后序:****DECB HGF A****(排在后面的为根节点)
  求先序:****A BCDE FGH

树的应用
 (1)树是数据库中数据组织的一种重要形式**
 (2)操作系统父子进程的关系本身就是一棵树**
 (3)面向对象语言中类的继承关系本身就是一棵树**
 (4)赫夫曼树**

你可能感兴趣的:(数据结构入门)