《数据结构》笔记

前缀表达式:也成为波兰表达式 *23+1-4

中缀表达式:最常用的那种    1+2*3-4

后缀表达式:为了区分前缀,也称为逆波兰表达式 123*+4-


使用三个结点三个结点查看的方式更明显

 

 :

图中的专业术语:

图定义:

图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E)

其中,G表示是一个图,V是图G中顶点的集合,E是图G中的边的集合

顶点:

线性表中将数据元素叫做元素,树中将数据元素叫做结点,在图中数据元素称之为顶点(Vertex)

线性表树和图之间的不同:

1、线性表中可以没有数据元素称为空表,树中可以没有结点称做空树,但是在图中不允许没有顶点

2、线性表中相邻的元素之间具有线性关系,树结构中相邻两层的节点具有层次关系,但是在图中

   任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的

 

各种图定义:

无向边:

若顶点vi到vj之间的边没有方向,则这条边称为无向边(Edge),用无序偶对(vi,vj)来表示。

如果图中任意两条边都是无向的,那么该图称做无向图

 

有向边:

若从顶点vi到vj的边有方向,则称这条边为有向边,也称为弧(Arc)

 

 

数据结构:

树状结构的几种表示方法:

第一种:画倒立树表示

第二种:嵌套集合表示法(圆圈包含)

第三种:凹入表表示法(使用长条长短来进行表示)

第四种:广义表表示法(使用括号包含来表示)

树形结构术语:

在一课非空树中,有且只有一个根结点

节点的度:有几个孩子就成为该结点的度为多少

树的度:  是节点的度最大的那一个就是树的度

叶子节点(终端节点):度为零的节点成为叶子节点

非终端节点:度不为零的节点

除根节点之外的节点统称为内部节点

双亲节点:

节点层数:

树的高度:最底下一层节点的层数

有序树:树中的每一个节点是有次序的,从左至右,不能交换

无序树:树中每一个节点是没有次序的,是可以交换的

森林:

对于模板类中还有一个类的话,如果函数返回值是类中类的类型,那么尽量写在类内部来实现该函数,写在外面会有点复杂,很容易就出错了(虽然编译能够通过,但是一使用就会报错的)

链表使用结点

树使用节点(一个节点就是一棵树)

设计一个n叉树的结构:

 

因为不知道一个节点有几个孩子,所以可以使用一个指针数组,将每一个指针都指向一个节点的孩子,还有一个父节点指针,以及一个数据域,但是这样设计的结构如果需要不断插入到一个节点的孩子或者兄弟节点的话,就需要重复申请指针数组,将原来的指针执行拷贝到新的指针数组中,这样会浪费开销

现在设计的一个结构是由一个数据域,一个指向父节点的指针域,还有一个指向兄弟节点的指针这样不管你有多少个孩子直接在最后一个兄弟之后进行插入就可以了

对于这样的结构插入情况:

在哪个节点之后进行插入,插入到是什么地方

如果没有找到要插入的节点,那么需要设计一个规则,将要插入的数据作为最后一层的第一个节点的孩子,如果找到了,如果找到了那么看是插入到哪个地方,如果是作为找到的节点的孩子的话,那么放在孩子节点的最后一个兄弟的后面,如果是兄弟,那么放在最后一个兄弟的兄弟,这样插入搞定

有序树:

所谓有序树就是按照指定的某个规则进行构建的一个树,然后按照先序中序后序其中的一个方式进行遍历就是有序的,这样的树就是一个有序树,如果不论用哪种方式进行遍历都是没有顺序的,那么这样的树就是一个无序树

另外需要注意的是:有序树不一定是一个完全二叉树,如果是将根作为最大或者最小的数据,那么这样构建出来的树就是一个堆了,所谓堆就是一个完全二叉树,有自己的插入删除规则,执行完任何操作之后要仍然保证是一个大顶堆或小顶堆

有序二叉树的使用递归方式插入(定义规则:左子树数据 < 根数据 <= 右字树数据)

void _insert(TreeNode **root,T const &date)  //往有序树中使用递归插入节点
{					//必须使用二级指针才行,因为需要在函数中改变root的数据
	if(*root)
	{
		if((*root)->date>date)
		{
			_insert((*root)->left,date);
		}
		else
		{
			_insert((*root)->right,date);
		}
	}
	else
	{
		*root=_createNode(date);
	}
}

有序二叉树的删除:

 

根据节点中的数据,删除有序二叉树中的节点

1、能找到

    1.1是根节点

        1.1.1有右字树

            将右字树的最小数据的节点的左孩子指针指向根结点的左孩子

        1.1.2没有右字树

    将pRoot指针直接指向pRoot的左孩子指针

    1.2是子树

满二叉树:

满二叉树中第i层上的节点个数为2i-1次方个

深度为n的二叉树中所有的节点个数之和为2i次方-1

对于任意的一个二叉树中满足叶子节点个数=度为2的节点个数+1

满二叉树:

完全二叉树:如果一棵二叉树最多只有最下面的两层其节点的度数可以小于2,并且最下一层的节点全部集中在左边

满二叉树是一棵特殊的完全二叉树,反之不是

使用顺序结构来实现完全二叉树是比较方便的,而且也节约空间:

 

哈夫曼树:

最优二叉树,所有节点的带权路径之和最小

用法:使用频率越高的数据越放在前面,频率越低的数据放下面

更多的作用用在搜索数据

构建哈夫曼树的规则是首先找到所有元素中的权值最小的两个节点,然后将这两个节点构建成一个树,然后在剩下的元素中取权值最小的元素和构建成的树的父节点组成一个新的树,重复这个操作将所有的元素都插入到整个二叉树当中,这样构建出来的就是一个哈夫曼树也就是最优二叉树了

 

:完全二叉树的一种表现形式

最大()完全二叉树也叫大顶堆小顶堆

在这个树中每一个父节点的值是大()于或者等于孩子节点的值就成为大顶堆

 

1、堆的插入

    首先将要插入的数据放在数组的最后,然后和当前节点的父节点进行比较,看是否满足 原来的堆规则,如果不满足,那么和父节点进行交换位置,一直交换到满足了原来的堆 的结构为止

2、堆的删除

    直接将最后一个元素覆盖将被删除的元素位置,然后往下找此时被删除位置元素的正确位置,这样就完成了堆元素的删除

总之目的只有一个在进行元素的插入和删除操作之后保证改变后的堆仍然是一个和原来堆一样的结构就可以了

3、堆的初始化

深度寻路(DFS):

深度寻路所谓深度就是指的按照一条路径走到底,如果可以到达目的地,那么这就算找到了一条路径,不会管是不是还有其他更短的路径可走的

深度寻路在空旷的地图上进行寻路比较合适

那么广度寻路是找到的路径最短的一条路线,广度比深度复杂,循环成本高,遍历地图越大越慢,一般在小地图里面,找任意最短用广度

 

