免责声明
本文仅为个人学习笔记,请谨慎参考,如有错误欢迎批评指正。
要求
格雷码是具有如下特点的编码,当输入n为如下数时对应的格雷码:
n=1: 0, 1
n=2: 00, 01, 11,10
n=3: 000,001,011,010, 110,111,101,100
…
格雷码要求相邻两个格雷码之间只有一位不同,其他位均相同。要求:
(1)写出n=5时的格雷码,要求写出求解过程中变量的变化过程以及求解结果。
(2)写出算法分析过程,试编写程序求输入n时对应的格雷码,并分析算法的时间复杂度。
参考链接
第一个题解,用的公式法是最简单的。
第二个题解,本文代码是基于它改的。
分析
这里我选择递归的方法来理解。
首先我们还是要找出格雷码的规律:
要看完整代码拉到最后,下面展示写代码的思路。
对于递归函数的构造,主要包括结束条件、递归调用和数据处理,三者顺序不限。因为我们要先构造n=0,n=1,n=2......我们就要先递归,再对数据进行处理。所以递归函数的大致结构是这样的。
void getGray(gray_arr, n){
// 1.结束条件
if(n==0){
gray_arr[n]=0;
return 1;
}
// 2.递归调用
getGray(gray_arr, n-1);
// 3.数据处理
for(int i=0; i<gray_arr的长度; i++){
// 复制n-1的格雷码,在每个后面添加1或0
}
}
C中函数将数组gray_arr作为传入参数的话,学过指针都知道,是会永久改变gray_arr数组的。C中没有直接返回数组长度的函数,所以该函数最好返回经复制后的数组长度,不然用pow自己算。
int getGray(gray_arr, n){
// 1.结束条件
if(n==0){
gray_arr[n]=0;
return 1;
}
// 2.递归调用
int length = getGray(gray_arr, n-1);
// 3.数据处理
for(int i=0; i<length; i++){
// 复制n-1的格雷码,在每个后面添加1或0
}
return length*2;
}
大致的结构有了,开始写数据处理部分:
int getGray(int gray_arr[], int n){
// 1.结束条件
if(n==0){
gray_arr[n]=0;
return 1;
}
// 2.递归调用
// 头可能会晕,但是就记住,假如n=3那么经过这个递归我们已经获得了n=2的格雷码数组和它的长度
int length = getGray(gray_arr, n-1);
// 3.数据处理
// 在n-1格雷码数组的基础上填写n的格雷码,即复制n-1的格雷码,分别在末位添加0或1
// 注意,gray_arr数组是从1开始的,不是0,length指向的就是最后一个数
for(int i = length-1; i >= 0; i--)
{
// 这里举具体的例子G(2)和G(3)方便理解
// G(2)={00, 01, 11, 10}
// G(3)={000,001,011,010,110,111,101,100}
// 每次循环i分别指向G(2)的4个数
// 我们要从G(3)的最后一个位置开始填写,不然会把后面的数覆盖了
// 这也是为什么i要从length开始
// i=0时,末位添加0、1;
// i=1时,末位添加1、0;
// i=2时,末位添加0、1。。。
// 所以i为偶数是0、1;i为偶数是1、0
int a,b;
i%2==0?(a=0,b=1):(a=1,b=0);
// i*2和i*2-1就是一对G(2)到G(3)复制出来的数
gray_arr[i*2] = gray_arr[i]*10+a;
gray_arr[i*2+1] = gray_arr[i]*10+b;
}
return length*2;
}
完整代码如下:
#include <stdio.h>
#include <math.h>
int getGray(int gray_arr[], int n){
// 1.结束条件
if(n==0){
gray_arr[n]=0;
return 1;
}
// 2.递归调用
// 头可能会晕,但是就记住,假如n=3那么经过这个递归我们已经获得了n=2的格雷码数组和它的长度
int length = getGray(gray_arr, n-1);
// 3.数据处理
// 在n-1格雷码数组的基础上填写n的格雷码,即复制n-1的格雷码,分别在末位添加0或1
// 注意,gray_arr数组是从1开始的,不是0,length指向的就是最后一个数
for(int i = length-1; i >= 0; i--)
{
// 这里举具体的例子G(2)和G(3)方便理解
// G(2)={00, 01, 11, 10}
// G(3)={000,001,011,010,110,111,101,100}
// 每次循环i分别指向G(2)的4个数
// 我们要从G(3)的最后一个位置开始填写,不然会把后面的数覆盖了
// 这也是为什么i要从length开始
// i=0时,末位添加0、1;
// i=1时,末位添加1、0;
// i=2时,末位添加0、1。。。
// 所以i为偶数是0、1;i为偶数是1、0
int a,b;
i%2==0?(a=0,b=1):(a=1,b=0);
// i*2和i*2-1就是一对G(2)到G(3)复制出来的数
gray_arr[i*2] = gray_arr[i]*10+a;
gray_arr[i*2+1] = gray_arr[i]*10+b;
}
return length*2;
}
int main(){
int n;
scanf("%d",&n);
int num = (int)pow(2,n);
int gray_arr[num];
// 返回格雷码
getGray(gray_arr, n);
// 打印格雷码
for(int i=0;i<num;i++){
// 这样打印前面的0不会显示,时间紧迫就不搞了
printf("%d ", gray_arr[i]);
}
return 0;
}
算法的复杂度是 ,应该吧。。。