算法系列15天速成——第十一天 树操作(上)

原文:算法系列15天速成——第十一天 树操作(上)

最近项目赶的紧,歇了一个星期没写博客了,趁周末继续写这个系列。

先前我们讲的都是“线性结构”,他的特征就是“一个节点最多有一个”前驱“和一个”后继“。那么我们今天讲的树会是怎样的呢?

我们可以对”线性结构“改造一下,变为”一个节点最多有一个"前驱“和”多个后继“。哈哈,这就是我们今天说的”树“。

一: 树

我们思维中的”树“就是一种枝繁叶茂的形象,那么数据结构中的”树“该是怎么样呢?对的,他是一种现实中倒立的树。

1:术语

其实树中有很多术语的,这个是我们学习树形结构必须掌握的。

<1>  父节点,子节点,兄弟节点

这个就比较简单了,B和C的父节点就是A,反过来说就是B和C是A的子节点。B和C就是兄弟节点。

<2>  结点的度

其实”度“就是”分支数“,比如A的分支数有两个“B和C",那么A的度为2。

<3> 树的度

看似比较莫名其妙吧,他和”结点的度“的区别就是,树的度讲究大局观,乃树中最大的结点度,其实也就是2。

<4> 叶结点,分支结点

叶结点就是既没有左孩子也没有右孩子结点,也就是结点度为0。分支节点也就是if的else的条件咯。

<5> 结点的层数

这个很简单,也就是树有几层。

<6> 有序树,无序树

有序树我们先前也用过,比如“堆”和“二叉排序树”,说明这种树是按照一定的规则进行排序的,else条件就是无序树。

<7>  森林

现实中,很多的树形成了森林,那在数据结构中,我们把上图的“A”节点砍掉,那么B,C子树合一起就是森林咯。

2: 树的表示

树这个结构的表示其实有很多种,常用的也就是“括号”表示法。

比如上面的树就可以表示为:(A(B(D),(E)),(C(F),(G)))

二: 二叉树

在我们项目开发中,很多地方都会用到树,但是多叉树的处理还是比较纠结的,所以俺们本着“大事化小,小事化了“的原则

把”多叉树“转化为”二叉树“,那么问题就简化了很多。

1: ”二叉树“和”树“有什么差异呢?

第一点:  树的度没有限制,而“二叉树”最多只能有两个,不然也就不叫二叉树了,哈哈。

第二点:树中的子树没有左右划分,很简单啊,找不到参照点,二叉树就有参照物咯。

2: 二叉树的类型

二叉树中有两种比较完美的类型,“完全二叉树”和“满二叉树”。

<1>  满二叉树

除叶子节点外,所有节点的度都为2,文章开头处的树就是这里的“满二叉树”。

<2>  完全二叉树

必须要满足两个条件就即可:  干掉最后一层,二叉树变为“满二叉树”。

最后一层的叶节点必须是“从左到右”依次排开。

我们干掉文章开头处的节点“F和”G",此时还是“完全二叉树”,但已经不是“满二叉树”了,你懂的。

3: 二叉树的性质

二叉树中有5点性质非常重要,也是俺们必须要记住的。

<1>  二叉树中,第i层的节点最多有2(i-1)个。

<2>  深度为k的二叉树最多有2k-1个节点。

<3>  二叉树中,叶子节点树为N1个,度为2的节点有N2个,那么N1=N2+1。

<4>  具有N个结点的二叉树深度为(Log2 N)+1层。

<5>  N个结点的完全二叉树如何用顺序存储,对于其中的一个结点i,存在以下关系,

2*i是结点i的父结点。

i/2是结点i的左孩子。

(i/2)+1是结点i的右孩子。

4: 二叉树的顺序存储

同样的存储方式也有两种,“顺序存储”和“链式存储”。

<1> 顺序存储

说实话,树的存储用顺序结构比较少,因为从性质定理中我们都可以看出只限定为“完全二叉树”,那么如果二叉树不是

“完全二叉树”,那我们就麻烦了,必须将其转化为“完全二叉树”,将空的节点可以用“#”代替,图中也可看出,为了维护

性质定理5的要求,我们牺牲了两个”资源“的空间。

