数据结构定义
我们如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应操作,这个相应的操作也叫算法。
数据结构是专门研究数据存储的问题。
数据的存储包含两方面:个体的存储+个体关系的存储
数据结构 = 个体存储 + 个体的关系存储
广义上:数据结构既包含数据的存储也包含数据的操作
算法是对数据存储的操作
算法 = 对存储数据的操作
算法 :
狭义的算法: 算法是和数据的存储方式密切相关
广义上的算法:算法和数据的存储方式无关,这是泛型思想
解题的方法和步骤
衡量算法的标准
1.时间复杂度:大概程序要执行的次数,而非执行的时间
2.空间复杂度:算法执行过程中大概所占用的最大内存
3.难易程度
4.健壮性
数据结构的地位:软件中最核心的课程
程序 = 数据的存储 + 数据的操作 + 可以被计算机执行的语言
预备知识
指针
指针的重要性:指针是C的灵魂
定义
地址:
地址就是内存单元的编号
从0开始的非负整数
范围:0 --- FFFFFFFF[0 -- 4G-1]
指针:
指针就是地址,地址就是指针
指针变量是存放内存单元地址的变量
指针的本质是一个操作受限的非负整数
注意:指针变量也是变量,只不过它存放的不能是内存单元的内容,只能存放内存单元的地址
如何通过被调函数修改主调函数中的普通变量的值
1.实参为相关变量的地址
2.形参为以该变量的类型为类型的指针变量
3.在被调函数中通过*形参变量名的方式就可以修改主调函数中普通变量的值
分类:
1.基本类型的指针
2.指针和数组的关系
int *p; //p是个变量的名字,int *表示改p变量只能存储int类型变量的地址
结构体
为什么会出现结构体
为了表示一些复杂的数据,而普通的的基本类型变量无法满足要求
什么叫结构体
结构体是用户根据实际需要自己定义的复合数据类型
如何使用结构体
struct Student {
int sid;
char name[200];
int age;
};
struct Student st = {99,"zhangsan",20};
struct Student *pst = &st;
1.
st.sid
2.
pst->sid
pst所指向的结构体变量中的sid这个成员
注意事项
1.结构体变量不能加减乘除,但可以相互赋值
2.普通结构体变量和结构体指针变量作为函数函数传参的问题
动态内存的分配和释放
线性结构【把所有的节点用一根直线穿起来】:
连续存储[数组]
数组名:一维数组名是个指针变量,它存放的是一维数组第一个元素的地址,它的值不能被改变
一维数组名指向的是数组的第一个元素
1.什么叫数组
元素类型相同,大小相等
2.数组的优缺点:
优点:存取速度很快
缺点:插入删除元素很慢,空间通常是有限制的,事先必须知道数组长度,需要大块连续的内存块,插入删除元素很慢
离散存储[链表]
定义:
n个节点离散分配,彼此通过指针相连,
每个节点只有一个前驱节点,每个节点只有一个后续节点
首节点没有前驱节点,尾节点没有后续节点
首节点:
第一个有效节点
尾节点:
最后一个有效节点
头节点:
头节点的数据类型和首节点类型一样
第一个有效节点之前的节点,头节点并不存放有效数据,
加头节点的目的主要是为了方便对链表的操作
头指针:
指向头节点的指针变量
尾指针:
指向尾节点的指针变量,指针域为空NULL
确定一个链表需要几个参数:
只需要一个参数:头指针
因为我们通过头指针可以推算出链表的其他所有信息
分类:
单链表
双链表
每一个节点有两个指针域
循环链表
能通过任何一个节点找到其他所有的节点
非循环链表
算法:
遍历
查找
清空
销毁
求长度
排序
删除节点
插入节点
算法:
狭义的算法是与数据的存储方式密切相关
广义的算法是与数据的存储方式无关
泛型:
利用某种技术达到的效果就是:不同的存储方式,执行的操作是一样的
链表的优缺点:
优点:空间没有限制,插入删除元素很快
缺点:存取速度很慢
线性结构的两种常见应用之一 栈(堆栈)
定义
一种可以实现先进后出的存储结构
分类
静态栈
动态栈
算法
出栈
入栈/压栈
应用
函数调用
中断
表达式求值
内存分配
缓冲处理
迷宫
备注:堆只是分配内存的形式,不属于存储数据结构
栈内存:压栈出栈分配的内存,
堆内存:排序的方式分配的内存
动态分配的是堆里面分配,静态分配时在栈分配
线性结构的两种常见应用之二 队列
定义:
一种可以实现“先进先出”的存储结构
分类:
链式队列 -- 用链表实现
静态队列 -- 用数组实现
静态队列通常都必须是循环队列
循环队列的讲解:
1.静态队列为什么必须是循环队列
2.循环队列需要几个参数来确认及其含义的讲解
需要两个参数来确定
front
rear
3.循环队列各个参数的含义
2个参数不同场合有不同的含义
1)队列初始化
front和rear的值都是0
2)队列非空
front代表队列的第一个元素
rear代表队列的最后一个有效元素的下一个元素
3)队列空
front和rear的值相等,但不一定都是0
4.循环队列入队伪算法讲解
两步完成
1)将值存入rear所代表的位置
2)正确的写法是:rear = (rear+1)%数组的长度
5.循环队列出队伪算法讲解
front = (front+1)%数组的长度
6.如何判断循环队列是否为空
如果front与rear的值相等,则该队列就一定为空
7.如何判断循环队列是否已满
两种方式:
1)多增加一个标识参数
2)少用一个元素(通常使用这种方式)
如果rear和front的值紧挨着,则队列已满
if((rear+1)%数组长度 == front)
已满
else
不满
队列算法
入队
出队
队列的具体应用
所有和时间有关的操作都与队列影子
专题:递归
定义:
一个函数自己直接或者间接调用自己
递归满足三个条件
1.递归必须要有一个明确的终止条件
2.该函数所处理的数据规模必须在递减
3.这个转化必须是可解的
递归和循环
递归:
易于理解,速度慢,存储空间大
循环:
不易理解,速度快,存储空间小
函数调用:
当一个函数的运行期间调用另外一个函数时,在运行被调函数之前,系统要完成三件事:
1.将所有的实际参数,返回地址等信息传递给被调函数保存
2.为被调函数的局部变量(也包括形参)分配存储空间
3.将控制转移到被调函数的入口
从被调函数返回主调函数之前,系统也要完成三件事:
1.保存被调函数的返回结果
2.释放被调函数所占的存储空间
3.依照被调函数保存的返回地址将控制转移到调用函数
当有多个函数相互调用时,按照后调用先返回的原则,上述函数之间信息传递和控制转移必须
借助栈来实现,即系统将真个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,
就在栈顶分配一个存储区,进行压栈操作,每当一个函数退出时,就释放它的存储区,就行出栈操
作,当前运行的函数永远都在栈顶位置
举例:
1. 求阶乘
2. 1+2+3+4+-----100的和
3. 汉诺塔
4. 走迷宫
递归的应用:
树和森林就是以递归的方式定义的
数和图的很多算法都是以递归来实现的
很多的数学公式就是以递归的方式定义的
斐波拉契序列:
1 2 3 5 8 13 21 .....
模块二:非线性结构
树
树定义
专业定义:
1.有且只有一个称为根的节点
2.有若干个互不相交的子树,这些子树本身也是一棵树
通俗的定义:
1.树是由节点和边组成
2.每个节点只有一个父节点,但可以有多个子节点
3.但有一个节点例外,该节点没有父节点,此节点称为根节点
专业术语
1.节点
2.父节点
3.子节点
4.子孙
5.堂兄弟
6.深度:
从根节点到最底层节点的层数称之为深度
根节点是第一层
7.叶子节点:
没有子节点的节点
8.非终端节点
实际就是非叶子节点
9.度
子节点的个数称之为度
树分类
一般树
任意一个节点的子节点个数都不受限制
二叉树
任意一个节点的子节点个数最多只有两个,且子节点的位置不可更改
分类:
一般二叉树
满二叉树
在不增加树层数的前提下,无法再多添加一个节点的二叉树就是满二叉树
完全二叉树
如果只是删除了满二叉树最底层最右边的连续若干个节点这样的二叉树就是完全
二叉树
森林
n个互不相交的树的集合
树的存储
二叉树的存储
连续存储【完全二叉树】
优点:
查找某个节点的父节点和子节点(也包括判断有没有子节点)速度很快
缺点:
耗用内存空间过大
链式存储
一般树的存储
双亲表示法
求父节点方便
孩子表示法
求子节点方便
双亲孩子表示法
求父节点和子节点都很方便
二叉树表示法
把一个普通树转化为二叉树来存储
具体转化方法:
设法保证任意一个节点
左指针域指向它的第一个孩子
右指针域指向它的下一个兄弟
只要能满足此条件,就可以把一个普通树转化为二叉树
一个普通树转化成的二叉树一定没有右子树
森林的存储
先把森林转化为二叉树,再存储二叉树
树操作
遍历
先序遍历【先访问根节点】
先访问根节点
再先序访问左子树
再先序访问右子树
中序遍历【中间访问根节点】
中序遍历左子树
再访问根节点
再中序遍历右子树
后序遍历【最后访问根节点】
先中序遍历左子树
再中序遍历右子树
再访问根节点
已知两种遍历序列求原始二叉树
通过先序和中序或者后序和后序我们可以还原出原始二叉树
但是通过先序和后序是无法还原出原始的二叉树的
换种说法:
只有通过先序和中序或者通过中序和后序我们才可以唯一的确定一个二叉树
已知先序和中序求后序
先序:ABCDEFGH
中序:BDCEAFHG
后序:DECBHGFA
先序:ABDGHCEFI
中序:GDHBAECIF
后序:GHFBEIFCA
已知中序和后序求先序
中序:BDCEAFHG
后序:DECBHGFA
先序:ABCDEFGH
树应用
树是数据库中数据组织的一种重要形式
操作系统子父进程的关系本身就是一棵树
面向对象语言中类的继承关系本身就是一棵树
赫夫曼树
图
模块三:查找和排序
折半查找
排序:
冒泡
插入
选择
快速排序
归并排序
排序和查找的关系
排序是查找的前提
排序时重点
再次讨论什么是数据结构
数据结构时研究是研究数据存储和数据操作的一门学问
数据存储分为两个部分:
个体的存储
个体关系的存储
从某个角度而言,数据的存储最核心的就是个体关系的存储,个体的存储可以忽略不计
再次讨论到底什么是泛型
同一种逻辑结构,无论该逻辑结构物理存储是什么样子的,我们都可以对它执行相同的操作