数据结构学习笔记-排序/队/栈/链/堆/查找树/红黑树

排序

插入排序:每次从剩余数据中选取一个最小的,插入已经排序完成的序列中

合并排序:将数据分成左右两组分别排序,然后合并,对每组数据的排序递归处理。

冒泡排序:重复交换两个相邻元素,从a[1]开始向a[0]方向冒泡,然后a[2]...当a[i]无法继续往前挤的时候说明前面的更小了,而且越往前越小(挤得越往前)

堆排序:构造最大堆,每次取走根结点(自然是最大的),再调用MAX-HEAPIFY算法(见后文的堆)恢复最大堆的性质,重复取走根结点

快速排序(对A[r]-A[n]进行排序):

1.序列中选取一个元素作为主元并与A[n]交换,或者直接把A[n]作为主元

2.序列划分为左中右三部分,主元在中间。left中任何元素都<=主元,right中任何元素都>主元。

具体划分方式为:将序列的A[r]-A[n-1]部分划分为三个依次相连的区域:left,right,未划分,在未划分的区域遍历,如果某元素比主元大不作处理,如果比主元小,则将此元素与right区的第一个元素交换,并将新的right区的第一个元素划给left区。遍历完毕够将A[n]与right区的第一个元素交换。

([left][right][未划分][主元],也就是把未划分区域的元素不断移动到left区或保持不动变成right区的一部分)

3.划分好的左右两部分分别排序,这样递归下去,直到某一部分的元素数量少于等于三个可以直接进行排序。

队栈链堆:

:先进后出的结构,push时栈顶升高,元素放入新栈顶;pop时取出栈顶元素,栈顶降低。

(循环)队列:先进先出的结构,top指向队首的位置,tail指向新元素要加入的位置(也就是队尾的后一个位置)。

enqueue时元素放入tail,tail循环后移;dequeue时取出top元素,top循环后移。

队列的容量比数组容量少一。(否则当top和tail相等时无法判断队列是空还是满)

链表:前一个元素指向后一个元素(后一个也可能指向前一个),经典操作插入,删除,搜索,可以在头元素之前尾元素之后加入哨兵元素以简化边界判断。

最大堆(最小堆同理):一颗完全二叉树,而且每个结点都是所在子树中的最大结点

最大堆MAX-HEAPIFY算法:当最大堆的根结点的值发生了变化,可能不再是整个树中最大的结点了,使用此算法可以维持最大堆的性质(每次和左右结点中最大的那个交换,递归下去,最终移动到合适的位置)

最大堆建堆(自下而上MAX-HEAPIFY):从第max/2个结点(最后一个元素是此元素的孩子结点,此元素再往后的点都没孩子,自然都是最大堆),从此结点依次往前调用MAX-HEAPIFY算法,可以保证从下到上的每个子树都是最大堆,那么整体也是最大堆。

用最大堆实现最大优先级队列:

1.取最大权重元素:A.First

2.移除最大权重元素:取出A.First,然后把A.Last移动到A.First并对新的A.First执行向下递归的MAX-HEAPIFY操作以维持最大堆性质。

3.插入元素:在最后面新增元素(成为新的A.Last),对A.Last向着父结点方向递归交换上升到适合的位置。

4.增加A[i]的权重:A[I]向着父结点方向递归交换上升到适合的位置。

二叉查找树:

每个结点的KEY,都比其左子树的任意结点的KEY大,比其右子树的任意结点的KEY小。因此通过中序遍历可以从小到大顺序输出(中序遍历:先遍历左子树,再遍历父结点,最后遍历右子树)

查找:从父到子从上到下,想找更小的数往左拐(如果要找的数小于根结点,则一定在左子树中)。直到匹配,或者找到了NIL。

最大KEY值元素:顺着右子树的右子树的右子树……找到底,因为右子树总是比较大。

最小KEY值元素:同理,顺着左子树找到底。

后继(中序遍历中下一个遍历到的结点):

1.如果结点有右子树,则后继是右子树RT的左下角(左中右,下一个遍历到的一定是RT的左子树的左子树的左子树的左子树。。。)

2.如果结点(设为c)不含有右子树,则一路向上找祖先,直到找到某个结点s,使得c在这个结点的左子树中(也就是说s的左孩子也是c的祖先,或者就是c),此时c的左子树刚遍历完毕即将遍历s,自然s就是c的后继了

前驱(和后继问题对称)

1.如果含有左子树,则一定是左子树的最大结点