<2> 链式存储

上面也说了,顺序存储会造成资源的浪费,所以嘛,我们开发中用的比较多的还是“链式存储”,同样“链式存储”

也非常的形象,非常的合理。

一个结点存放着一个“左指针”和一个“右指针”,这就是二叉链表。

如何方便的查找到该结点的父结点,可以采用三叉链表。

5: 常用操作

一般也就是“添加结点“,“查找节点”,“计算深度”,“遍历结点”,“清空结点”

<1> 这里我们就用二叉链表来定义链式存储模型

 1 #region 二叉链表存储结构 2     /// <summary> 3 /// 二叉链表存储结构 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6     public class ChainTree<T> 7     { 8         public T data; 9 10         public ChainTree<T> left;11 12         public ChainTree<T> right; 13     }14     #endregion

<2> 添加结点

要添加结点,我们就要找到添加结点的父结点,并且根据指示插入到父结点中指定左结点或者右结点。

 1 #region 将指定节点插入到二叉树中 2         /// <summary> 3 /// 将指定节点插入到二叉树中 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="tree"></param> 7 /// <param name="node"></param> 8 /// <param name="direction">插入做左是右</param> 9 /// <returns></returns>10         public ChainTree<T> BinTreeAddNode<T>(ChainTree<T> tree, ChainTree<T> node, T data, Direction direction)11         {12             if (tree == null)13                 return null;14 15             if (tree.data.Equals(data))16             {17                 switch (direction)18                 {19                     case Direction.Left:20                         if (tree.left != null)21                             throw new Exception("树的左节点不为空,不能插入");22                         else23                             tree.left = node;24 25                         break;26                     case Direction.Right:27                         if (tree.right != null)28                             throw new Exception("树的右节点不为空,不能插入");29                         else30                             tree.right = node;31 32                         break;33                 }34             }35 36             BinTreeAddNode(tree.left, node, data, direction);37             BinTreeAddNode(tree.right, node, data, direction);38 39             return tree;40         }41         #endregion     

<3>  查找节点

二叉树中到处都散发着递归思想,很能锻炼一下我们对递归的认识,同样查找也是用到了递归思想。

 1         #region 在二叉树中查找指定的key 2         /// <summary> 3 ///在二叉树中查找指定的key 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="tree"></param> 7 /// <param name="data"></param> 8 /// <returns></returns> 9         public ChainTree<T> BinTreeFind<T>(ChainTree<T> tree, T data)10         {11             if (tree == null)12                 return null;13 14             if (tree.data.Equals(data))15                 return tree;16 17             return BinTreeFind(tree, data);18         }19         #endregion

<4> 计算深度

这个问题纠结了我二个多小时,原因在于没有深刻的体会到递归,其实主要思想就是递归左子树和右子树,然后得出较大的一个。

 1 #region 获取二叉树的深度 2         /// <summary> 3 /// 获取二叉树的深度 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="tree"></param> 7 /// <returns></returns> 8         public int BinTreeLen<T>(ChainTree<T> tree) 9         {10             int leftLength;11             int rightLength;12 13             if (tree == null)14                 return 0;15 16             //递归左子树的深度17             leftLength = BinTreeLen(tree.left);18 19             //递归右子书的深度20             rightLength = BinTreeLen(tree.right);21 22             if (leftLength > rightLength)23                 return leftLength + 1;24             else25                 return rightLength + 1;26         }27         #endregion

<5>  遍历结点

二叉树中遍历节点的方法还是比较多的,有“先序”,“中序”,“后序”,“按层”,其实这些东西只可意会,不可言传,真的很难在口头

上说清楚,需要反复的体会递归思想。

先序:先访问根,然后递归访问左子树,最后递归右子树。(DLR模式)

中序:先递归访问左子树,在访问根,最后递归右子树。(LDR模式)

后序:先递归访问左子树,然后递归访问右子树,最后访问根。(LRD模式)