广度寻路(BFS:广度优先算法

广度寻路是一个树状结构

深度是一个上一节点找下一个节点

广度是上一个节点找下几个节点

深度游回退,广度没有回退

因为广度寻路需要进行构建一棵树结构,所以如果要进行寻路的地图很空旷,那么构建出来的树就会很大,所以广度寻路是不太适合地图较大,地图比较空旷的情况的。这样情况用深度寻路会比较好

  

补充:

二叉排序树(Binary Sort Tree),又称为二叉查找树。它或者是一棵空树,或者是具有下列性质的二叉树:

1、若它的左子树不空,则左子树上所有节点的值均小于它的根结点的值

2、若它的右字树不空,则右字树上所有节点的值均大于它的根结点的值

3、它的左右子树也分别为二叉排序树

实际上二叉排序树就是有序二叉树

堆结构:

堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆

堆排序:(Heap Sort

堆排序就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将他移走(其实就是将其和堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值,如此反复执行,便能得到一个有序序列了

A*寻路:

优点:寻路能够找到最短路径,开销比广度寻路要小,寻路中会将一些路径丢弃掉

缺点:代码的会比较复杂点,需要考虑的东西有点多

深度在空旷的环境中,并且最好也知道目的地相对起点的大概位置是比较适合的

广度适合在小范围寻路是比较适合的,可以找到所有路径

A*在大范围的地图中一定要使用A*

A*只能找到一条最短路径

A*寻路使用最多的一种寻路算法

A*寻路是启发式搜索,在地图空间中对每一个待搜索位置进行评估,得到最优位置,从最优位置进行寻路

启发式的搜索评估的公式:

f=g+h;

F:节点评估值f值越小,路劲越优,优先搜索

G:从初始点到当前点需要多少代价(花费多长的时间或者多上的精力)

H:从当前点到终点的预估代价,预估代价走直线,且忽视障碍

A星寻路和深度广度寻路最大的区别:A星寻路可以走斜路,广度深度只能走直线

A*寻路过程实例分析:

 

例如上图:如果小猫想要从当前位置去迟到墙壁右边的骨头的按照A*寻路过程分析:

首先小猫一开始的位置对四周四个位置(如果有障碍物则不考虑)进行判断f的值,可以得到小猫右边位置的f值是最小的,那么就将小猫一开始的位置放入close列表中,将右边的位置放入open列表中,然后将右边的位置作为开始位置,对该位置进行查看四周f值的判断操作,然后可以得到下面的位置f值是最小的,重复操作,将这次的当前位置从open列表中移入close列表中,将这次找到的f值最小的位置放到open列表中,然后对这次的open中的位置进行四周查看f值操作,得到下面的位置f值是最小的

红黑树:

红黑树是一种特殊的二叉查找树,它能保证在最坏情况下基本操作的时间复杂度为O(log2N)。这种数据结构避免了普通二叉树基本操作与树高度成正比的劣势,是一种常用的重要数据结构。

 

一棵满足红黑性质的二叉查找树就是红黑树(前提条件是二叉查找树,即满足左<<右)

红黑树必须满足的性质:

1、每个节点或者是红的,或者是黑的

2、根节点一定是黑

3、叶节点一定是黑的

4、如果一个节点是红的,那么它的两个孩子节点都是黑的(反之说也就是黑的父必是红)

5、对每个节点,对该节点到其子孙节点的所有路径上包含相同数目的黑节点

平衡二叉树

平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右字树的高度差至多等于1

平衡二叉树是一种高度平衡的二叉排序树

我们将二叉树上节点的左子树深度减去右字树深度的值称为平衡因子BFBalance Factor,那么平衡二叉树上所有节点的平衡因子只可能是-1,0,1,只要二叉树上有一个节点的平衡因子的绝对值大于1,那么该二叉树就是不平衡的

最小不平衡树:

距离插入节点最近的,且平衡因子的绝对值大于1的节点的为根的子树,我们称为最小不平衡子树


平衡二叉树实现原理:

平衡二叉树构建的基本思想就是在构建二叉排序树的过程中,每当插入一个节点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡树。在保持二叉排序树特性的前提下,调整最小不平衡子树中个结点之间的链接关系,进行相应的旋转,使之成为新的平衡二叉树

哈希表

 

Hash:哈希,也叫做散列表,是一种无序存储,所谓无序就是自己构建地址,不一定是内存地址

自己构建地址:地址和值的关系,这种关系称之为哈希函数

学习哈希表,最重要的就是哈希函数的构造,所谓哈希函数就是地址和值的映射

制作哈希函数几种常用的方法:

第一种:

余数法 H(key)=key%p; //p是个常量,自己设置

例如将p设置为10的话:

那么有:

H(key)=key%10;

3 12 46 37 48

Arr[3%10]=3;

Arr[12%10]=12;

Arr[46%10]=46;

Arr[37%10]=37;

Arr[48%10]=48;

会发现数组下标有重复的,这种情况就称为哈希冲突

第二种:平方取中法:

  1234^2=1522756  h(key)=227;

第三种:直接定址法

H(key)=key;

3 12 46 37 48

Arr[3]=3;

Arr[12]=12;

Arr[46]=46;

Arr[37]=37;

Arr[48]=48;

第四种:折叠法(比较适合大数)

哈希表:

哈希法又名散列法,是一种特殊的查找方法,查找很循序

理想查找方法是不经过比较

如果有一种方法不经过比较,就能找到,这样就是最理想的查找

哈希法就是这种查找方式,不经过比较

哈希表中冲突现象

设计一个哈希表需要做的准备工作:

1、确定表的范围

2、构造合适的哈希函数

哈希表的构建就是通过数组和链表进行组合的方式构建的一种结构

数组是一个指针数组,每一个下标数据都代表一种数据类型,然后用该下标的指针最为该类型的链表头,这一类的数据都放在这个链表中

哈希表的其实就是通过将数据进行分类,然后将每一类数据都用链表来存放,这样查找的时候根据关键字,也就是下标了,然后直接在该关键字类型代表的链表中进行查找数据,这样对于大量的数据进行查找就非常方便快速了

解决哈希冲突的两种方法:

第一种:开放地址法

线型探测的方法

第二种:数组链表法

做的事情:

1、实现哈希

2、解决哈希表的冲突

3、哈希表的查找

4、哈希表的打印

有什么用处:对数据进行加密操作

归并排序:

两个有序数组合并成一个有序数组就叫归并排序

 

猜你喜欢

转载自blog.csdn.net/qq_36769722/article/details/80713215