c语言数据结构顺序表的代码,c语言数据结构用什么软件

  c语言数据结构顺序表的代码,c语言数据结构用什么软件

  第一篇前言。链式二叉树的基本结构2。分治递归思想3。前/中/后顺序遍历3.1通过递归遍历计算节点数3.2用后继遍历的思想破坏树3.3前/中/后顺序遍历OJ问题4。计算节点数4.1叶节点数4.2二叉树第k层节点数4.3求二叉树的深度OJ问题5。在树6中找到值为X的节点。序列遍历6.1判断二叉树是否是完全二叉树结论前言在前面对树的学习中,我们接触了二叉树的知识点以及堆的操作和堆排序。

  两个知识点都是超链接。可以点击查看我之前的博客,复习这两个知识点!

  接下来,我们将更进一步,学习链式二叉树的运算。

  本博客将通过知识点讲解OJ题目验证,扩展链式二叉树的内容。

  1.链式二叉树的基本结构在学习链式二叉树的基本运算之前,需要先创建一棵二叉树,然后才能学习其相关的基本运算。

  正如我们之前提到的,树的最佳表示是父子表示。但是对于一个固定度的树,比如二叉树,你可以用最简单的方式直接定义两个指针指向它的左右叶节点。

  typedef int BTDataType

  typedef struct BTreeNode

  {

  BTDataType数据;

  struct BTreeNode * left

  struct BTreeNode * right

  } BTNode这里要说明一点,普通树的“增、删、检、改”操作是没有意义的,因为树并不是一个最优的存储结构。所以当我们研究链式二叉树的运算时,我们更多地了解了分治递归。

  2.分治递归思想什么是分治思想?

  比如学校需要筛选,找出学校最高的人。这时候校长可以去找各年级的班长,然后班长去找班主任,班主任让班长统计小组成员的身高数据。

  这时候小组长已经可以把最高的值返回给班主任,然后层层上报。校长只需要找到最后上报的四个数据中最高的一个,也就是全校最高的同学。

  分而治之的思想就是分而治之,即先把一个大规模的问题分解成几个小规模的问题,然后对这些小规模的问题进行求解,将得到的解组合起来得到最终的解。上例中,较小的问题是组长统计队员身高并上报。转换成代码语言是一种价值回报。

  更详细的解释,请参考大佬们的这个博客?

  ——递归和分治策略的五种常见算法策略

  早期学习递归求斐波那契数用的是分而治之的思想?入口

  int fo2(int a)

  {

  if ((a==1) (a==2))

  返回1;

  其他

  返回(fo2(a-1))(fo2(a-2));//n-1和n-2项

  }当a=1或2时,为分而治之的结束节点,递归以return 1终止。

  3.在理解了上述分治递归思想后,我们将学习链式二叉树的三种遍历方式。

  前序遍历:根节点-左子树-右子树中序遍历:左子树-根节点-右子树逆序遍历:左子树-右子树-根节点从前序遍历开始。我们来实践一下分而治之的思想:用递归按照前序遍历的顺序打印出树中节点的值。

  假设我们现在创建一个简单的二叉树,就像这样?你能弄清楚序言遍历的打印顺序应该是什么吗?

  答案是1 2 3 4 5 6

  看到这里,你一定一脸懵:啊,怎么出来的?

  别担心,我们现在将它们的end subtree NULL添加到打印的内容中,因此前导遍历的结果是:

  1 3 Null Null Null 4 5 Null Null 6 Null Null没有悬念,就开始分析这个遍历结果是怎么出来的吧!

  如前所述,前序遍历的顺序是:根节点-左子树-右子树。

  下图可以让你更直观的看到这三种遍历方式的区别。

  其中前序遍历被转换成如下的代码语言

  //二叉树的前序遍历

  void btreeprevord(Bt node * root)

  {

  if (root==NULL)

  {//为了便于理解,也把空节点打印出来。

  printf( NULL );

  返回;

  }

  printf(%d ,root-data);

  BTreePrevOrder(根-左);

  BTreePrevOrder(root-right);

  }在这个过程中,当根节点为NULL时,就是分而治之的结束,递归停止。

  恐怕还不清楚。为了彻底说清楚,我们得画一个递归示意图来求解。

  图中一个字打错了,看到一半才发现.请忽略!

  如果你能理解上图中前序遍历的思路,那么中序遍历和后序遍历的操作就很简单了!猜猜怎么修改前导码?

  没错,就是把printf的位置改了!

  //二叉树中的顺序遍历

  void BTreeInOrder(BTNode* root)

  {

  if (root==NULL){

  printf( NULL );

  返回;

  }

  BTreeInOrder(根左);

  printf(%d ,root-data);

  BTreeInOrder(根右);

  }

  //按逆序遍历二叉树

  void BTreePostOrder(BTNode* root)

  {

  if (root==NULL){

  printf( NULL );

  返回;

  }

  BTreePostOrder(根-左);

  BTreePostOrder(根-右);

  printf(%d ,root-data);

  }最后打印出来的结果分别如下,完全对应上面的示意图!返回原理图

  3.1通过递归遍历上面的递归来计算节点数,我们打印出每个节点的值。

  我们只需要对其中一个递归代码做一个小的修改,把printf改成count,这样就可以把它从遍历改成计算一棵二叉树的节点数了。

  在这里,我选择指针变量,这样主函数就可以得到计数结果。

  //二叉树中的节点数

  void BTreeSize(BTNode* root,int* pcount)

  {

  if (root==NULL)

  返回;

  (* pcount);//指针变量,可以在主函数中调用

  BTreeSize(左根,pcount);

  BTreeSize(root- right,pcount);

  }

  可以用全局静态变量来计数,但是那种计数会在下一次调用中叠加,调用后放0很不方便。

  当然,我贴的方法也不是最优的,因为它需要额外创建一个变量count来作为参数调用,而不是直接返回节点数。

  所以有下面这个用三只眼的算子?递归地分而治之,计算节点数。

  如果根节点为空,则为结束案例;否则,左子树和右子树的节点大小为1(节点本身)。//高级方法

  int BTreeSize(BTNode* root) {

  return root==NULL?0 :

  BTreeSize(根左)

  BTreeSize(根右)1;

  }3.2用后续遍历的思想破坏树。当我们不需要使用二叉树的时候,我们需要释放它调用的内存。

  问题是,如果释放根节点,如何找到它的左右子树?

  所以我们在释放的时候要用遍历的顺序来释放,就是先破坏左右子树,再向上破坏。这样可以避免找不到子树的问题。

  //二叉树销毁

  void BTreeDestory(BTNode** root)

  {

  if (*root==NULL)

  返回;

  //通过后续遍历的思想破坏树

  BTreeDestory((* root)-left);

  BTreeDestory((* root)-right);

  免费(* root);

  * root=NULL//可以使用二级指针直接在函数中设置根节点的指针。

  }3.3按照前/中/后的顺序遍历OJ问题

  三个Leetcode OJ问题

  44.二叉树145的一阶遍历。二叉树的二阶遍历94。二叉树的中序遍历

  在这里,我只说明前序遍历这个话题,因为后两者的思路是完全一样的,只需要做一点代码上的改动。

  给定一棵树,你需要按照前面序列的顺序遍历它,将每个节点的值保存在一个数组中并返回它。

  这里的输入用例是“伪代码”,[1,null,2,3]表示1的左子树为NULL,右子树为2;2的左子树为3,右子树为空。

  既然要用数组来存储,首先要知道这个二叉树有多少个节点,这样才能方便的打开数组。

  注意:这种接口题目,数组必须用动态内存函数malloc开发。

  然后改变preorder遍历中的printf把值放到arr数组中!

  //二叉树中的节点数

  void BTreeSize(struct TreeNode * root,int* pcount)

  {

  if (root==NULL)

  返回;

  (* pcount);

  BTreeSize(左根,pcount);

  BTreeSize(root- right,pcount);

  }

  //前导遍历代码

  void btreeprevord(struct TreeNode * root,int*arr,int* i)

  {

  if (root==NULL){

  返回;

  }

  arr[(* I)]=root-val;

  //因为在递归调用中需要多次调用不同的I,所以需要获取地址

  BTreePrevOrder(root- left,arr,I);

  BTreePrevOrder(root- right,arr,I);

  }

  int * preorderstraversal(struct TreeNode * root,int* returnSize){

  int count=0;

  BTreeSize(根,计数);//计算树节点的数量

  int * arr=(int *)malloc(sizeof(int)* count);//打开数组

  int I=0;//下标

  BTreePrevOrder(root,arr,I);

  * returnSize=count

  返回arr

  }

  4.计算节点数计算二叉树中节点数的方法已在3.1中描述。下面是对节点数量的更详细的计算。

  4.1叶节点的数量众所周知。在这个二叉树中,只有3、5和6是叶节点。

  判断一个节点是不是叶节点其实很简单:只要它的左右子树都是空的,就是叶节点。

  以此作为分而治之的结束条件。只要满足这个条件,就会返回1。如果它遍历了空节点,它将返回0。在其他情况下,它将返回左子树和右子树的叶节点数之和。//二叉树的叶节点数。

  int BTreeLeafSize(BTNode* root)

  {

  if (root==NULL)

  返回0;

  if(左根==NULL右根==NULL)

  返回1;

  返回BTreeLeafSize(根左)BTreeLeafSize(根右);

  }4.2二叉树第k层的节点数。假设根节点是第一层。如果想知道第k层有多少个节点,如何设计函数?

  先这么想:3所在的层对于根来说是第三层,但对于2来说是第二层,对于3本身来说是第一层。

  那么,我们可以让k层向下到-1进行递归吗?

  //二叉树第k层的节点数

  int BTreeLevelKSize(BTNode* root,int k)

  {

  断言(k=1);//保证k=1

  if (root==NULL)

  返回0;

  如果(k==1)

  返回1;

  返回BTreeLevelKSize(左根,k - 1)

  BTreeLevelKSize(根右,k-1);

  }

  4.3二叉树的深度在前面对树的概念研究中,我们解释了树的深度(即一棵树有多少层)。

  深度和前面校长身高统计的例子差不多。我们需要找到左边和右边子树中更深的一个并返回它。结束条件是当节点为空时,return 0终止递归。

  //二叉树深度,即有多少层?

  int BTreeDepth(BTNode* root)

  {

  if (root==NULL)

  返回0;

  int left=BTreeDepth(root-left);

  int right=BTreeDepth(root-right);

  向左向右返回?左1:右1;

  }注意:这里的左1和右1是用来计算上层节点本身的。

  求二叉树的深度oj问题leetcode上有一个OJ问题就是求二叉树的最大深度。复制代码,改名字就搞定了!

  标题链接:104。二叉树的最大深度

  5.在树中找到值为X的节点。在二叉树中寻找一个值。基本思想是遍历它,判断根节点和左右子树中是否有值为X的节点。

  具体分析,写以下代码注释!

  //二叉树查找值为x的节点。

  BTNode* BTreeFind(BTNode* root,BTDataType x)

  {

  if (root==NULL)

  返回NULL//如果节点为空,则将其作为空返回。

  if (root- data==x)

  返回根目录;//节点本身是X,返回节点自己的地址

  BTNode* ret1=BTreeFind(根左,x);

  如果(ret1!=NULL)//如果在左边找到,返回左边节点的地址。

  返回ret1

  BTNode* ret2=BTreeFind(根右,x);

  如果(ret2!=NULL)//如果找到右侧,则返回右侧节点的地址

  返回ret2

  返回NULL//两者都找不到,也不是它本身。返回null

  }6.顾名思义,序列遍历就是一层一层的遍历。

  例如上面的树,序列遍历的结果如下

  1 4 3 null 5 6实现序列遍历,需要用到堆栈中的队列和之前学过的队列知识?入口

  在VS项目中,导入预写队列的头文件和源文件,然后引用!

  这里可以体现出之前typedef类型的作用:你只需要改变第一个数据类型,剩下的就可以搞定了!

  在引用头文件的时候,因为需要用到栈代码中二叉树的定义,所以需要先引用二叉树的头文件,再引用队列的头文件。

  做完这些之后,我们来谈谈序列遍历的思想

  首先插入根节点,然后在根节点不在队列中时插入其左右子树的节点。

  当队列中的所有值都为空时,序列遍历完成。

  所以我们需要排队的不是节点的值,因为那样我们就找不到节点的左右子树了。队列是节点的地址!

  //序列遍历

  void btreelevelord(Bt node * root)

  {

  队列q;

  queue init(q);

  如果(根!=NULL){//根节点不为空,开始排队。

  QueuePush( q,root);

  }

  而(!QueueEmpty( q))

  {

  Bt node * head=queue front(q);//获取队列头数据

  queue pop(q);//出列

  printf(%d ,head-data);

  如果(头-左!=空)

  {

  QueuePush( q,头向左);

  }

  如果(头-对!=空)

  {

  QueuePush( q,头右);

  }

  //序列遍历不需要递归,可以通过循环来解决问题。

  }

  printf( \ n );

  QueueDestory(q);//防止内存泄漏

  返回;

  }遍历的结果如下

  6.1判断一棵二叉树是否是完全二叉树。学会了序列遍历,就可以判断一棵二叉树是不是完全二叉树了!

  完全二叉树:第k-1层是完全二叉树,最后一层不满足,但从左到右分布。

  具体想法是

  当队列头为空时,遍历开始。如果所有队列都为空,则表示二叉树已满。如果有非空节点,则不是满二叉树。//判断二叉树是否是完全二叉树。

  bool BTreeComplete(BTNode* root)

  {

  队列q;

  queue init(q);

  如果(根!=NULL) {

  QueuePush( q,root);

  }

  而(!QueueEmpty( q))

  {

  Bt node * head=queue front(q);//获取队列头数据

  queue pop(q);//出列

  if (head==NULL){

  打破;//遇到队列中空节点,退出循环。

  }

  QueuePush( q,头向左);

  QueuePush( q,头右);

  }

  而(!QueueEmpty( q))

  {

  Bt node * head=queue front(q);//获取队列头数据

  queue pop(q);//出列

  如果(头!=空)

  {

  //printf(不是完整的二叉树\ n );

  返回false

  }

  }

  QueueDestory(q);//防止内存泄漏

  返回true

  }结论链式二叉树的内容到这里就结束了!

  如果博客中有不清楚的地方,请在评论区提出来!

  下一篇博客会讲解一些关于二叉树的OJ题和概念性选择题!~我们后会有期~

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • mysql复合索引和组合索引,mysql组合索引数据结构
  • mysql复合索引和组合索引,mysql组合索引数据结构,Mysql之组合索引方法详解
  • mysql复合索引和组合索引,mysql复合索引数据结构
  • mysql复合索引和组合索引,mysql复合索引数据结构,MySQL的复合索引总结
  • b+树 多路搜索树,数据结构中树的分类
  • b+树 多路搜索树,数据结构中树的分类,数据结构-树(三):多路搜索树B树、B+树
  • avl树的构造,avl树特性,数据结构之AVL树详解
  • 数据结构c语言哈夫曼树,c语言哈夫曼树的构造,使用C语言详解霍夫曼树数据结构
  • c语言数据结构算法编程库,数据结构 c语言中文网
  • c语言数据结构算法编程库,数据结构 c语言中文网,C语言编程数据结构基础详解小白篇
  • c++纸牌游戏,数据结构纸牌游戏c语言
  • c++纸牌游戏,数据结构纸牌游戏c语言,C语言实战之纸牌游戏
  • ,,c#解析jobject的数据结构
  • ,,javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】
  • ,,Java 数据结构与算法系列精讲之背包问题
  • 留言与评论(共有 条评论)
       
    验证码: