递归面试题

最近离职找工作,遇到许多面试题会考算法,决定先不找工作,看看面试题,前天面试一家公司做的笔试题特别爱考递归题目。许久没碰过这些东西了都忘了,刷一刷。

一、超级楼梯
有一个m阶的楼梯,上楼每次只能跨1步或者2步,问:爬上楼梯需要几步?
我的做法就是用递归遍历所有可能,像一个二叉树一样,往左走代表1步,往右走代表2步,这样走完后,每一个树的路代表一种走法。贴代码:

int fun( int n )  //n代表楼梯阶数
{
   if ( n == 0 ) {
       return 1;
   }
   else if ( n < 0 ) {
       return 0;
   }
   return fun( n - 1 ) + fun( n - 2 );
}   

二、分解数为质因数
例如12 = 2 * 2 * 3,题目的思路是:一个数若能被质数分解,可以先从最小的质数开始分解,即2,若不能,则试3,这样当试到数字的一半了还没有质数可以分解,那它本身就是质数了(因为2是最小的质数了)。贴一段网上的代码:

void Prim(int m)  
{  
    int i=0 ;  
    for(i=2;i<(m/2+1);i++)  
    {  
        if(m%i==0)  
        {  
            break ;  
        }  
    }  
    if(i==(m/2+1))  
    {  
        cout<<m<<endl ;  
    }else  
    {  
        cout<<i<<'*' ;  
        Prim(m/i) ;  
    }  
} 

三、翻转链表
假设链表节点结构:

typedef struct node {
    int value;
    struct node *next;
}node;

递归:做法也采用尾递归,每次拆分原链表为头结点和头结点的下一个,并且在下一次递归函数中用头结点指向新链表的头

node *reverse_link_list( node *head, node *prehead )
{
    node *p = head->next;
    head->next = prehead;
    if ( p ) {
        return reverse_link_list( p, head );
    }
    else {
        return head;
    }
}

我写的非递归:新建一个头结点指向链表头结点,用一个节点指针来指向原链表头结点的下一个,这样每次循环取出指针指向的节点插入到新建头结点的next,即每次取结点用头插法头插。

node *myreverse_link_list( node *head )
{
    node newhead;
    newhead.next = head;

    node *p = head->next;

    while ( p != NULL ) {
        head->next = p->next;//取出head->next并使链表继续成链
        p->next = newhead.next;//使p先指向新头结点下一个
        newhead.next = p;//新头结点下一个指向取出的节点
        p = head->next;//继续取下一个
    }
    head = newhead.next;//倒序了,要返回新头结点,newhead自动释放
    return head;
}

四、打印所有名字
假设名字由a-z A-Z 0-9这62个字符随机组成,且名字长度为5-10,打印所有可能。

我的思路是先定义62个字符的字符数组,假设打印5长度名字,第一个字符从字符数组中0开始选,第二个也从字符数组0开始选…….这样就是遍历打印所有选择。代码:

 const char a[62] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
//测试用的字符数组,62的太长了
const char a1[9] = {'a', 'b', 'c', 'A', 'B', 'C', '0', '1', '2'};
int fd;
int fun( char *list, int index, int len, int arrlen )
{
    if ( index == len ) {
        list[len] = '\0';
        printf("%s\n", list);
        list[len] = '\n';
        write( fd, list, len + 1 );
    }
    else {
        int i = 0;
        for ( i = 0; i < arrlen; i++ ) {
            list[index] = a1[i];
            fun( list, index + 1, len, arrlen);
        }
    }
    return 0;
}   
int main()
{

    fd = open( "name.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
    char list[20] = {0};

    fun( list, 0, 5, 9 );
    fun( list, 0, 6, 62 );
    fun( list, 0, 7, 62 );
    fun( list, 0, 8, 62 );
    fun( list, 0, 9, 62 );
    fun( list, 0, 10, 9 );
    close( fd );
    return 0;
}

其它面试题也写下来吧:

一、将字符串中*提前
字符串中含有*,例如ab**cd**e*12,不改变其它字符顺序变为*****abcde12

我的思路:从字符串末尾倒序遍历,碰到星号就停住,然后另起循环从当前减一的位置开始倒序遍历,碰到不是星号的就跟星号交换,外层循环继续。缺点就是当字符串只有一个星号,且星号在字符串末尾时效率很低下,要一直交换,像冒泡排序一样哈哈。

void fun1( char *str, int len )
{
    int i = len - 1;
    int j, tmp;
    int starP = len - 1;

    for ( ; i >=0; i-- ) {
        if ( str[i] == '*' ) {
            for ( j = i - 1; j >= 0; j-- ) {
                if ( str[j] != '*' ) {
                    tmp = str[i];
                    str[i] = str[j];
                    str[j] = tmp;
                    break;
                }
            }
            if ( j == -1 ) {
                break;
            }
        }
    }
    printf("%s\n", str);
}   

二、谁是罪犯
这个问题蛮好玩的,面试也遇到过几次,这次再看了一遍网上搜的代码。

题目就是有几个人,其中有罪犯和无罪的人,每个人都说一句话,但是有真有假,写代码判断出谁是罪犯,思路就是暴力破解,将每个人说的话都是真话和都是假话来做假设,几个人的话都能对上就成立了。贴个网上的代码吧:

int main()//主函数
{                   //主函数开始
    char tmp[] = { 'a', 'b', 'c', 'd', 'e' };
    //Perm( tmp, 0, 5 );
    printf("%d\n", fun( 4 ));
    int dis[6];
    char name[2][15]={"不是罪犯","是罪犯"};
    int A, B, C, D, E, F;
    for( A=0 ;A<2 ;A++)//枚举A的两种可能
    {
        for( B=0 ;B<2 ;B++)//枚举B的两种可能
        {
            for( C=0 ;C<2 ;C++)//枚举C的两种可能
            {
                for( D=0 ;D<2 ;D++)//枚举D的两种可能
                {
                    for( E=0 ;E<2 ;E++)//枚举E的两种可能
                    {
                        for( F=0 ;F<2 ;F++)//枚举F的两种可能
                        {                       //64重循环开始
                            dis[0]= A||B; //第一句话的逻辑表达
                            dis[1]= (A&&F)||(A&&E)||(E&&F);// 第二句话的逻辑表达
                            dis[2]= !(A&&D);//第三句话的逻辑表达
                            dis[3]= (B&&C)||(!B&&!C);//第四句话的逻辑表达
                            dis[4]= (C&&!D)||(!C&&D);//第五句话的逻辑表达
                            dis[5]= D||(!E);//第六句话的逻辑表达
                            if(6==dis[0]+dis[1]+dis[2]+dis[3]+dis[4]+dis[5])
                            {                   //六句话全是真话输出提示信息
                                printf("A:%s\n", name[A]);
                                printf("B:%s\n", name[B]);
                                printf("C:%s\n", name[C]);
                                printf("D:%s\n", name[D]);
                                printf("E:%s\n", name[E]);
                                printf("F:%s\n", name[F]);
                            }
                        }
                    }
                }
            }
        }
    }//循环结束
    return 0;//主函数结束
}

啊,还有一些不写了吧,笔试题怎么刷得完呢,只是做一下找个感觉,笔试题有要求递归啥的还能写个“解”哈哈。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值