递归再一次让哥震惊了

原创 2011年12月22日 11:38:28
 

递归再一次让哥震惊了

先说那两个让哥震惊的递归问题:

1:用递归实现单链表的倒序输出

2:从二插查找树中删除节点,并保证还是二插查找树

 

同学们可以开始思考这两个问题了,当然你可能N年前就遇到过这两个问题,那么不妨看看,看你是否真的理解了递归。实现这两个问题的代码当然很简单,就在下面。

 

百度百科中递归的名片:递归做为一种算法在程序设计语言中广泛应用.是指函数/过程/子程序在运行过程中直接或间接调用自身而产生的重入现象.递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写程序能使程序变得简洁和清晰。

 

刚开始学习的递归的时候,觉得他好强大,实现某些功能不用递归可能要几十行代码,用递归可能几行就搞定了,而且代码清晰简洁。一直以为递归也就是自己调用自己,有一个出口条件,让他停止递归,退出函数,其实的特点并非就这些。

 

递归还有一个非常重要的特点:先进后出,跟栈类似,先递进去的后递出来。由于递归一直在自己调用自己,有时候我们很难清楚的看出,他的返回值到底是哪个,只要你理解了先进后出这个特点,你就会明白,第一次调用时,作为返回值的那个变量的值就是递归函数的返回值。先进后出吗,他是第一个进来,也就是最后出去的那个,当然就是递归的返回值啦。

 

第一个让哥震惊的问题:用递归实现单链表的倒序输出。

我前段时间写过一篇博客《四种方式实现--从尾到头输出链表》,其中一种方法就是用递归实现的,当时看见那位高人用递归实现了这个功能,哥被震住了,他怎么可以这么聪明,他的博客真的是学算法的好地方:http://zhedahht.blog.163.com/blog/#m=0。代码如下,这是我那篇博客的源码: 

       //用递归实现
        //很诚实的说盗用了别人的思想,真的太妙了,完全能看出你是否真的体会了递归的原理
        //正如那位哥们所说,递归就是一个进栈出栈的过程,链表前面的元素先进栈,在栈底,后面的元素后进栈,在栈顶,先出栈,哈哈。。。
        void recursion(node* head)
        {
            if(NULL==head)
            {
                return;
            }

            if(head->next!=NULL)
            {
                recursion(head->next);
            }

            //如果把这句放在第二个if前面,那就是从头到尾输出链表,曾经的你或许是用while或者用for循环输出链表,现在你又多了一种方式
            cout<<head->data<<"\t";
        }


 

最近在博客园中看的一些博客,发现有几篇文章跟树联系得比较紧,前天晚上,我于是把数据结构与算法中树的那一章温习了一下,哥被二插查找树删除节点的算法给震住了,因为我以前也写过一篇关于二插查找树的博客《算法学习--二叉查找树》,在这篇博客中,删除节点的那个算法写得很长,以至于叫我自己现在去看都不是很理解,今天会让大家看到看到简洁清晰的代码,递归写的吗,哈哈哈!

先来C++版的吧,好久没写了,都生疏了:

   

#include "string.h"
#include <iostream>
using namespace std;

typedef struct TreeNode1
{
	 public:
		int element;
		TreeNode1 *left;
		TreeNode1 *right;
	
		TreeNode1(int element):element(element),left(NULL),right(NULL){}
} TreeNode;

class AdtTree
{
	 
	public :
		TreeNode *root;//根节点
		AdtTree()
		{ 
			root=NULL;
		}
		 
		//查找指定节点下的最小节点
		TreeNode* FindMin(TreeNode *t)
		{
			if(t==NULL)
			{
				return NULL;
			}else if(t->left==NULL)
			{
				return t;
			}else 
			{
				return FindMin(t->left);
			}
		}

		//查找最小节点
		TreeNode* FindMin()
		{
			return FindMin(root);
		}
 
		//查找指定节点下的节点
		TreeNode* Find(int element,TreeNode *t)
		{
			if(t==NULL)
			{
				return NULL;
			}
			if(element<t->element)
			{
				return Find(element,t->left);
			}else if(element>t->element)
			{
				return Find(element,t->right);
			}else
			{
				return t;
			}
		}

		//查找节点
		TreeNode* Find(int element)
		{
			return Find(element,root);
		}

		//在指定节点下天骄节点
		TreeNode* Add(int element,TreeNode *t)
		{
			if(t==NULL)
			{
				return NULL;
			}

			if(element<t->element)
			{
				if(t->left==NULL)
				{
					return t->left=new TreeNode(element);
				}
				return Add(element,t->left);
			}else if(element>t->element)
			{
				if(t->right==NULL)
				{
					return t->right=new TreeNode(element);
				}
				return Add(element,t->right);
			}

			return t;
		}

