用递归时踩的坑——一定要给出结束条件

前言

本人在大一上刚学程序设计时就已经初步接触了递归的概念,当时还用其完成过几道类似于求阶乘、斐波拉契数列之类的作业闯关题,但我发现时至今日,我已经在上数据结构与算法课程了,对递归依然似懂非懂,甚至多次因为忘记了设定结束条件而导致无法输出结果。现把比较典型的两道题抽出来记录一下,鞭策自己:下次一定要写递归结束条件!!!

题目1:二叉树的创建及前中后序遍历输出

#include <iostream>
using namespace std;
//此处如有需要则可以自行增加自己所需要的头文件
/*-------begin--------*/
#include <stdio.h>  
#include <stdlib.h>  
/*-------end--------*/  
 
typedef char DataType;
 
//二叉树结点定义 
struct node
{
   DataType data; //存放结点数据 
   struct node *lchild, *rchild ; //左右孩子指针 
};
typedef struct  node  BiTree;
typedef struct  node  *ptree;

//函数可直接使用,功能:输出结点数据
void print(DataType d)
 {
   cout<<d<<" ";
 }

/*
函数名:createBiTree
函数功能:创建二叉树,要求输入二叉树的先根序序列(具体输入方式请看左侧说明),并创建对应二叉树,并返回二叉树的根结点指针 
参数:无 
返回值:二叉树的根结点指针 
*/ 
BiTree *createBiTree() {
//请在此处填写代码,完成创建二叉树并返回二叉树根结点指针的功能    
/*-------begin--------*/
  BiTree *p=NULL;   	
char c;
cin>>c;
if(c!='#'){
p=(ptree)malloc(sizeof(BiTree));
p->data=c;
	p->lchild=createBiTree();
	p->rchild=createBiTree();
}

    return p;
    
/*-------end--------*/    
}

/*
函数名:preOrder
函数功能:先根遍历二叉树 
参数:二叉树根结点指针 
返回值:无 
*/
void preOrder(BiTree *T) 
{
//请在此处填写代码,完成先根遍历二叉树功能    
/*-------begin--------*/
//思路:递归,每次先访问根节点,再分别访问左右节点    
if(T!=NULL){
print(T->data);
preOrder(T->lchild);
preOrder(T->rchild);	
}

/*-------end--------*/  
}
 
/*
函数名: inOrder
函数功能:中根遍历二叉树 
参数:二叉树根结点指针 
返回值:无 
*/  
void inOrder(BiTree *T) 
{
    //请在此处填写代码,完成中根遍历二叉树功能    
/*-------begin--------*/
if(T!=NULL){
	inOrder(T->lchild);
print(T->data);
inOrder(T->rchild);  
} 
    
/*-------end--------*/  
}

/*
函数名:postOrder
函数功能:后根遍历二叉树 
参数:二叉树根结点指针 
返回值:无 
*/  
 void postOrder(BiTree *T)  
    {  
      //请在此处填写代码,完成后根遍历二叉树功能    
/*-------begin--------*/
   if(T!=NULL){
   	postOrder(T->lchild);
postOrder(T->rchild);   
print(T->data);
   }

/*-------end--------*/  
    }  
 
 

主函数如下:

int main(void)  
{  
 BiTree  *T;  
     T =  createBiTree(); //调用创建二叉树功能,得到二叉树的根结点指针
 
	 
    preOrder(T);//调用先根遍历二叉树,按先根遍历顺序输出二叉树结点功能 
    cout<<endl; //换行
	inOrder(T);//调用中根遍历二叉树,按中根遍历顺序输出二叉树结点功能 
	cout<<endl; 
    postOrder(T);//调用后根遍历二叉树,按后根遍历顺序输出二叉树结点功能 
   
   return 0;
}  

注意:递归结束的条件都是:T==NULL;
附注:代码都是用c写的,只是评测平台的主函数里面用到了c++的库,但我没有对主函数的修改权限,所以我写的代码也加了几行c++规范。

题目二:哈夫曼编码及译码应用

测试输入:5 2 7 4 5 19

