最近离职找工作,遇到许多面试题会考算法,决定先不找工作,看看面试题,前天面试一家公司做的笔试题特别爱考递归题目。许久没碰过这些东西了都忘了,刷一刷。
一、超级楼梯
有一个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;//主函数结束
}
啊,还有一些不写了吧,笔试题怎么刷得完呢,只是做一下找个感觉,笔试题有要求递归啥的还能写个“解”哈哈。