C++应用中递归的利与弊

递归”在C++(C++培训 )++中主要解决具有树型特征的算法或数据结构,递归的利用可以使算法或数据结构大大简化,代码简洁明了,相同一个具有该特性的课题采用递归或其他算法,所要求的预定义及相应的结果都将不一样,用了递归可能使用减少部份定义,代码实现部份大大减少,一看便知。下面是一个从数据库中取数的例子对比:

  实现中所使用的数据结构(表结构)

  序号 英文名 中文名 类型 说明

  1 Id 权限ID Int

  2 ParentId 父权限ID Int 用于指定父结点

  3 Name 权限名称 Varchar(32)

  4 IdCode 菜单项ID int 权限与菜单项关联

  由数据结构可以看出,通过ParentId,实现权限的树状结构,来描述权限的层次关系,这是一个典型的树型特征的数据结构,采用递归可以简化程序的实现过程,但通过实验证明简单的采用递归将导致性能上的不足,运行效果无法满足用户的基本操作,在实现递归算法的后面将描述本程序在实现递归中作了相应的处理。

  1、通过对树结点的记忆来实现假递归

  DWORD dwFunction = 0; //功能ID

  HTREEITEM hItemLayer[5][2];//用于保存当前正在操作的结点,用于回溯

  int nIdCollection[5][2]; //保留父层结点的ID,用于识别下一个结点的父层所属

  // 设置树根

  hItemLayer[0][0] = m_treeOperatorPermission.InsertItem(_T("权限设置"),3,3);

  m_treeOperatorPermission.SetItemData (hItemLayer[0][0] , dwFunction);

  hItemLayer[0][1] = hItemLayer[0][0];

  nIdCollection[0][0] = 0; //父层ID

  nIdCollection[0][1] = 0; //当前层ID

  int nCurParentLay = 0;

  CADORecordset collection(&m_conn); //ADO对象,用于从数据库取出记录集

  CString strSQLString("select id ,ParentId , Name , IdCode from tbl_function order by id , parentid");

  if(collection.Open (strSQLString))

  { int nCount = collection.GetRecordCount ();

  CString strFunctionName;

  for(int i = 0;i<ncount;i p="" ++)<="">

  { //从数据库中取出结点数据

  collection.GetFieldValue ("Name" , strFunctionName);

  int nId;

  int nParentId;

  collection.GetFieldValue ("Id" , nId);

  collection.GetFieldValue ("ParentId" , nParentId);

  do {

  //判断其保留的父结点是否一致,用于判断是否从当前插入子结点,还是从父结点插入子结点

  if(nParentId == nIdCollection[nCurParentLay][0])

  { //向父层插入子结点,并保留当前结点数据,用于回溯

  hItemLayer[nCurParentLay][1] = m_treeOperatorPermission.InsertItem ((LPCTSTR)strFunctionName , 0 , 1 , hItemLayer[nCurParentLay][0]);

  nIdCollection[nCurParentLay][1] = nId;

  m_treeOperatorPermission.SetHalfChecked (hItemLayer[nCurParentLay][1]);

  dwFunction = nId;

  m_treeOperatorPermission.SetItemData (hItemLayer[nCurParentLay][1] , dwFunction);

  } elseif(nParentId == nIdCollection[nCurParentLay][1])

  { //在当前层建立子层

  hItemLayer[nCurParentLay + 1][1] = m_treeOperatorPermission.InsertItem ((LPCTSTR)strFunctionName , 0 , 1 , hItemLayer[nCurParentLay][1]);

  hItemLayer[nCurParentLay + 1][0] = hItemLayer[nCurParentLay][1];

  nIdCollection[nCurParentLay + 1][0] = nParentId;

  nIdCollection[nCurParentLay + 1][1] = nId;

  m_treeOperatorPermission.SetChecked (hItemLayer[nCurParentLay + 1][1] , FALSE);

  dwFunction = nId;

  m_treeOperatorPermission.SetItemData (hItemLayer[nCurParentLay + 1][1] , dwFunction);

  nCurParentLay ++;

  } else

  { //回溯,用于找到相匹配的父结点,便于插入结点

  nCurParentLay --;

  continue;

  } break;

  }while(true);

  collection.MoveNext ();

  } m_treeOperatorPermission.Expand (hItemLayer[0][0] , TVE_EXPAND);

  } collection.Close ();

  m_treeOperatorPermission.ClearALLCheck ();

  return 0;

  点评:这种方法是通过状态的方法来实现递归的变相方法,可以看出在代码实现方面相当复杂,程序员必须详细注明其实现过程,才能够使其他程序员读懂(当然注释本来就是应该的,这里所说的是如何让其他程序更容易看懂代码)。

  本程序中采用保留从父结点到当前结点的路径,用于回溯找到下一个结点的父结点,程序员是费尽心机,在他走过的足上做个标签,便于他回去是可以认得路,也便于摸索下一条路时不会重复走同样的一条分支(形成死循环)。

  优点:该程序只用到一条检索语句即实现权限树的初始化,减少数据库连接数,从而在性能上将会是最优,即实现最其本的数据操作。

  缺点:在点评中已经说到,代码的复杂性,给代码隐患的存在带来了很大的可能性,另外对数据也有一定的要求,必须符合一不的顺序才能够被正确执行。

  2、递归算法的应用

  longInitDefaultPermissionTree(int nParentId ,HTREEITEM hItem)

  {

  CString strSQLString;

  strSQLString.Format ("select id , name from tbl_function where parentid = %d" , nParentId);

  CADORecordset collection(&m_conn);

  if(collection.Open (strSQLString))

  {

  //将所有数据取出

  CArray nIdArray;

  CArray strNameArray;

  int nCount = collection.GetRecordCount ();

  for(int i = 0;i < nCount ;i ++)

  { int nId;

  CString strName;

  collection.GetFieldValue ("id" , nId);

  collection.GetFieldValue ("name" , strName);

  collection.MoveNext ();

  nIdArray.Add (nId);

  strNameArray.Add (strName);

  } collection.Close ();

  //将从数据库中取出的数据插入到树图上

  for(i = 0;i < nCount;i ++)

  { int nId = nIdArray.GetAt (i);

  HTREEITEM hSonItem = m_treeOperatorPermission.InsertItem (strNameArray.GetAt (i) , 0 , 0 , hItem);

  m_treeOperatorPermission.SetItemData (hSonItem , nId);

  //后面讲述采用m_TreeDataMap(CMap)的目的

  m_TreeDataMap.SetAt(nId , hSonItem);

  //对当前结点进行递归插入子结点数据

  InitDefaultPermissionTree(nIdArray.GetAt (i) , hSonItem);

  } } return 0;

  }

  点评:在本程序中简单地看去,只用了一个循环即完成数据的读取与显示(本程序采用两个循环只是想减少由于递归而增加数据库连接数),显而易见,代码清晰易懂。不需要太多的注释便可明白其中的实现过程。

  在实现过程中没有象第一个例子的那样具有相当多的辅助变量来帮助记忆树的结构,这个实例由递归的特性来完成。

  优点:简洁明了,通俗易懂,最大的特点就是执行递归时对其实现的默认,这也是在编写递归程序时应该具备的基本思想认识,不然程序员绝对想不到该算法是可以用递归来实现的。

  缺点:第一例中已经说到的优点,其实也就是本例的缺点,递归所产生相应的出入栈操作及相当的其他数据(如数据库连接数等)都将对程序的性能产生负面影响,特别对于层次较多的情况则更为严重,当然对于非树型特征的不提倡采用递归的实现算法,如求1~100的累加时,虽然可以用递归算法可以实现,但它仍然可以用常规算法来实现,这里就不提倡递归算法。

  正常算法

  Int Sum(int nMax)

  { int nSum = 0;

  for(int I = 1;I <= nMax;I ++)

  { nSum += I;

  } return nSum;

  } 递归算法

  Int Sum(int nMax)

  { if(nMax > 0)

  { return Sum(nMax – 1) + nMax;

  } else

  { return 0;

  } }

  综上所述,递归算法应该用于某些采用常规算法实现较为困难、并且具有递归特征的算法才会采用递归算法,否则不建议变相应用递归算法,如后面所述的计算1~100的累加,这里就是坚决否定递归算法的应用。

  编写代码应该考虑多方面因素,包括代码的可读性、可理解性、简单性(这个特性有一定的局限性)、执行性能等因素决定。

  对于一个性能要求不高但采用递归可以提高代码的可读性与可理解性并且可以大大简化代码的实现过程,递归将是首选。

  对于执行性能要求较高时,可能要求程序员采用其他类似的算法来替代,确保性能优先,但部份情况,采用其他算法来替代递归未必能够提高算法的性能,反而递归是最佳算法(一般指需要的递归层次较少)。

  总之,使用递归有其利,也有其弊,程序员在实现过程中是否应该采用递归算法,应考虑采用递归算法是否会影响相关模块或系统的整体要求。