按层:这个比较简单,从上到下,从左到右的遍历节点。

  1  #region 二叉树的先序遍历  2         /// <summary>  3 /// 二叉树的先序遍历  4 /// </summary>  5 /// <typeparam name="T"></typeparam>  6 /// <param name="tree"></param>  7         public void BinTree_DLR<T>(ChainTree<T> tree)  8         {  9             if (tree == null) 10                 return; 11  12             //先输出根元素 13             Console.Write(tree.data + "\t"); 14  15             //然后遍历左子树 16             BinTree_DLR(tree.left); 17  18             //最后遍历右子树 19             BinTree_DLR(tree.right); 20         } 21         #endregion 22  23         #region 二叉树的中序遍历 24         /// <summary> 25 /// 二叉树的中序遍历 26 /// </summary> 27 /// <typeparam name="T"></typeparam> 28 /// <param name="tree"></param> 29         public void BinTree_LDR<T>(ChainTree<T> tree) 30         { 31             if (tree == null) 32                 return; 33  34             //优先遍历左子树 35             BinTree_LDR(tree.left); 36  37             //然后输出节点 38             Console.Write(tree.data + "\t"); 39  40             //最后遍历右子树 41             BinTree_LDR(tree.right); 42         } 43         #endregion 44  45         #region 二叉树的后序遍历 46         /// <summary> 47 /// 二叉树的后序遍历 48 /// </summary> 49 /// <typeparam name="T"></typeparam> 50 /// <param name="tree"></param> 51         public void BinTree_LRD<T>(ChainTree<T> tree) 52         { 53             if (tree == null) 54                 return; 55  56             //优先遍历左子树 57             BinTree_LRD(tree.left); 58  59             //然后遍历右子树 60             BinTree_LRD(tree.right); 61  62             //最后输出节点元素 63             Console.Write(tree.data + "\t"); 64         } 65         #endregion 66  67         #region 二叉树的按层遍历 68         /// <summary> 69 /// 二叉树的按层遍历 70 /// </summary> 71 /// <typeparam name="T"></typeparam> 72 /// <param name="tree"></param> 73         public void BinTree_Level<T>(ChainTree<T> tree) 74         { 75             if (tree == null) 76                 return; 77  78             //申请保存空间 79             ChainTree<T>[] treeList = new ChainTree<T>[Length]; 80  81             int head = 0; 82             int tail = 0; 83  84             //存放数组 85             treeList[tail] = tree; 86  87             //循环链中计算tail位置 88             tail = (tail + 1) % Length; 89  90             while (head != tail) 91             { 92                 var tempNode = treeList[head]; 93  94                 head = (head + 1) % Length; 95  96                 //输出节点 97                 Console.Write(tempNode.data + "\t"); 98  99                 //如果左子树不为空,则将左子树存于数组的tail位置100                 if (tempNode.left != null)101                 {102                     treeList[tail] = tempNode.left;103 104                     tail = (tail + 1) % Length;105                 }106 107                 //如果右子树不为空,则将右子树存于数组的tail位置108                 if (tempNode.right != null)109                 {110                     treeList[tail] = tempNode.right;111 112                     tail = (tail + 1) % Length;113                 }114             }115         }116         #endregion

<6> 清空二叉树

虽然C#里面有GC,但是我们能自己释放的就不麻烦GC了,同样清空二叉树节点,我们用到了递归,说实话,这次练习让我喜欢

上的递归,虽然XXX的情况下,递归的不是很好,但是递归还是很强大的。

 1 #region 清空二叉树 2         /// <summary> 3 /// 清空二叉树 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="tree"></param> 7         public void BinTreeClear<T>(ChainTree<T> tree) 8         { 9             //递的结束点,归的起始点10             if (tree == null)11                 return;12 13             BinTreeClear(tree.left);14             BinTreeClear(tree.right);15 16             //在归的过程中,释放当前节点的数据空间17             tree = null;18         }19         #endregion