2.如果没有左子树,则F所在的某颗右子树马上要被遍历了,找前驱顺着父节点往上找就是(直到找到某个结点S,使得S的右孩子也是F的祖先)

插入

像查找一样从上到下根据和父结点大小的对比,选择左转还是右转,直到落到合适的空位上。

删除:

1.如果F没有孩子结点,直接删,什么都不影响

2.如果F有一个孩子结点,删除该结点,并且将它的孩子节点(C)连接到父结点(S)上

(这样并不会改变S的左子树所有结点比S小,右子树所有结点比S大的局面)

3.如果F有两个孩子结点,则把F的后继删除,然后用后继替换F(后继是右子树左下角,最多只有一个右孩子,用1或2的方式删了无影响;由于F和其后继大小相邻,替换后中序遍历依然从小到大,说明没有破坏查找树的性质)

红黑树:

根节点和最底下无数据的叶子结点(Nil结点)为黑色,任意父子结点不能同时为红(这样可以保证每个路径红的数量不大于黑),任意路径的黑结点数量相同(配合前面的性质,保证了最长的路径最多不超过最短路径的二倍,一种平衡策略)。插入删除操作都是 0(lnN), 且最多旋转三次

旋转:

分左旋和右旋。旋转改变某个结点和它的左孩子或右孩子的关系。因为改变上下的同时也改变了左右,因此不影响左<中<右的查找树性质。一定的旋转和变色操作可以恢复红黑树的性质。

左旋:结点F绕着右孩子R逆时针旋转90度,结果F变成了R的左孩子,R原来的左孩子分给了F的右边,F原来的父结点被R连上了。左旋导致了右孩子的右侧分支变短了。

右旋:结点F绕着左孩子L顺时针旋转90度,指针变更和左旋同理,导致左孩子的左分支变短了。

红黑树的插入:(插入的位置如同普通查找树,每次只插入红的,这样只破坏红色不相邻的性质):

如果红黑树是空的:放入根结点的位置,变成黑色

如果父结点是黑的。不破坏性质。

之后以新结点的父结点是爷爷结点的左孩子为例;右孩子的情形与此对称:

1.父结点和叔父结点都是红的:

让父结点和叔父结点变黑,祖父变红,这样解决了父子同红的问题。如果恰好曾祖父为红,则递归向上解决问题。

2.父红,叔父黑,新结点是父结点的右孩子。则左旋父结点,这样父子左右关系颠倒,变成情况3

3.父红,叔父黑,新节点是父结点的左孩子。则右旋祖父结点(按照预设,父结点是祖父结点的左孩子)。此时父子双红的左分支高度降低,原来的父节点位置变低,跑到右侧。此时再交换原祖父结点和父结点的颜色,就左右均衡了。

删除

如果红黑树只有一个结点,直接删除;

如果删除的结点有两个孩子,则实际删除的是其后继,变成没有或者一个孩子的情况。

如果有一个孩子,则必定是黑结点红孩子,直接用孩子结点替代父结点并且把颜色变黑即可。

如果结点没有孩子且结点为红色,直接删除即可。

因此唯一需要讨论的就是要删除的结点为黑色而且没有孩子结点。

包含多种情况,使用变色旋转等技巧可以使树平衡。看了好久好久也没整理下来,不继续浪费时间了

 

时间: 2024-10-27 08:01:06

数据结构学习笔记-排序/队/栈/链/堆/查找树/红黑树的相关文章

数据结构学习笔记——排序

