正确代码:
#include <stdio.h>
#include <math.h>
#include <string.h>
int n;//表示位数
int a[10];
int hash_tabel[10];
void print()
{
for(int i=n;i>0;i--)
printf("%d",a[i]);
printf("\n");
}
void core(int d)
{
if(d==0)//排序完成
{
print();
return ;
}
for(int i=1;i<=n;i++)
{
if(!hash_tabel[i])
{
a[d]=i;
hash_tabel[i]=1;
core(d-1);
hash_tabel[i]=0;
}
}
}
int main()
{
int num;
scanf("%d",&n);
core(n);
return 0;
}
题目来源:
河北大学数据结构与算法第四版教科书P21 题目2。
代码分析:
没有用书上的方法,交换啥的,没想明白,这个代码的基本方法是置入。
基本思路是递归,从1到n依次尝试填入目标位置,在解决当前位置后继续解决下一位置,直到最后一个位置也填好数据,即解决了一种情况,注意在子问题解决后要将 hash_tabel[i]置零还原。
错误代码:
#include <stdio.h>
#include <math.h>
#include <string.h>
int n;//表示位数
int a[10];
int hash_tabel[10];
void init_hash()
{
memset(hash_tabel,0,sizeof(hash_tabel) );
}
void print()
{
for(int i=n;i>0;i--)
printf("%d",a[i]);
printf("\n");
}
void core(int d)
{
if(d==0)//排序完成
{
print();
init_hash();//错误点1
return ;
}
for(int i=1;i<=n;i++)
{
if(!hash_tabel[i])
{
a[d]=i;
hash_tabel[i]=1;
core(d-1);
//错误点2
}
}
}
int main()
{
int num;
scanf("%d",&n);
core(n);
return 0;
}
错误代码分析:
这个是我一开始写出来的代码,主要问题是在解决子问题后没有还原散列,先上错误运行结果:
可以看出一个数值多次出现,一开始我还以为是散列的问题,没有正确初始化,修改了之后还是有问题,后来才想到是子问题解决后没有正确进入下一状态,看如下运行模拟:
输入值 3
进入core函数 当前位置 3 状态1
i=1 a[3]=1 hash[1]=1 递归调用core
进入core函数 当前位置 2 状态2
i=1 无法置入(hash[1]=1)
i=2 a[2]=2 hash[2]=1 递归调用core
进入core函数 当前位置 1 状态3
i=1 无法置入
i=2 无法置入
i=3 a[1]=3 hash[3]=1 递归调用core
进入core函数 当前位置 0 状态4
重置hash为0 退出core 打印 1 2 3
此时回退到 状态3
i=4 退出core
回退到 状态2
i=3 a[2]=3 hash[3]=1 递归调用core
进入core函数 当前位置 1 状态5
i=1 a[1]=1 hash[1]=1 递归调用core
进入core函数 当前位置 0 状态6
重置hash为0 退出core 打印 1 3 1
可以看出问题出在hash的处理上,在最后输出时初始化hash导致在某一状态的子问题解决后处理下一状态时hash是完全错的,正确处理方式应该是当回退到某一状态后同时将hash状态也回退,即正确代码第29行。
另memset用法简记:
头文件:
#include <string.h>
用途:
将数组存储位置以字节为单位进行赋值
函数原型:
void*memset(void* dest ,int c ,size_t count );
参数:
Parameters
dest
Pointer to destination
c
Character to set
count
Number of characters
用法:
我比较常用的是将int数组内容置0,也可以直接置-1,下面举例:
int hash_tabel[10];
memset(hash_tabel,0,sizeof(hash_tabel) ); //写法1
memset(hash_tabel,0,sizeof(hash_tabel[0])*10 ); //写法2