??xml version="1.0" encoding="utf-8" standalone="yes"?>
一步一步写q二叉树(AVL树)
作者:C加 更新旉Q?/span>2012-8-20
q二叉树(Balanced Binary TreeQ是二叉查找树的一个进化体Q也是第一个引入^衡概늚二叉树?962q_G.M. Adelson-Velsky ?E.M. Landis发明了这|Q所以它又叫AVL树。^衡二叉树要求对于每一个节Ҏ(gu)_它的左右子树的高度之差不能超q?Q如果插入或者删除一个节点得高度之差大?Q就要进行节点之间的旋{Q将二叉树重新维持在一个^衡状态。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找Q删除的旉复杂度最好情况和最坏情况都l持在O(logN)。但是频J旋转会使插入和删除牺牲掉O(logN)左右的时_不过相对二叉查找树来_旉上稳定了很多?/p>
q二叉树实现的大部分过E和二叉查找树是一LQ学q二叉树之前一定要会二叉查找树Q,区别在于插入和删除之后要写一个旋转算法去l持qQ维持^衡需要借助一个节炚w度的属性。我参考了机械工业出版C《数据结构与法分析-C语言描述》写了一个C++版的代码。这本书的AVLTree讲的很好Q不q没有很完整的去描述。我会一步一步的讲解如何写^衡二叉树Q重Ҏ(gu)q二叉树的核心部分Q也是旋{法?/p>
W一步:节点信息
相对于二叉查找树的节Ҏ(gu)_我们需要用一个属性二叉树的高度,目的是维护插入和删除q程中的旋{法?/p>
代码如下Q?/p>
W二步:q二叉树类的声?/strong>
声明中的旋{函数在后边的步骤中详解?/p>
代码如下Q?/p>
W三步:两个辅助Ҏ(gu)
旋{法需要借助于两个功能的辅助Q一个是求树的高度,一个是求两个高度的最大倹{这里规定,一늩树的高度?1Q只有一个根节点的树的高度ؓ0Q以后每多一层高度加1。ؓ了解x针NULLq种情况Q写了一个求高度的函敎ͼq个函数q是很有必要的?/p>
代码如下Q?/p>
W四步:旋{
对于一个^衡的节点Q由于Q意节Ҏ(gu)多有两个儿子Q因此高度不qӞ此节点的两颗子树的高度差2.Ҏ(gu)看出Q这U不q出现在下面四U情况:
1?节点的左子树3节点高度比右子树7节点?Q左子树3节点的左子树1节点高度大于叛_?节点Q这U情冉|?span data-mce-style="color: #ff0000;" style="color: #ff0000; ">左左?/p>
2?节点的左子树2节点高度比右子树7节点?Q左子树2节点的左子树1节点高度于叛_?节点Q这U情冉|?span data-mce-style="color: #ff0000;" style="color: #ff0000; ">左右?/p>
3?节点的左子树1节点高度比右子树5节点?Q右子树5节点的左子树3节点高度大于叛_?节点Q这U情冉|?span data-mce-style="color: #ff0000;" style="color: #ff0000; ">叛_?/p>
4?节点的左子树1节点高度比右子树4节点?Q右子树4节点的左子树3节点高度于叛_?节点Q这U情冉|?span data-mce-style="color: #ff0000;" style="color: #ff0000; ">叛_?/p>
从图2中可以可以看出,1?两种情况是对U的Q这两种情况的旋转算法是一致的Q只需要经q一ơ旋转就可以辑ֈ目标Q我们称之ؓ单旋转??两种情况也是对称的,q两U情늚旋{法也是一致的Q需要进行两ơ旋转,我们UC为双旋{?/p>
W五步:单旋?/strong>
单旋转是针对于左左和叛_q两U情늚解决Ҏ(gu)Q这两种情况是对U的Q只要解决了左左q种情况Q右叛_很好办了。图3是左左情늚解决Ҏ(gu)Q节点k2不满_^衡特性,因ؓ它的左子树k1比右子树Z?层,而且k1子树中,更深的一层的是k1的左子树X子树Q所以属于左左情c?/p>
Z树恢复^衡,我们把k2变成q棵树的根节点,因ؓk2大于k1Q把k2|于k1的右子树上,而原本在k1叛_树的Y大于k1Q小于k2Q就把Y|于k2的左子树上,q样既满了二叉查找树的性质Q又满了^衡二叉树的性质?/p>
q样的操作只需要一部分指针改变Q结果我们得到另外一颗二叉查找树Q它是一AVL树,因ؓX向上一Ud了一层,Yq停留在原来的层面上QZ向下Ud了一层。整|的新高度和之前没有在左子树上插入的高度相同,插入操作使得X高度镉K了。因此,׃q颗子树高度没有变化Q所以通往根节点的路径׃需要l旋转了?/p>
代码如下Q?/p>
W六步:双旋?/strong>
对于左右和右左这两种情况Q单旋{不能使它辑ֈ一个^衡状态,要经q两ơ旋转。双旋{是针对于q两U情늚解决Ҏ(gu)Q同LQ这样两U情况也是对U的Q只要解决了左右q种情况Q右左就很好办了。图4是左x늚解决Ҏ(gu)Q节点k3不满_^衡特性,因ؓ它的左子树k1比右子树Z?层,而且k1子树中,更深的一层的是k1的右子树k2子树Q所以属于左xc?/p>
Z树恢复^衡,我们需要进行两步,W一步,把k1作ؓ根,q行一ơ右x转,旋{之后变成了左左情况Q所以第二步再进行一ơ左左旋转,最后得C一以k2为根的^衡二叉树树?/p>
代码如下Q?/p>
W七步:插入
插入的方法和二叉查找树基本一P区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径Q每l过一个节炚w要维持树的^衡。维持树的^衡要Ҏ(gu)高度差的特点选择不同的旋转算法?/p>
代码如下Q?/p>
W八步:查找
和二叉查找树相比Q查找方法没有变法,不过Ҏ(gu)存储的特性,AVL树能l持在一个O(logN)的稳定的旉Q而二叉查找树则相当不E_?/p>
代码如下Q?/p>
W九(ji)步:删除
删除的方法也和二叉查找树的一_区别是,删除完成后,需要从删除节点的父亲开始向上维护树的^衡一直到根节炏V?/p>
代码如下Q?/p>
W十步:中序遍历
代码如下Q?/p>
W十一步:关于效率
此数据结构插入、查扑֒删除的时间复杂度均ؓO(logN)Q但是插入和删除需要额外的旋{法需要的旉Q有时旋转过多也会媄响效率?/p>
关于递归和非递归。我用的是递归的方法进行插入,查找和删除,而非递归的方法一般来说要比递归的方法快很多Q但是我感觉非递归的方法写出来会比较困难,所以我q是选择了递归的方法?/p>
q有一U效率的问题是关于高度信息的存储Q由于我们需要的仅仅是高度的差,不需要知道这|的高度,所以只需要用两个二q制位就可以表示q个差。这样可以避免^衡因子的重复计算Q可以稍微的加快一些速度Q不q代码也丧失了相对简明性和清晰度。如果采用递归写法的话Q这U微加速就更显得微乎其微了?/p>
如果有哪些不对的或者不清晰的地方请指出Q我会修改ƈ加以完善?/p>
附:完整代码
作者:C加 更新旉Q?/span>2012-8-9
二叉查找树(BSTQ是二叉树的一个重要的应用Q它在二叉树的基上加上了q样的一个性质Q对于树中的每一个节Ҏ(gu)_如果有左儿子的话Q它的左儿子的g定小于它本n的|如果有右儿子的话Q它的右儿子的g定大于它本n的倹{?/span>
二叉查找树的操作一般有插入、删除和查找Q这几个操作的^均时间复杂度都ؓO(logn)Q插入和查找操作很简单,删除操作会复杂一点,除此之外Q因Z叉树的中序遍历是一个有序序列,我就额外加上了一个中序遍历操作?/span>
二叉查找树的应用不是很多Q因为它最坏的时候跟U性表差不多,大部分会应用到它的升U版Q^衡二叉树和红黑树Q这两棵树都能把旉复杂度稳定在O(logn)左右。虽然不会用刎ͼ但是二叉查找树是一定要学好的,毕竟它是q二叉树和U黑树的基础?/span>
接下来一步一步写一个二叉查找树模板?a href="/Files/cxiaojia/bst.zip">完整代码下蝲
W一步:节点信息
二叉查找树的节点和二叉树的节点大部分是一LQ不同的是,二叉查找树多了一个值出现的ơ数。如?/span>1昄了二叉查找树的节点信息?br />
代码如下Q?/span>
W二步:二叉查找树类的声?/span>
代码如下Q?/span>
W三步:插入
Ҏ(gu)二叉查找树的性质Q插入一个节点的时候,如果根节点ؓI,此节点作ؓ根节点,如果根节点不为空Q就要先和根节点比较Q如果比根节点的值小Q就插入到根节点的左子树中,如果比根节点的值大插入到根节点的叛_树中Q如此递归下去Q找到插入的位置。重复节点的插入用值域中的freq标记。如?/span>2是一个插入的q程?/span>
二叉查找树的旉复杂度要看这|的Ş态,如果比较接近一一完全二叉树Q那么时间复杂度?/span>O(logn)左右Q如果遇到如?/span>3q样的二叉树的话Q那么时间复杂度׃恢复到线性的O(n)了?/span>
q二叉树会很好的解军_?/span>3q种情况?/span>
插入函数的代码如下:
W四步:查找
查找的功能和插入差不多一P按照插入那样的方式递归下去Q如果找CQ就q回q个节点的地址Q如果没有找刎ͼp?/span>NULL?/span>
代码如下Q?/span>
W五步:删除
删除会麻烦一点,如果是叶子节点的话,直接删除可以了。如果只有一个孩子的话,p它的父亲指向它的儿子Q然后删除这个节炏V图4昄了一初始树?/span>4节点被删除后的结果。先用一个时指针指?/span>4节点Q再?/span>4节点的地址指向它的孩子Q这个时?/span>2节点的右儿子变成了3节点Q最后删除时节Ҏ(gu)向的I间Q也是4节点?/span>
删除有两个儿子的节点会比较复杂一些。一般的删除{略是用其右子树最的数据代替该节点的数据q归的删除掉叛_树中最数据的节点。因为右子树中数据最的节点肯定没有左儿子,所以删除的时候容易一些。图5昄了一初始树?/span>2节点被删除后的结果。首先在2节点的右子树中找到最的节点3Q然后把3的数据赋值给2节点Q这个时?/span>2节点的数据变?/span>3Q然后的工作是删除叛_树中?/span>3节点了,采用递归删除?/span>
我们发现?/span>2节点叛_树的查找q行了两遍,W一遍找到最节点ƈ赋|W二遍删除这个最的节点Q这L效率q不是很高。你能不能写出只查找一ơ就可以实现赋值和删除两个功能的函数呢Q?/span>
如果删除的次C是很多的话,有一U删除的Ҏ(gu)会比较快一点,名字叫懒惰删除法Q当一个元素要被删除时Q它仍留在树中,只是多了一个删除的标记。这U方法的优点是删除那一步的旉开销可以避免了Q如果重新插入删除的节点的话Q插入时也避免了分配I间的时间开销。缺Ҏ(gu)树的深度会增加,查找的时间复杂度会增加,插入的时间可能会增加?/span>
删除函数代码如下Q?/span>
W六步:中序遍历
遍历的方法和二叉树的Ҏ(gu)一P写这个方法的目的呢,是输个二叉查找树的有序序列?/span>
代码如下Q?/span>
到此Q整个代码就完成了,代码中肯定有很多不完善的地方h出,我会加以完善Q谢谢?/span>
对于二叉查找树不E_的时间复杂度的解x案有不少Q^衡二叉树、展树和红黑树都可以解册个问题,但效果是不一L?/span>
作者:C加 更新旉Q?/span>2012-8-6
二叉树首先是一|Q每个节炚w不能有多于两个的儿子Q也是树的度不能超q?。二叉树的两个儿子分别称?#8220;左儿?#8221;?#8220;叛_?#8221;Q次序不能颠倒。如?是一个简单的二叉树?br />
二叉树的U类
一U是满二叉树Q除了最后一层的叶子节点外,每一层的节点都必L两个儿子节点。如?是一个满二叉树?br />
另一U是完全二叉树,一二叉树L最后一层后剩下的节点组成的树ؓ满二叉树Q最后一层的节点从左到右q箋Q没有空出的节点Q这L树称为完全二叉树。如?是一完全二叉树?br />
二叉树的实现
因ؓ一|有最多只有两个儿子,所以我们可以用指针直接指向他们。一个节点包括|dataQ、指向左儿子的指针(lsonQ和指向叛_子的指针QrsonQ?/span>
二叉树的插入,删除,查找和链表差不多,不同的是需要指定是左儿子还是右儿子?/span>
二叉树的数组实现也很单,假如说根节点在arr[0]q个位置Q那么它的左儿子在arr[2*0+1]Q也是arr[1]q个位置Q它的右儿子在arr[2*0+2] Q也是arr[2]q个位置。对于下标ؓi的节Ҏ(gu)_它的左儿子的下标?*i+1Q右儿子的下标ؓ2*i+2?/span>
二叉树的遍历
二叉树的遍历有三U,分别为先序遍历,中序遍历和后序遍历。这三种遍历方式是根据根节点的读取顺序来分的Q?/span>
先序遍历Q就是最先读取根节点Q然后再d左子树(按照同样的方法读取子树上的节点)Q最后读取右子树Q?/span>
中序遍历Q就是第二个d根节点,最先要d的是左子树,然后根节点,最后右子树Q?/span>
后序遍历Q就是最后一个读取根节点Q最先读取的是左子树Q第二个d叛_树,最后读取根节点?/span>
先序遍历的递归实现代码Q?/span>
作者:C加 更新旉Q?/span>2012-8-3
无论是链表,栈还是队列,它们都是U性结构的Q每个节点的左边最多一个节点,双也最多一个节点,对于大量的输入数据,U性表的访问时间太慢,不宜使用。这里我要说一U非U性的数据l构Q其大部分操作的q行旉q_为O(logn)?/span>
我们涉及到的q种数据l构叫做树。在计算机科学中Q树是非常有用的抽象概念。我们Ş象的LqC|Q一个家族的老祖可能有两个儿子,q两个儿子一个有一个儿子,一个有三个儿子Q像q样发展下去的一个族谱,是一个树Q如?所C?/span>
像一늜正的树一P我们把老祖UCؓ树根,两个字儿是分叉开的两个树枝,q两|枝可以l向下分成N个树枝,循环下去Q一直到长出叶子为止?/span>
我们把老祖或者树根称为根QrootQ节点,老祖的儿子称为子节点Q每个儿子作为根节点又可以Ş成一|Q我们把q样的树UCؓ根节点的子树?/span>
树的标准定义Q?/span>
树(treeQ是包含nQn>0Q个节点的有I集合,其中Q?/span>
Q?Q每个元素称点(nodeQ;
Q?Q有一个特定的节点被称为根节点或树根(rootQ?/span>
Q?Q除根节点之外的其余数据元素被分为mQm≥0Q个互不怺的结合T1QT2Q?#8230;…Tm-1Q其中每一个集合TiQ?<=i<=mQ本w也是一|Q被UC原树的子树(subtreeQ?/span>
树具有以下特点:
Q?Q?span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman'; "> 每个节点有零个或多个子节炏V?/span>
Q?Q?span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman'; "> 每个子节点只有一个父节点?/span>
Q?Q?span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman'; "> 没有父节点的节点UCؓ根节炏V?/span>
关于树的一些术?/span>
节点的度Q一个节点含有的子树的个数称节点的度Q?/span>
叶节Ҏ(gu)l端节点Q度为零的节点称为叶节点Q?/span>
非终端节Ҏ(gu)分支节点Q度不ؓ零的节点Q?/span>
双亲节点或父节点Q若一个结点含有子节点Q则q个节点UCؓ其子节点的父节点Q?/span>
孩子节点或子节点Q一个节点含有的子树的根节点UCؓ该节点的子节点;
兄弟节点Q具有相同父节点的节点互UCؓ兄弟节点Q?/span>
树的高度或深度:定义一|的根l点层次?Q其他节点的层次是其父结点层ơ加1。一|中所有结点的层次的最大值称|的深度。节点的层次Q从根开始定义vQ根为第1层,根的子结点ؓW?层,以此cLQ?/span>
树的度:一|中,最大的节点的度UCؓ树的度;
节点的祖先:从根到该节点所l分支上的所有节点;
子孙Q以某节点ؓ根的子树中Q一节点都称节点的子孙?/span>
林Q由mQm>=0Q棵互不怺的树的集合称为森林;
树的实现
节点的代码如?
树的应用
大部分操作系l的目录l构是采用树结构?/span>
树的U类有很多,树所扩展出来的很多数据结构都有着很大的作用,比如说红黑树QB树,后缀树等{,q将在日后写到?/span>
作者:C加 更新旉Q?/span>2012-8-2
像栈一P队列QqueueQ也是一U线性表Q它的特性是先进先出Q插入在一端,删除在另一端。就像排队一P刚来的h入队QpushQ要排在队尾(rear)Q每ơ出?pop)的都是队?front)的h。如?Q描qC一个队列模型?/span>
和栈一P队列也有数组实现和链表实CU,两种实现都能l出快速的O(1)q行旉Q区别在于链表实现指针域要占用空_频繁的new和delete会消耗不的旉开销Q数l实现唯一的缺Ҏ(gu)建立时要定I间大小?/span>
假如一个队列最多只能站10个hQ当占满10个h后,W?1个h׃能入队,q种情况成ؓ溢出。而如果第一个h出队了,剩下?个h依然q在原来的位|,队列里空Z一个位|,但第11个hq是不能入队Q这U情冉|为假溢出。克服假溢出有效的办法是使用循环队列?/span>
循环队列是把队֒队首q接hQŞ成一个环Q队下一个位|就是队首,q样可以有效的防止假溢出现象Q但队列的实际容量已然固定?/span>
队列的实?/span>
队列的数l实现和栈差不多Q不同的是,栈用top做下标,队列用front和rear作ؓ下标?/span>
我更改了单链表的模板来实C个简单的队列。代码仅供学习,不之处q请指明Q我会对不之处q行修改和更新?/span>
代码如下Q?br />
队列的应?br />
打印机处理作业采用的是队列l构Q它们会按照提交的顺序排列v来。STL也给Z一个强大的队列Q我们直接可以去用它?/span>
队列相关问题
如何用两个栈模拟一个队列,如果用两个队列模拟一个栈Q?/span>
作者:C加 更新旉Q?/span>2012-8-1
栈(stackQ是限制插入和删除只能在一个位|上q行的线性表Q该位置在表的末端,叫做栈顶。添加元素只能在节点后dQ删除元素只能删除尾节点Q查看节点也只能查看节炏V添加、删除、查看依ơؓ入栈QpushQ、出栈(popQ、栈节点(topQ。Ş象的_栈是一个先q后出(LIFOQ表Q先q去的节点要{到后边q去的节点出来才能出来?/span>
如图1Q是一个栈的Ş象图Qtop指针指向的是栈顶节点Q所以我们可以通过top讉K?节点Q但??节点׃先于2q入q个表,所以是不可见的。如果把0节点当做头节点,2节点当做节点,那么栈限制了讉K权限Q只可以讉K节炏V?/span>
如图2Q当d一个节?的时候,只能在栈节点,也就是尾节点后添加,q样3节点变成了栈Ӟ2节点变成了不可见节点Q访问的时候只能访问到3节点。入栈时限制了插入地址Q只能在栈顶d节点?/span>
当我们执行出栈的命oӞ?的栈元素是3节点Q删除的时候只能允许删除栈的元素Q这样子3节点被删除,top指向删除后的栈顶2节点Q如?所C?/span>
栈有两种是实现结构,一U是序存储l构Q也是利用数组实现Q一U是铑ּ存储l构Q可以用单链表实现。数l实现栈很简单,用一个下标标记top来表C栈Ӟtop==-1Ӟ栈空Qtop==0Ӟ表示栈里只有一个元素,通过讉KtopZ标的数组元素卛_。出栈top自减Q入栈top自加O(jin)K了?/span>
单链表实现栈要比单链表的实现单点。我们通过在表的尾端插入来实现pushQ通过删除节Ҏ(gu)实现popQ获取尾节点的元素来表示top。我修改了链表那一章的单链表代码,把头节点当做栈顶节点Q实C一个简单的栈模板,仅供学习所用。代码会不定时更新?a href="/Files/cxiaojia/stack.rar">代码下蝲
代码如下Q?br />
很清楚,除了clear函数外,所有的Ҏ(gu)的时间复杂度都是O(1)。这U实现方式的~点在于?span lang="EN-US">new?span lang="EN-US">delete的调用的开销是昂늚Q所以采用数l的方式实现会更好一炏V?br />
栈的应用
使用栈的时候一般不用自己重新去写,因ؓSTLl我们实C一个很安全的栈Q可以放心去使用?/span>也可以用数组模拟一个,很简单?/span>
~译器调用函数就用了栈结构,当第一个函数还没执行完毕,调用W二个函数的时候,~译器就会把W一个函数压栈,W二个函数调用完毕的时候,׃取栈函敎ͼ也就是第一个函数l执行?span lang="EN-US">
qW号 http://acm.nyist.net/JudgeOnline/problem.php?pid=2
中缀转后~ http://acm.nyist.net/JudgeOnline/problem.php?pid=467
后缀试求?http://acm.nyist.net/JudgeOnline/problem.php?pid=35
poj 3250 http://acm.pku.edu.cn/JudgeOnline/problem?id=3250
poj 1363 http://acm.pku.edu.cn/JudgeOnline/problem?id=1363
poj 1208 http://acm.pku.edu.cn/JudgeOnline/problem?id=1208
poj 1686 http://acm.pku.edu.cn/JudgeOnline/problem?id=1686
poj 3250 http://acm.pku.edu.cn/JudgeOnline/problem?id=2045
hdu 1022 http://acm.hdu.edu.cn/showproblem.php?pid=1022
作者:C加 更新旉Q?/span>2012-7-31
谈到链表之前Q先说一下线性表。线性表是最基本、最单、也是最常用的一U?/span>数据l构。线性表中数据元素之间的关系是一对一的关p,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表有两U存储方式,一U是序存储l构Q另一U是铑ּ存储l构?/span>
序存储l构是两个盔R的元素在内存中也是相?c)。这U存储方式的优点是查询的旉复杂度ؓO(1)Q通过首地址和偏U量可以直接访问到某元素,关于查找的适配法很多Q最快可以达到O(logn)。缺Ҏ(gu)插入和删除的旉复杂度最坏能辑ֈO(n)Q如果你在第一个位|插入一个元素,你需要把数组的每一个元素向后移动一位,如果你在W一个位|删除一个元素,你需要把数组的每一个元素向前移动一位。还有一个缺点,是当你不确定元素的数量Ӟ你开的数l必M证能够放下元素最大数量,遗憾的是如果实际数量比最大数量少很多Ӟ你开的数l没有用到的内存只能浪Ҏ(gu)了?/span>
我们常用的数l就是一U典型的序存储l构Q如??/span>
铑ּ存储l构是两个盔R的元素在内存中可能不是相?c)Q每一个元素都有一个指针域Q指针域一般是存储着C一个元素的指针。这U存储方式的优点是插入和删除的时间复杂度为O(1)Q不会浪费太多内存,d元素的时候才会申请内存,删除元素会释攑ֆ存,。缺Ҏ(gu)讉K的时间复杂度最坏ؓO(n)Q关于查扄法很少Q一般只能遍历,q样旉复杂度也是线性(O(n)Q的?频繁的申请和释放内存也会消耗时间?/span>
序表的Ҏ(gu)是随机dQ也是讉K一个元素的旉复杂度是O(1)Q链式表的特性是插入和删除的旉复杂度ؓO(1)。要Ҏ(gu)实际情况去选取适合自己的存储结构?/span>
链表是铑ּ存储的线性表。根据指针域的不同,链表分ؓ单向链表、双向链表、@环链表等{?/span>
一?单向链表QslistQ?/span>
链表中最单的一U是单向链表Q每个元素包含两个域Q值域和指针域Q我们把q样的元素称之ؓ节点。每个节点的指针域内有一个指针,指向下一个节点,而最后一个节点则指向一个空倹{如?是一个单向链表?/span>
一个单向链表的节点被分成两个部分。第一个部分保存或者显C关于节点的信息Q第二个部分存储下一个节点的地址。单向链表只可向一个方向遍历?/span>
我写了一个简单的C++版单向链表类模板Q就用这D代码讲解一下一个具体的单向链表该怎么写(代码仅供学习Q,当然首先你要具备C++基础知识和简单的模板元编E?br />完整代码
首先我们要写一个节点类Q链表中的每一个节点就是一个节点类的对象。如??br />
代码如下Q?/span>
W二步,写单链表cȝ声明Q包括属性和Ҏ(gu)?/span>
代码如下Q?/span>
W三步,写构造函敎ͼ初始化链表类的属性?/span>
代码如下Q?/span>
W四步,实现add()Ҏ(gu)?/span>
代码如下Q?/span>
W五步,实现traversal()函数Q遍历ƈ输出节点信息?/span>
代码如下Q?br />
W六步,实现isEmpty()函数Q判断链表是否ؓI,q回真ؓI,假则不空?/span>
代码如下Q?/span>
W七步,实现find()函数?/span>
代码如下Q?br />
W八步,实现delete()函数Q删除第一个gؓx的节点,如图4?/span>
代码如下Q?br />
W九(ji)步,实现insert()?span lang="EN-US">insertHead()函数Q在p节点后插入gؓx的节炏V如?span lang="EN-US">5?span lang="EN-US">
代码如下Q?/span>
最l,我们完成一个简单的单向链表。此单向链表代码q有很多待完善的地方Q以后会修改代码q不定时更新?/span>
二?双向链表
双向链表的指针域有两个指针,每个数据l点分别指向直接后和直接前驱。单向链表只能从表头开始向后遍历,而双向链表不但可以从前向后遍历,也可以从后向前遍历。除了双向遍历的优点Q双向链表的删除的时间复杂度会降为OQ?Q,因ؓ直接通过目的指针可以找到前p点,单向链表得从表头开始遍历寻扑։p炏V缺Ҏ(gu)每个节点多了一个指针的I间开销。如?是一个双向链表?/span>
三?循环链表
循环链表是让链表的最后一个节Ҏ(gu)向第一个节点,q样Ş成了一个圆环,可以循环遍历。单向@环链表可以单向@环遍历,双向循环链表的头节点的指针也要指向最后一个节点,q样的可以双向@环遍历。如?是一个双向@环链表?/span>
四?链表相关问题
1、如何判断一个单链表有环
2、如何判断一个环的入口点在哪?/span>
3、如何知道环的长?/span>
4、如何知道两个单链表Q无环)是否怺
5、如果两个单链表Q无环)怺Q如何知道它们相交的W一个节Ҏ(gu)什?/span>
6、如何知道两个单链表Q有环)是否怺
7、如果两个单链表Q有环)怺Q如何知道它们相交的W一个节Ҏ(gu)什?/span>
hash_mapQ顾名思义Q就是利?/span>hash_set存储l构的写map映照容器Q普通的map用的是红黑树存储l构写的。元素的索时间对比,hash_setq似?/span>O(1)Q红黑树?/span>O(logn)?/span>Hash_map的空间开销要比map 的空间开销大,其是我用数l模拟指针写?/span>hash_map?/span>
所以,如果有必要采取映结构的时候,能用hash_map别?/span>map?/span>
需要注意的是,hash_map遍历出来的元素不是有序的?/span>
Hash_map和普?/span>hash_set相比的话Q?/span>hash_map?/span>hash_set多了一?/span>value表,也就是映表?/span>
下面?/span>hash_map的模板,?/span>hash_set的模板差不多?br />
l你一串数字,让你求出某个子串中某个数字出现的ơ数?/span>
用邻接表储存每个数出现的位置Q然后对L表进行二分查找,扑և区间?/span>
作者:C加 更新旉Q?/span>2012-8-16
Ƣ迎自荐?span style="color: red; ">推荐链接。请于留a处告知?br />