		//天骄节点
		TreeNode* Add(int element)
		{
			if(root==NULL)
			{
				return root=new TreeNode(element);
			}else{
				return Add(element,root);
			}
		}

		//删除指定节点下节点
		TreeNode* Delete(int element,TreeNode *t)
		{
			if(t==NULL)
			{
				return NULL;
			}else if(element<t->element)
			{
				t->left= Delete(element,t->left);
			}else if(element>t->element)
			{
				t->right= Delete(element,t->right);
			}else
			{
				if(t->left!=NULL && t->right!=NULL)
				{
					TreeNode* tmpNode=FindMin(t->right);
					t->element=tmpNode->element;
					t->right=Delete(t->element,t->right);
				}else
				{
					TreeNode* tmpNode=t;
					if(t->left==NULL)
					{
						t=t->right;
					}else if(t->right==NULL)
					{
						t=t->left;
					}
					delete tmpNode;
				}
			}
			return t;
		}

		//删除节点
		TreeNode* Delete(int element)
		{
			return Delete(element,root);
		}
 
}; 


 

在来C#版:

namespace Utils
{
    public class TreeNode 
    {
        public int Element
        {
            get;
            set;
        }

        public TreeNode Left
        {
            get;
            set;
        }

        public TreeNode Right
        {
            set;
            get;
        }

        public TreeNode(int element)
        {
            this.Element = element;
        }
    }

    /// <summary>
    /// 二插查找树
    /// </summary>
    public class AdtTree
    {
        public AdtTree() { }
        public AdtTree(TreeNode node)
        {
            this.root = node;
        }
        //根节点
        private TreeNode root;

        //添加节点(没有检查根节点是否为空,所以设为private)
        private void AddNode(int element, TreeNode node)
        {
            if (node == null)
            {
                return;
            }
            if (element < node.Element)
            {
                if (node.Left == null)
                {
                    node.Left = new TreeNode(element);
                }
                else
                {
                    AddNode(element, node.Left);
                }
            }
            else if (element > node.Element)
            {
                if (node.Right == null)
                {
                    node.Right = new TreeNode(element);
                }
                else
                {
                    AddNode(element, node.Right);
                }
            }
        }

        //添加节点
        public void Add(int element, TreeNode node)
        {
            if (this.root == null)
            {
                this.root = new TreeNode(element);
            }
            else
            {
                AddNode(element, node);
            }
        }

        public void Add(int element)
        {
            Add(element, this.root);
        }

        //查找指定节点下的最小节点
        public TreeNode FindMin(TreeNode node)
        {
            if (node == null)
            {
                return null;
            }
            if (node.Left == null)
            {
                return node;
            }
            else
            {
                return FindMin(node.Left);
            }
        }

        //查找最小节点
        public TreeNode FindMin()
        {
            return FindMin(this.root);
        }

        //删除指定节点下的节点
        public TreeNode Delete(int element, TreeNode node)
        {
            if (node == null)
            {
                return null;
            }
            if (element < node.Element)
            {
                node.Left = Delete(element, node.Left);
            }
            else if (element > node.Element)
            {
                node.Right = Delete(element, node.Right);
            }
            else
            {
                if (node.Left != null && node.Right != null)
                {
                    TreeNode tmpNode = FindMin(node.Right);//查找当前节点有子树的最小节点
                    node.Element = tmpNode.Element;//将右子树的最小节点取代当前要删除的节点
                    node.Right = Delete(node.Element, node.Right);//这里是亮点,删除当前节点右子树的最小节点
                }
                else
                {
                    if (node.Left == null)
                    {
                        node = node.Right;
                    }
                    else if (node.Right == null)
                    {
                        node = node.Left;
                    }
                    else {
                        node = null;
                    }
                }
            }

            return node;
        }

        //删除节点
        public TreeNode Delete(int element)
        {
            //如果只有一个节点,即根节点,将根节点制空
            if (root != null && root.Element == element && root.Left == null && root.Right == null)
            {
                 root = null;
                 return new TreeNode(element);
            }
            return Delete(element,this.root);
        }

    }
}

 这里的重点是怎么处理,被删除的那个节点有左右两棵子树,其他的都很好处理,处理方式是:

1:找到要删除节点的右子树的最小节点,用FindMin这个方法就可以搞定;