预期输出:(对哈夫曼树按中序遍历输出对应叶子的哈夫曼编码)

7 00
5 010
2 0110
4 0111
19 1

代码如下:

//huffman tree
#include <stdio.h>
#include <stdlib.h>
#define MAXINT 10000

char huffcode[10]={'\0'}; 

//define huffman node 
struct HtNode{
	int ww;
	int parent,llink,rlink;
};

//define huffman tree
struct HtTree{
	struct HtNode *ht;//存放所有节点的数组
	int m;//m个有效节点,即真正用于编码的,在树的结构中可理解为外部节点 
	int root;//根节点的下标 	 
};

typedef struct HtTree *PHtTree;

//create huffman tree
//使用回溯法编码 
PHtTree huffman(int m,int *w){
	PHtTree pht;
	int i,j,x1,x2,m1,m2;
	//申请内存空间并检查各环节是否分配成功
	pht=(PHtTree)malloc(sizeof(struct HtTree));
	if(pht==NULL){
		printf("create tree failed!");
		return NULL;
	}
	pht->ht=(struct HtNode*)malloc(sizeof(struct HtNode)*(2*m-1)); 
	if(pht->ht==NULL){
			printf("create node of tree failed!");
		return NULL;
	}
	//initialize array of ht
	for(i=0;i<2*m-1;i++){
	pht->ht[i].llink=-1;
	pht->ht[i].rlink=-1;	
	pht->ht[i].parent=-1;
	if(i<m){
		pht->ht[i].ww=w[i];
	}
	else pht->ht[i].ww=-1; 
	}
	
	//每循环一次构造一个内部结点
	for(i=0;i<m-1;i++){
		m1=MAXINT;m2=MAXINT;
		x1=-1;x2=-1;//m1m2是权值,x1x2是下标 
		//找出两个最小权的无父节点的节点
		for(j=0;j<m+i;j++){
			if(pht->ht[j].ww<m1&&pht->ht[j].parent==-1){
				m2=m1;x2=x1;m1=pht->ht[j].ww;x1=j;
			}//最小权 
			else if(pht->ht[j].ww<m2&&pht->ht[j].parent==-1){
				m2=pht->ht[j].ww;
				x2=j;
			}}//次最小权  
		pht->ht[x1].parent=m+i;
		pht->ht[x2].parent=m+i;	
		pht->ht[m+i].ww=m1+m2;
		pht->ht[m+i].llink=x1;
		pht->ht[m+i].rlink=x2;
		
	} 
	pht->root=2*m-2;
	return pht;	
}

//译码
//用到递归必需要注意递归结束条件!!! 
void print(PHtTree T,int num,int i){
	if(T->ht[num].llink==-1&&T->ht[num].rlink==-1){
		printf("%d ",T->ht[num].ww);
		int j;
		for(j=0;j<i;j++){
			printf("%c",huffcode[j]);
		}
		printf("\n");
		return ; 
	}
	huffcode[i]='0';
	print(T,T->ht[num].llink,i+1);
	huffcode[i]='1';
	print(T,T->ht[num].rlink,i+1);
} 


int main(){
	int m,i,arr[100];
	scanf("%d",&m);
	for(i=0;i<m;i++){
		scanf("%d",&arr[i]);
	}
	PHtTree T=huffman(m,arr);
	print(T,T->root,0);
	return 0;
} 


写在最后:
哈夫曼编码的题不完全是递归结束条件的锅,书上有哈夫曼树的构造以及编码过程(huffman()函数),不过我一开始没理解透,依葫芦画瓢的时候还抄错了(捂脸),pht->ht[x1].parent=m+i; pht->ht[x2].parent=m+i; pht->ht[m+i].ww=m1+m2; pht->ht[m+i].llink=x1; pht->ht[m+i].rlink=x2;的这几步应该是第二层递归结束后第一层for循环结束前进行的,而不是在第二层for循环中,因为第二层for循环是找两个最小结点的过程,找到了就跳出来进行结点合并。

结语

这是我的个人学习记录,写得比较随意,如果你能刷到这篇文章还能看到这里的话,真心说声感谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值