【数据结构】树的基本内容总结

课程正式开始了。因为有些课感觉好没意思。恰好,背着数据结构(c语言版)去上算法课,于是从那次开始看。慢慢的看的还挺有意思,于是把树这章基本看完了,做个小结。、
因为普通的树应用价值不大而且不用以表示,所以现在只讨论二叉树。
1.内存中的表示方法:
(1)数组。主要用来表示完全二叉树,这样对于寻找父节点和子节点很容易。某个节点i,它的父节点是i/2 取下整。左子节点是2i,右节点的2i+1.如果存在的话。
(2)链表。这个更常用一些吧。一个节点有三个域,data,leftChild,rightChild.当然了,为了便于寻找父节点方便,可以增加一个域,用于指向父节点的位置所在。

2二叉树的抽象数据结构:
*********************************************************************************
structure BinaryTree;
objects:节点的有限集合。
functions:
(1)BinTree Create() //创建一个树
(2)Boolean IsEmpty(bt) //是否是空
(3)BinTree MakeBT(btleft,item,btright)//生成一个树,左子树为btleft,右子树为btright,item作为跟节点
(4)BinTree Lchild(bt) //返回左子树或者NULL
(5)BinTree Rchild(bt)//返回右子树或者NULL
(6)element Data(bt)//返回根节点的数据
*********************************************************************************

3二叉树的若干性质。这个比较偏理论,基本不怎么用吧,先不写了
4二叉树的遍历
以前总是觉得这个有点难,发现也没有什么。顶多是非递归的要复杂一点,递归的很容易啊。
(1)中序遍历 前序遍历和后序遍历的递归版本。
********************************
中序遍历
void inorder(tree ptr)
{
if(ptr)
{
inorder(ptr->left)
visit(ptr->data)
inorder(ptr->right)
}
}
********************************

(2)使用栈的迭代的中序遍历参考P129的程序。很简单。
(3)层次遍历。也就是说广度优先搜索,使用队列实现。至于是循环队列还是其他队列,可以自己考虑。效果是:使树中的节点按照层次依次输出。
(4)其他操作:二叉树的复制,二叉树的判断相等。
复制是一个递归的实现过程。分配内存,然后左子树递归复制,右子树递归进行复制。
判断是否相等主要是先检查空树条件。然后递归的判断左右子树是否分别相等。如果有任何一个地方不同,即可返回False。程序结束。

5线索二叉树
在二叉树中,叶子节点基本都是指向空的,而我们如果把这些点都弄起来,发现共有n+1个空闲的指针。那么,就可以用这些空闲的指针来进行一些使用,从而方便我们的一些操作。在中序遍历中,主要是用这些指针来指向某个节点的前驱节点和后继节点。这样的话,我们就不用递归的进行遍历了,而是可以跟据这个线索,一步一步的完成遍历。为了便于区分某个节点的左右指针是指向了自己的孩子节点还是指向了自己的前驱和后继节点,需要对数据结构中的表示方法进行一些更改,以便区分。新的结构如下所示:
****************************
struct threadedTree
{
short int leftThread;
threadTree * leftChild;
char data;
short int rightThread;
threadTree * rightChild;
}
****************************
(1)向线索二叉树中插入新的节点。分为两类:插入左儿子和插入右儿子
插入右儿子:有个节点p,其右子树为空,想要插入节点child,步骤如下:
1把parent->rightThread 置为false
2把child的leftThread 和rightthread置为true
3把child的leftthread指向p
4把child的rightChild指向p的rightChild
5修改parten的rightChild,使他指向child
插入右儿子,有个节点p,它的右子树不为空,想要插入child节点:
插入后,child变成原来p节点的右子树。原来p节点的右子树成为新的child节点的右子树。另外需要对线索进行更改。因为原来parent节点的的后继节点的中序前驱节点变成了child。

但是上面两种空与非空的情况都可以用同一个程序进行统一的处理。程序就不在这里列出了。后者主要是比前者多一个找后续节点直到为空的语句,别的基本按照上面的五步走。

6 堆
(1)定义:最大堆就是跟节点为整个树的最大值的完全二叉树,对,是完全二叉树。
(2)为什么用堆?
因为与数组和链表相比,堆进行插入和删除的操作可能更方便一些。插入和 删除,堆的复杂度都是lgn,而数组和链表(不管是有序的还是无序的)基本一个复杂度是O1,另外一个复杂对是On的。
(3)最大堆的插入
可以用一个数组来表示这个最大堆。先将新的节点插入到末尾,然后只管树中的这个分支,领用i/2找到父节点,进行比较,然后换位,最终可以讲该id按插入到合适的位置。

