分享
 
 
 

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

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

“递归”在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 ++)

{

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

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);

}

else if(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、递归算法的应用

long InitDefaultPermissionTree(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 <int , int > nIdArray;

CArray <CString , CString> 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<int , int & ,HTREEITEM, HTREEITEM&>)的目的

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的累加,这里就是坚决否定递归算法的应用。

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

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

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

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有