1. 分类 2. 7种内排序算法的各种指标 排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性 移动次数的平均情况 移动次数的最好情况 移动次数的最坏情况 冒泡排序 O(n2) O(n) O(n2) O(1) 稳定 O(n2) 0 O(n2) 简单选择排序 O(n2) O(n2) O(n2) O(1) 稳定 O(n) 0 O(n) 直接插入排序 O(n2) O(n) O(n2) O(1) 稳定 O(n2) O(n) O(n2) 希尔排序 O(nlogn)~O(n2) O(n1.3) O(n

数据结构学习笔记之栈

栈(stack)  是限定仅在表尾进行插入或删除操作的线性表.因此,对栈来说,表尾端有其特殊含义,称为栈项(top),相应地,表头端称为栈底(bottom).不含元素的空表称为空栈. 栈有两种存储表示方法:顺序栈和链栈.顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置.通常的习惯做法是以top=0表示空栈,鉴于C语言中数组的下标约定从0开始,则当以C作描述语言时,如此设定会带来很大不便:另一方面,由于栈在使用过程

《大话数据结构》学习笔记 排序

排序的严格定义:  假设含有n个记录的序列为{r1,r2,......,rn},对应的关键字分别为{k1,k2......,kn},需确定1,2,......,n的一种排列p1,p2,......,pn,使其相应的关键字 满足Kp1<=Kp2<=......Kpn关系,即使得序列成为一个按关键字有序的序列(rpq,rp2,......rpn),此操作称为排序.  排序的稳定性:内排序与外排序(根据记录是否全部放置在内存中). 根据排序中的主要操作,可以分为插入排序类(直接插入排序->希尔

小猪的数据结构学习笔记(二)

小猪的数据结构学习笔记(二) 线性表中的顺序表 本节引言: 在上个章节中,我们对数据结构与算法的相关概念进行了了解,知道数据结构的 逻辑结构与物理结构的区别,算法的特性以及设计要求;还学了如何去衡量一个算法 的好坏,以及时间复杂度的计算!在本节中我们将接触第一个数据结构--线性表; 而线性表有两种表现形式,分别是顺序表和链表;学好这一章很重要,是学习后面的基石; 这一节我们会重点学习下顺序表,在这里给大家一个忠告,学编程切忌眼高手低,看懂不代表自己 写得出来,给出的实现代码,自己要理解思路,自己

小猪的数据结构学习笔记(五)

小猪的数据结构学习笔记(五) 线性表之--循环链表                           --转载请注明出处:coder-pig 循环链表知识点归纳: 相关代码实现: ①判断是否为空表: ②单循环链表的存储结构 其实和单链表的结构是一样的! /*定义循环链表的存储结构*/ typedef struct Cir_List { int data; struct Cir_List *next; }Lnode; ③初始化循环单链表 代码如下: //1.循环链表的初始化 //表示一个元素,如

数据结构学习笔记(1)-数据结构与算法

基本概念和术语 1.数据  数据元素  数据对象   数据结构 数据:在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称. 数据元素:是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理. 数据对象:是性质相同的数据元素的集合.是数据的一个子集. 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合. 2.数据结构 数据结构分为逻辑结构和物理结构 2.1逻辑结构 逻辑结构表示数据之间的相互关系.通常有四种基本结构: 集合:结构中的数据元素除了同属于一种类型外,别

小猪的数据结构学习笔记(三)

小猪的数据结构学习笔记(三) 线性表之单链表 本章引言: 上一节中我们见识了第一个数据结构--线性表中的顺序表; 当你把操作的代码自己写几遍就会有点感觉了,如果现在让你写顺序表的 插入算法,你能够想出大概的代码么?如果可以,那么你就可以进入新的章节了; 否则,还是回头看看吧!在本节,我们将迎来线性表的链式表示--单链表 单链表和顺序表有什么优势和劣势呢?单链表的头插法和尾插法有什么不同呢? 请大家跟随笔者的脚步来解析线性表中的单链表把! 本节学习路线图 路线图解析: ①先要理解顺序表和单链表各自

小猪的数据结构学习笔记(四)

小猪的数据结构学习笔记(四) 线性表之静态链表 --转载请注明出处:coder-pig 本章引言: 在二,三中中我们分别学习了顺序表中的线性表与单链表,线性表有点类似于 我们前面所学的数组,而单链表使用的最多的是指针,这里问个简单的问题, 如果是在以前没有指针的话,前辈先人们怎么实现单链表呢?大家思考下! 没有指针,那么用什么来代替呢?前辈先人们非常机智,想出了使用下标+游标的方式 来实现单链表的效果!也就是今天要讲的--静态链表! 当然你也可以直接跳过本章,因为有了单链表就没有必要用静态链表了

【数据结构学习笔记(C#描述)】(二)算法分析

由上一章的内容可知软件质量的重要特征之一就是能够高效的利用资源(运行效率),因此我们就要考虑如何创建出能够高效利用CPU及内存的数据结构与算法.而算法分析的目的就是为了让我们能够认识到算法对于资源的利用效率. 我们要想分析算法的效率,就需要找到一个评价算法效率的标准及方法. 一般我们如果能快速的利用CPU就会更好的节省时间,因此在时间层面上我们的评价标准就是时间复杂度,而如果我们能够较好的利用内存的话我们将会节省更多的内存空间,因此在空间层面上我们的评价标准就是空间复杂度. 所谓时间复杂度和空间