2:将右子树的最小节点取代当前删除的节点,因为右子树的最小节点比当前节点的左子树中的所有节点都大,比右子树的节点都小,它就是符合条件的那个节点来代替当前要删除的节点

3:由于右子树的最小节点取代了当前节点,所以要在右子树中删除这个最小节点,现在又转换成同一个问题,在一棵二插查找树中删除一个节点,于是就递归咯。

我当时就是没有想到这里还可以用递归,写了一堆自己现在都不是很懂的代码。

第一个问题让我震惊是以前没有理解递归的先进后出的思想,第二个是因为我没有掌握问题转换的思想,看似两个不同的问题,其实是同一个问题,当然解法也是一样的,既然把两个解法一样的问题放在一起,用递归就再好不过了,他同时把你们搞定

 

 作者:陈太汉

 博客:http://www.cnblogs.com/hlxs/


 

回到家,让我震惊了

http://www.woaidiannao.com/club/display.asp?id=93407 http://www.woaidiannao.com/club/display.asp?id...
  • DSFDSUI
  • DSFDSUI
  • 2014年07月16日 17:12
  • 12

1138 - 震惊,99%+的中国人都会算错的问题(容斥变型)

DESCRIPTION 众所周知zhu是一个大厨,zhu一直有自己独特的咸鱼制作技巧. tang是一个咸鱼供应商,他告诉zhu在他那里面有N条咸鱼(标号从1到N)可以被用来制作.每条咸鱼都有一个咸鱼值...
  • Littlewhite520
  • Littlewhite520
  • 2017年07月04日 13:42
  • 219

江哥案之后,江妈妈应该怎样生活

最近引爆网络的江歌案,本月要在日本开庭。 来自山东青岛的24岁中国留学生蒋歌于2016年11月3日在东京的一间公寓遇害。在2016年11月3日凌晨(当地时间),为保护毒闺蜜刘鑫,被其男友陈世峰连...
  • Donutsapps
  • Donutsapps
  • 2017年12月13日 10:05
  • 2411

震惊了

看到某人的“无辜被黑,我在北大青鸟的真实工作经历”。。。真正震惊一把...
  • cos_sin_tan
  • cos_sin_tan
  • 2012年11月07日 19:33
  • 462

玲珑 1138 - 震惊,99%+的中国人都会算错的问题(容斥原理)@

1138 - 震惊,99%+的中国人都会算错的问题 Time Limit:4s Memory Limit:128MByte Submissions:305Solved:85 ...
  • yjf3151731373
  • yjf3151731373
  • 2017年07月08日 18:09
  • 111

震惊!手机浏览器里面竟然藏了这么些秘密

介绍了移动端浏览器的分类,主要的几种浏览器引擎,浏览器主要功能,浏览器的主要组成,渲染引擎的执行流程等...
  • u011026779
  • u011026779
  • 2017年03月18日 21:25
  • 282

洛谷OJ上的A+B花(zhuang)式(bi)解法

转眼间快到了8月,一想自己都毕业好久了,很怀念曾经在各大OJ上刷题的时光,今天无意在一个算法群里看到最近有个叫洛谷的oj网站貌似蛮火的,于是注册了一个下进去看一看,顺手打开了A+B problem,然...
  • sm9sun
  • sm9sun
  • 2017年07月28日 16:26
  • 724

水壶再一次爆了!!!

水壶使用寿命2个月左右,第四个哦,,,,,第一个被盗,,,其他四个爆了。。是水壶的质量问题,还是其他,,,,我的不知道。。。。。。...
  • sun5208
  • sun5208
  • 2010年03月28日 18:18
  • 293

iOS面试一般性问题

原文出自:标哥的技术博客 前言面试题中有一些一般性的问题,通常是会问到的。面试iOS应聘者时,切入点很重要,不同的切入点会导致不同的结果,没有找到合适的切入点也无法对应聘者有一个全面的了解。所以下面的...
  • woaifen3344
  • woaifen3344
  • 2016年01月11日 18:23
  • 4975

《网络编程》非阻塞 I/O

非阻塞式的 I/O 是进程调用 I/O 操作时,若数据未准备就绪,则立即返回一个 EWOULDBLOCK 错误,在数据准备就绪之前,应用进程采用轮询的方式检查数据是否准备就绪。直到数据准备就绪,则内核...
  • chenhanzhun
  • chenhanzhun
  • 2014年12月17日 08:28
  • 2444
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:递归再一次让哥震惊了
举报原因:
原因补充:

(最多只允许输入30个字)