最后上一下总的代码

  1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Text;  5   6 namespace ChainTree  7 {  8     public class Program  9     { 10         static void Main(string[] args) 11         { 12             ChainTreeManager manager = new ChainTreeManager(); 13  14             //插入节点操作 15             ChainTree<string> tree = CreateRoot(); 16  17             //插入节点数据 18             AddNode(tree); 19  20             //先序遍历 21             Console.WriteLine("\n先序结果为: \n"); 22             manager.BinTree_DLR(tree); 23  24             //中序遍历 25             Console.WriteLine("\n中序结果为: \n"); 26             manager.BinTree_LDR(tree); 27  28             //后序遍历 29             Console.WriteLine("\n后序结果为: \n"); 30             manager.BinTree_LRD(tree); 31  32             //层次遍历 33             Console.WriteLine("\n层次结果为: \n"); 34             manager.Length = 100; 35             manager.BinTree_Level(tree); 36  37             Console.WriteLine("\n树的深度为:" + manager.BinTreeLen(tree) + "\n"); 38  39             Console.ReadLine(); 40  41         } 42  43         #region 生成根节点 44         /// <summary> 45 /// 生成根节点 46 /// </summary> 47 /// <returns></returns> 48         static ChainTree<string> CreateRoot() 49         { 50             ChainTree<string> tree = new ChainTree<string>(); 51  52             Console.WriteLine("请输入根节点,方便我们生成树\n"); 53  54             tree.data = Console.ReadLine(); 55  56             Console.WriteLine("根节点生成已经生成\n"); 57  58             return tree; 59         } 60         #endregion 61  62         #region 插入节点操作 63         /// <summary> 64 /// 插入节点操作 65 /// </summary> 66 /// <param name="tree"></param> 67         static ChainTree<string> AddNode(ChainTree<string> tree) 68         { 69             ChainTreeManager mananger = new ChainTreeManager(); 70  71             while (true) 72             { 73                 ChainTree<string> node = new ChainTree<string>(); 74  75                 Console.WriteLine("请输入要插入节点的数据:\n"); 76  77                 node.data = Console.ReadLine(); 78  79                 Console.WriteLine("请输入要查找的父节点数据:\n"); 80  81                 var parentData = Console.ReadLine(); 82  83                 if (tree == null) 84                 { 85                     Console.WriteLine("未找到您输入的父节点,请重新输入。"); 86                     continue; 87                 } 88  89                 Console.WriteLine("请确定要插入到父节点的:1 左侧,2 右侧"); 90  91                 Direction direction = (Direction)Enum.Parse(typeof(Direction), Console.ReadLine()); 92  93                 tree = mananger.BinTreeAddNode(tree, node, parentData, direction); 94  95                 Console.WriteLine("插入成功,是否继续?  1 继续, 2 退出"); 96  97                 if (int.Parse(Console.ReadLine()) == 1) 98                     continue; 99                 else100                     break;101             }102 103             return tree;104         }105         #endregion106     }107 108     #region 插入左节点或者右节点109     /// <summary>110 /// 插入左节点或者右节点111 /// </summary>112     public enum Direction { Left = 1, Right = 2 }113     #endregion114 115     #region 二叉链表存储结构116     /// <summary>117 /// 二叉链表存储结构118 /// </summary>119 /// <typeparam name="T"></typeparam>120     public class ChainTree<T>121     {122         public T data;123 124         public ChainTree<T> left;125 126         public ChainTree<T> right;127     }128     #endregion129 130     /// <summary>131 /// 二叉树的操作帮助类132 /// </summary>133     public class ChainTreeManager134     {135         #region 按层遍历的Length空间存储136         /// <summary>137 /// 按层遍历的Length空间存储138 /// </summary>139         public int Length { get; set; }140         #endregion141 142         #region 将指定节点插入到二叉树中143         /// <summary>144 /// 将指定节点插入到二叉树中145 /// </summary>146 /// <typeparam name="T"></typeparam>147 /// <param name="tree"></param>148 /// <param name="node"></param>149 /// <param name="direction">插入做左是右</param>150 /// <returns></returns>151         public ChainTree<T> BinTreeAddNode<T>(ChainTree<T> tree, ChainTree<T> node, T data, Direction direction)152         {153             if (tree == null)154                 return null;155 156             if (tree.data.Equals(data))157             {158                 switch (direction)159                 {160                     case Direction.Left:161                         if (tree.left != null)162                             throw new Exception("树的左节点不为空,不能插入");163                         else164                             tree.left = node;165 166                         break;167                     case Direction.Right:168                         if (tree.right != null)169                             throw new Exception("树的右节点不为空,不能插入");170                         else171                             tree.right = node;172 173                         break;174                 }175             }176 177             BinTreeAddNode(tree.left, node, data, direction);178             BinTreeAddNode(tree.right, node, data, direction);179 180             return tree;181         }182         #endregion183 184         #region 获取二叉树指定孩子的状态185         /// <summary>186 /// 获取二叉树指定孩子的状态187 /// </summary>188 /// <typeparam name="T"></typeparam>189 /// <param name="tree"></param>190 /// <param name="direction"></param>191 /// <returns></returns>192         public ChainTree<T> BinTreeChild<T>(ChainTree<T> tree, Direction direction)193         {194             ChainTree<T> childNode = null;195 196             if (tree == null)197                 throw new Exception("二叉树为空");198 199             switch (direction)200             {201                 case Direction.Left:202                     childNode = tree.left;203                     break;204                 case Direction.Right:205                     childNode = tree.right;206                     break;207             }208 209             return childNode;210         }211 212         #endregion213 214         #region 获取二叉树的深度215         /// <summary>216 /// 获取二叉树的深度217 /// </summary>218 /// <typeparam name="T"></typeparam>219 /// <param name="tree"></param>220 /// <returns></returns>221         public int BinTreeLen<T>(ChainTree<T> tree)222         {223             int leftLength;224             int rightLength;225 226             if (tree == null)227                 return 0;228 229             //递归左子树的深度230             leftLength = BinTreeLen(tree.left);231 232             //递归右子书的深度233             rightLength = BinTreeLen(tree.right);234 235             if (leftLength > rightLength)236                 return leftLength + 1;237             else238                 return rightLength + 1;239         }240         #endregion241 242         #region 判断二叉树是否为空243         /// <summary>244 /// 判断二叉树是否为空245 /// </summary>246 /// <typeparam name="T"></typeparam>247 /// <param name="tree"></param>248 /// <returns></returns>249         public bool BinTreeisEmpty<T>(ChainTree<T> tree)250         {251             return tree == null ? true : false;252         }253         #endregion254 255         #region 在二叉树中查找指定的key256         /// <summary>257 ///在二叉树中查找指定的key258 /// </summary>259 /// <typeparam name="T"></typeparam>260 /// <param name="tree"></param>261 /// <param name="data"></param>262 /// <returns></returns>263         public ChainTree<T> BinTreeFind<T>(ChainTree<T> tree, T data)264         {265             if (tree == null)266                 return null;267 268             if (tree.data.Equals(data))269                 return tree;270 271             return BinTreeFind(tree, data);272         }273         #endregion274 275         #region 清空二叉树276         /// <summary>277 /// 清空二叉树278 /// </summary>279 /// <typeparam name="T"></typeparam>280 /// <param name="tree"></param>281         public void BinTreeClear<T>(ChainTree<T> tree)282         {283             //递的结束点,归的起始点284             if (tree == null)285                 return;286 287             BinTreeClear(tree.left);288             BinTreeClear(tree.right);289 290             //在归的过程中,释放当前节点的数据空间291             tree = null;292         }293         #endregion294 295         #region 二叉树的先序遍历296         /// <summary>297 /// 二叉树的先序遍历298 /// </summary>299 /// <typeparam name="T"></typeparam>300 /// <param name="tree"></param>301         public void BinTree_DLR<T>(ChainTree<T> tree)302         {303             if (tree == null)304                 return;305 306             //先输出根元素307             Console.Write(tree.data + "\t");308 309             //然后遍历左子树310             BinTree_DLR(tree.left);311 312             //最后遍历右子树313             BinTree_DLR(tree.right);314         }315         #endregion316 317         #region 二叉树的中序遍历318         /// <summary>319 /// 二叉树的中序遍历320 /// </summary>321 /// <typeparam name="T"></typeparam>322 /// <param name="tree"></param>323         public void BinTree_LDR<T>(ChainTree<T> tree)324         {325             if (tree == null)326                 return;327 328             //优先遍历左子树329             BinTree_LDR(tree.left);330 331             //然后输出节点332             Console.Write(tree.data + "\t");333 334             //最后遍历右子树335             BinTree_LDR(tree.right);336         }337         #endregion338 339         #region 二叉树的后序遍历340         /// <summary>341 /// 二叉树的后序遍历342 /// </summary>343 /// <typeparam name="T"></typeparam>344 /// <param name="tree"></param>345         public void BinTree_LRD<T>(ChainTree<T> tree)346         {347             if (tree == null)348                 return;349 350             //优先遍历左子树351             BinTree_LRD(tree.left);352 353             //然后遍历右子树354             BinTree_LRD(tree.right);355 356             //最后输出节点元素357             Console.Write(tree.data + "\t");358         }359         #endregion360 361         #region 二叉树的按层遍历362         /// <summary>363 /// 二叉树的按层遍历364 /// </summary>365 /// <typeparam name="T"></typeparam>366 /// <param name="tree"></param>367         public void BinTree_Level<T>(ChainTree<T> tree)368         {369             if (tree == null)370                 return;371 372             //申请保存空间373             ChainTree<T>[] treeList = new ChainTree<T>[Length];374 375             int head = 0;376             int tail = 0;377 378             //存放数组379             treeList[tail] = tree;380 381             //循环链中计算tail位置382             tail = (tail + 1) % Length;383 384             while (head != tail)385             {386                 var tempNode = treeList[head];387 388                 head = (head + 1) % Length;389 390                 //输出节点391                 Console.Write(tempNode.data + "\t");392 393                 //如果左子树不为空,则将左子树存于数组的tail位置394                 if (tempNode.left != null)395                 {396                     treeList[tail] = tempNode.left;397 398                     tail = (tail + 1) % Length;399                 }400 401                 //如果右子树不为空,则将右子树存于数组的tail位置402                 if (tempNode.right != null)403                 {404                     treeList[tail] = tempNode.right;405 406                     tail = (tail + 1) % Length;407                 }408             }409         }410         #endregion411 412     }413 }

我们把文章开头的“二叉树”的节点输入到我们的结构中,看看遍历效果咋样。

时间: 2024-12-16 21:07:06

算法系列15天速成——第十一天 树操作(上)的相关文章

算法系列15天速成——第三天 七大经典排序【下】

原文:算法系列15天速成--第三天 七大经典排序[下] 今天跟大家聊聊最后三种排序: 直接插入排序,希尔排序和归并排序. 直接插入排序: 这种排序其实蛮好理解的,很现实的例子就是俺们斗地主,当我们抓到一手乱牌时,我们就要按照大小梳理扑克,30秒后, 扑克梳理完毕,4条3,5条s,哇塞......  回忆一下,俺们当时是怎么梳理的. 最左一张牌是3,第二张牌是5,第三张牌又是3,赶紧插到第一张牌后面去,第四张牌又是3,大喜,赶紧插到第二张后面去, 第五张牌又是3,狂喜,哈哈,一门炮就这样产生了.

算法系列15天速成——第四天 五大经典查找【上】

原文:算法系列15天速成--第四天 五大经典查找[上] 在我们的生活中,无处不存在着查找,比如找一下班里哪个mm最pl,猜一猜mm的芳龄....... 对的这些都是查找. 在我们的算法中,有一种叫做线性查找. 分为:顺序查找. 折半查找. 查找有两种形态: 分为:破坏性查找,   比如有一群mm,我猜她们的年龄,第一位猜到了是23+,此时这位mm已经从我脑海里面的mmlist中remove掉了. 哥不找23+的,所以此种查找破坏了原来的结构. 非破坏性查找, 这种就反之了,不破坏结构. 顺序查找

算法系列15天速成——第一天 七大经典排序【上】

原文:算法系列15天速成--第一天 七大经典排序[上] 今天是开篇,得要吹一下算法,算法就好比程序开发中的利剑,所到之处,刀起头落. 针对现实中的排序问题,算法有七把利剑可以助你马道成功. 首先排序分为四种: 交换排序: 包括冒泡排序,快速排序. 选择排序: 包括直接选择排序,堆排序. 插入排序: 包括直接插入排序,希尔排序. 合并排序: 合并排序. 那么今天我们讲的就是交换排序,我们都知道,C#类库提供的排序是快排,为了让今天玩的有意思点, 我们设计算法来跟类库提供的快排较量较量.争取KO对手

算法系列15天速成——第七天 线性表【上】

原文:算法系列15天速成--第七天 线性表[上] 人活在社会上不可能孤立,比如跟美女有着千丝万缕的关系,有的是一对一,有的是一对多,有的是多对多. 哈哈,我们的数据也一样,存在这三种基本关系,用术语来说就是: <1>  线性关系. <2>  树形关系. <3>  网状关系. 一: 线性表 1 概念: 线性表也就是关系户中最简单的一种关系,一对一. 如:学生学号的集合就是一个线性表. 2 特征: ① 有且只有一个“首元素“. ② 有且只有一个“末元素”. ③ 除“末元素”

算法系列15天速成——第十天 栈

原文:算法系列15天速成--第十天 栈 今天跟大家聊聊栈,在程序设计中,栈的使用还是非常广泛的,比如有“括号匹配问题“,”html结构匹配问题“. 所以说掌握了”栈“的使用,对我们学习算法还是很有帮助的. 一: 概念 栈,同样是一种特殊的线性表,是一种Last In First Out(LIFO)的形式,现实中有很多这样的例子, 比如:食堂中的一叠盘子,我们只能从顶端一个一个的取. 二:存储结构 ”栈“不像”队列“,需要两个指针来维护,栈只需要一个指针就够了,这得益于栈是一种一端受限的线性表.

算法系列15天速成——第六天 五大经典查找【下】

原文:算法系列15天速成--第六天 五大经典查找[下] 大家是否感觉到,树在数据结构中大行其道,什么领域都要沾一沾,碰一碰. 就拿我们前几天学过的排序就用到了堆和今天讲的”二叉排序树“,所以偏激的说,掌握的树你就是牛人了. 今天就聊聊这个”五大经典查找“中的最后一个”二叉排序树“. 1. 概念: <1> 其实很简单,若根节点有左子树,则左子树的所有节点都比根节点小. 若根节点有右子树,则右子树的所有节点都比根节点大. <2> 如图就是一个”二叉排序树“,然后对照概念一比较比较. 2

算法系列15天速成——第五天 五大经典查找【中】

原文:算法系列15天速成--第五天 五大经典查找[中] 大家可否知道,其实查找中有一种O(1)的查找,即所谓的秒杀. 哈希查找: 对的,他就是哈希查找,说到哈希,大家肯定要提到哈希函数,呵呵,这东西已经在我们脑子里面形成 固有思维了.大家一定要知道“哈希“中的对应关系. 比如说: ”5“是一个要保存的数,然后我丢给哈希函数,哈希函数给我返回一个”2",那么此时的”5“ 和“2”就建立一种对应关系,这种关系就是所谓的“哈希关系”,在实际应用中也就形成了”2“是key,”5“是value. 那么有的

算法系列15天速成——第二天 七大经典排序【中】

原文:算法系列15天速成--第二天 七大经典排序[中] 首先感谢朋友们对第一篇文章的鼎力支持,感动中.......  今天说的是选择排序,包括“直接选择排序”和“堆排序”. 话说上次“冒泡排序”被快排虐了,而且“快排”赢得了内库的重用,众兄弟自然眼红,非要找快排一比高下. 这不今天就来了两兄弟找快排算账. 1.直接选择排序: 先上图: 说实话,直接选择排序最类似于人的本能思想,比如把大小不一的玩具让三岁小毛孩对大小排个序, 那小孩首先会在这么多玩具中找到最小的放在第一位,然后找到次小的放在第二位

算法系列15天速成——第八天 线性表【下】

原文:算法系列15天速成--第八天 线性表[下] 一:线性表的简单回顾 上一篇跟大家聊过“线性表"顺序存储,通过实验,大家也知道,如果我每次向 顺序表的头部插入元素,都会引起痉挛,效率比较低下,第二点我们用顺序存储时,容 易受到长度的限制,反之就会造成空间资源的浪费. 二:链表 对于顺序表存在的若干问题,链表都给出了相应的解决方案. 1. 概念:其实链表的“每个节点”都包含一个”数据域“和”指针域“. ”数据域“中包含当前的数据. ”指针域“中包含下一个节点的指针. ”头指针”也就是head,指