时间: 2024-10-17 04:06:32

C++应用中递归的利与弊的相关文章

程序员的出路,其实并不难寻找(跳槽要果断,而且利大于弊)

从我做小程序员开始,就从未间断的在论坛看到有人在问程序员的出路在哪里,其实我很能理解这些人的想法,在行业做了几年,有些感想跟大家随便聊聊. 俗话说,365行,行行出状元,此话也适用于IT行业,尤其是程序员. 当你迷茫找不到出路,又想快速成长和提高的时候,有两种方案可以供你参考: 1.努力成为你工作环境中最优秀的人(技术最好的人): 2.跳槽,去另外一家公司做比你能力要高的工作. 针对工作环境的不同,分为大环境和小环境(人多和人少): 1.小环境,寻找技术最好的人,努力不断向他接近,当你通过努力觉

什么是App加壳,以及App加壳的利与弊

非著名程序员涩郎 非著名程序员,字耿左直右,号涩郎,爱搞机,爱编程,是爬行在移动互联网中的一名码匠!个人微信号:loonggg,微博:涩郎,专注于移动互联网的开发和研究,本号致力于分享IT技术和程序猿工作心得体会.欢迎大家关注与转载. 非著名程序员 目前针对移动应用市场上安卓APP被破解.反编译.盗版丛生的现象,很多APP开发人员已经意识到保护APP的重要性.而对于移动应用APP加密保护的问题,如何对DEX文件加密尤为重要.那么接下来,我们就先介绍一下什么是App加壳和加壳的原理,利与弊等. 一