(4)最大堆的删除操作
删除最顶的最大元素。保存在一个新的节点中,方便函数返回
将末尾的最小元素放到顶层,并从尾部删除该元素。
然后重建堆,比较跟节点和其孩子节点的大小(+1即右节点了),然后进行位置的交换。直到顶端的这个元素找到自己合适的位置了,即可跳出循环了。(在此期间,可将末尾那个东西保存在一个节点中,模拟将其放入到头节点中,然后parent从1开始算,child从2开始算。不断的进行迭代,注意,parent每次都换成下一个child的值,child每次都换成新的parent的child的值(即乘以2就可),注意,最后的迭代中,child的计数不能超过堆中数据的总个数)。

7二叉查找树
(1)二叉查找树特点:
可以为空
没有重复的元素
左子树一定小于根节点
右子树一定大于跟节点
(2)二叉查找树的查找
两种方式:递归和非递归。递归就是三句话。==返回根。< 递归查询左子树,>递归查询右子树。
非递归查询:就是不同按着树杈走。走到头即可知道是否找到。
(3)二叉查找树的插入
在插入之前,首先要在树中查找这个节点,看是否已经存在,如果已经存在,返回错我。如果没有存在,会指向一个树中的某个叶子节点。然后将新的节点插入到这个被指向的叶子节点的尾部就好了。
(4)二叉查找树的删除
分为三种情况讨论:1删除叶子节点,容易。2删除仅有单个子节点的,容易,令其子节点取代被删除的及诶单即可。3删除有两个儿子的节点,稍微麻烦。用左子树中的最大节点或者右子树中的最小节点代替这个节点,然后将这个节点删除。然后,如果用左中的最大和右中的最小进行替代时,也需要删除这个节点,此时就需要再继续对该节点子节点进行调整。但是可以证明,一个树中的最大节点或者最小节点一定是度为0或者1的节点。

8选择树
对多路进行归并的排序时,可以使用这个。如果不用,多路归并的复杂度应该是n*n,而如果用了这个,复杂度应该是n*logn。能够有一定的提高。
总体的思想就是:先从各路归并K中选出分别一个元素,从而形成k个节点,然后两两进行锦标赛,得到k/2个优胜的节点,迭代进行锦标赛,最终得到了一个节点,就是将要在归并中选择的节点。然后,从该节点的出生地队列中再拿下一个树,重建选择树,重建过程和ilogn。这个是优胜树,也有失败树的,我不是太明白这个。

二叉树的基本内容就包括上面这些内容。深入的内容,应该是在以后的章节中继续深入探讨。这个部分就到此为止。

下面再简单说一下森林,这个也不太常用。森林主要是可以用来表示集合。其中,表示方式有所改变,之前是父指子,现在是子指父,对于查找操作比较方便。集合中主要涉及到并查操作。这个,等以后用到的时候再看吧。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


【啊哈!算法】算法3:最常用的排序——快速排序       上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那有没有既不浪费空间又可以快一点的排序算法
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正则表达式就可以组成第一个构造。正如稍后要介绍的一样,既然也可以命名组,大家就有考虑把这个构造作为匿名组。作为一个实例,请看看下列字符串: “08/14/57 46 02/25/59 45 06/05/85 18 03/12/88 16 09/09/90 13“ 这个字符串就是由生日和年龄组成的。如果需要匹配年两而不要生日,就可以把正则
选择排序:从数组的起始位置处开始,把第一个元素与数组中其他元素进行比较。然后,将最小的元素方式在第0个位置上,接着再从第1个位置开始再次进行排序操作。这种操作一直到除最后一个元素外的每一个元素都作为新循环的起始点操作过后才终止。 public void SelectionSort() { int min, temp;
public struct Pqitem { public int priority; public string name; } class CQueue { private ArrayList pqueue; public CQueue() { pqueue
在编写正则表达式的时候,经常会向要向正则表达式添加数量型数据,诸如”精确匹配两次”或者”匹配一次或多次”。利用数量词就可以把这些数据添加到正则表达式里面了。 数量词(+):这个数量词说明正则表达式应该匹配一个或多个紧紧接其前的字符。 string[] words = new string[] { "bad", "boy", "baad", "baaad" ,"bear", "b
来自:http://blog.csdn.net/morewindows/article/details/6678165/归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列
插入排序算法有两层循环。外层循环会啄个遍历数组元素,而内存循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内存循环选择的元素,那么瘦元素都想右移动以便为内存循环元素留出位置。 public void InsertionSort() { int inner, temp;
public int binSearch(int value) { int upperBround, lowerBound, mid; upperBround = arr.Length - 1; lowerBound = 0; while (lowerBound <= upper
虽然从表内第一个节点到最后一个节点的遍历操作是非常简单的,但是反向遍历链表却不是一件容易的事情。如果为Node类添加一个字段来存储指向前一个节点的连接,那么久会使得这个反向操作过程变得容易许多。当向链表插入节点的时候,为了吧数据复制给新的字段会需要执行更多的操作,但是当腰吧节点从表移除的时候就能看到他的改进效果了。 首先需要修改Node类来为累增加一个额外的链接。为了区别两个连接,这个把指
八、树(Tree)树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。不说那么多了,图一看就懂:当然了,引入了树之后,就不得不引入树的一些概念,这些概念我照样尽量用图,谁会记那么多文字?树这种结构还可以表示成下面这种方式,可见树用来描述包含关系是很不错的,但这种包含关系不得出现交叉重叠区域,否则就不能用树描述了,看图:面试的时候我们经常被考到的是一种叫“二叉树”的结构,二叉
Queue的实现: 就像Stack类的实现所做的一样,Queue类的实现用ArrayList简直是毋庸置疑的。对于这些数据结构类型而言,由于他们都是动态内置的结构,所以ArrayList是极好的实现选择。当需要往队列中插入数据项时,ArrayList会在表中把每一个保留的数据项向前移动一个元素。 class CQueue { private ArrayLis
来自:http://yingyingol.iteye.com/blog/13348911 快速排序介绍:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地
Stack的实现必须采用一种基本结构来保存数据。因为再新数据项进栈的时候不需要担心调整表的大小,所以选择用arrayList.using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Collecti
数组类测试环境与排序算法using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Data_structure_and_algorithm{ class CArray { pr
一、构造二叉树 二叉树查找树由节点组成,所以需要有个Node类,这个类类似于链表实现中用到的Node类。首先一起来看看Node类的代码。 public class Node { public int Data; public Node Left; public Node Right; public v
二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。         二叉树的使用范围最广,一棵多叉树也可以转化为二叉树,因此我们将着重讲解二叉树。二叉树中还有连两种特殊的二叉树叫做满二叉树和
上一节中我们学习了队列,它是一种先进先出的数据结构。还有一种是后进先出的数据结构它叫做栈。栈限定只能在一端进行插入和删除操作。比如说有一个小桶,小桶的直径只能放一个小球,我们现在向小桶内依次放入2号、1号、3号小球。假如你现在需要拿出2号小球,那就必须先将3号小球拿出,再拿出1号小球,最后才能将2号小球拿出来。在刚才取小球的过程中,我们最先放进去的小球最后才能拿出来,而最后放进去的小球却可以最先拿
msdn中的描述如下:(?= 子表达式)(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。(?(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?此构造不会回溯。msdn描述的比较清楚,如:w+(?=ing) 可以匹配以ing结尾的单词(匹配结果不包括ing),(
1.引入线索二叉树 二叉树的遍历实质上是对一个非线性结构实现线性化的过程,使每一个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中,只能找到一个节点的左、右孩子信息,而不能直接得到节点在任一遍历序列中的前驱和后继信息。这些信息只有在遍历的动态过程中才能得到,因此,引入线索二叉树来保存这些从动态过程中得到的信息。 2.建立线索二叉树 为了保
排序与我们日常生活中息息相关,比如,我们要从电话簿中找到某个联系人首先会按照姓氏排序、买火车票会按照出发时间或者时长排序、买东西会按照销量或者好评度排序、查找文件会按照修改时间排序等等。在计算机程序设计中,排序和查找也是最基本的算法,很多其他的算法都是以排序算法为基础,在一般的数据处理或分析中,通常第一步就是进行排序,比如说二分查找,首先要对数据进行排序。在Donald Knuth 的计算机程序设