最近離職找工作,遇到許多面試題會考算法,決定先不找工作,看看面試題,前天面試一家公司做的筆試題特別愛考遞歸題目。許久沒碰過這些東西了都忘了,刷一刷。
一、超級樓梯
有一個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;//主函數結束
}
啊,還有一些不寫了吧,筆試題怎麼刷得完呢,只是做一下找個感覺,筆試題有要求遞歸啥的還能寫個“解”哈哈。