网络孤岛是利还是弊?

网络孤岛是利还是弊? 当非结构化和无计划的网络开发以及意外网络与安全架构增长,往往导致企业里网络孤岛问题凸显.网络孤岛有利于企业对核心数据的保护,提高了安全性,却阻碍了信息互访的便捷性,对权限精细化管控需求也越来越高.因此网络孤岛是利还是弊显然还要企业自己权衡. 网络孤岛的形成 "孤岛",顾名思义,在地理位置上没有相邻岛屿的小岛,而在通信网络中,则常常以"网络孤岛"来形容与主网或其他网络无连接的局域网络,在拓扑图中该部分似乎与网络的其他部分都没有连接. 企业内的网络

java 中递归的实现 以及利用递归方法实现汉诺塔

今天说下java语言中比较常见的一种方法,递归方法. 递归的定义 简单来说递归的方法就是"自己调用自己",通过递归方法往往可以将一个大问题简单化,最终压缩到一个易于处理的程度.对于编程来说,每次递归都会减少数据量: java中递归的模式 每个递归函数的开头一定是判断递归结束条件是否满足的语句(一般是if语句):函数体一定至少有一句是"自己调用自己"的.每个递归函数一定有一个控制递归可以终结的变量(通常是作为函数的参数而存在).每次自己调用自己时,此变量会变化(一般是

Javascript中递归造成的堆栈溢出及解决方案

关于堆栈的溢出问题,在Javascript日常开发中很常见,Google了下,相关问题还是比较多的.本文旨在描述如何解决此类问题. 首先看一个实例(当然你可以使用更容易的方式实现,这里我们仅探讨递归): function isEven (num) { if (num === 0) { return true; } if (num === 1) { return false; } return isEven(Math.abs(num) - 2); } //Outputs: true console

虚拟现实技术之于传统旅游业,利还是弊?

国外媒体撰文指出,虚拟现实技术正快速发展.不久之后,消费者版Oculus Rift虚拟现实头盔将会进入市场.连接计算机或者移动设备后,它就能够给用户带来3D体验,让他们有身临虚拟世界的逼真感.那么,它会显著改变传统旅游业吗? 虚拟现实技术不仅仅适用于游戏领域.借助3D绘图软件,企业可以给真实的地方创造出虚拟的版本,让任何人可以在任何地方获得身临现场的体验.通过像Oculus Rift头盔这样的科技工具,终有一天你将能够进入虚拟世界游览博物馆,探索主题公园,甚至在国家公园溜达溜达. 这种虚拟旅行功

Android-Java-synchronized同步锁机制&amp;利与弊

synchronized同步锁机制 定义锁??的方式一: package android.java.thread09; public class Test implements Runnable { @Override public void run() { /** * 定义一个锁??,这个锁是Jav内部要去判断用的,属于隐士的 看不到的 * Java内部的实现属于 ??锁机制 */ Object lock = new Object(); synchronized (lock) { /** *

电加热纸塑分离机比水洗分离设备的利与弊的分解

电加热纸塑分离机比水洗分离设备的利与弊的分解: 现在是市场上由于环保查的紧,电加热纸塑分离机的市场前景就比较看好,相比水洗分离电加热纸塑分离机他的主要优势在于:电加热纸塑分离机优势:1.电加热分离主要原理就是电分离牛皮纸塑袋子没有任何的污染环境,主要是供电比较充足,相对现在环保局势来说 优势是比较大的.2.就是电加热分离的原料再生造粒的颜色优势,相比水洗电加热直接造粒没有任何的颜色体是什么颜色就是什么颜 色,成品颗粒的成色好,颜色纯正.3.电加热分离也是有弊端的,他的不足就在于产量,相比水洗的产

图片的base64编码的利与弊?

什么是图片的base64编码? 图片的base64编码,就是将一张图片的数据进行base64编码成字符串,用这个字符串代替图片的地址.这样的话下载图片,就不用发起http请求了,而可以随着html的下载一起将图片下载到本地. 图片的base64编码的利处: 将一些小图片数据编码成base64,可以减少http请求,避免造成浏览器请求阻塞. 图片的base64编码的弊处: 使用图片的base64编码,虽然可以减少http请求,但是如果在处理过程中,将大图片数据也进行了base64编码,这样在很大的