遞歸面試題

最近離職找工作,遇到許多面試題會考算法,決定先不找工作,看看面試題,前天面試一家公司做的筆試題特別愛考遞歸題目。許久沒碰過這些東西了都忘了,刷一刷。

一、超級樓梯
有一個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;//主函數結束
}

啊,還有一些不寫了吧,筆試題怎麼刷得完呢,只是做一下找個感覺,筆試題有要求遞歸啥的還能寫個“解”哈哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章