前言
本人在大一上刚学程序设计时就已经初步接触了递归的概念,当时还用其完成过几道类似于求阶乘、斐波拉契数列之类的作业闯关题,但我发现时至今日,我已经在上数据结构与算法课程了,对递归依然似懂非懂,甚至多次因为忘记了设定结束条件而导致无法输出结果。现把比较典型的两道题抽出来记录一下,鞭策自己:下次一定要写递归结束条件!!!
题目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循环是找两个最小结点的过程,找到了就跳出来进行结点合并。
结语
这是我的个人学习记录,写得比较随意,如果你能刷到这篇文章还能看到这里的话,真心说声感谢!