动态规划和递归试题整理

 1,给定由n个整数(可能是负整数)组成的序列a1,a2,a3,……,an,求该序列的的字段和的最大值。

方法:动态规划

int maxDP(int* a,int len)
{
    int i,max=0,b=0;
    if (a[0]>0)
    {
        b=a[0];//可以在函数内部根据len ,new出来,再释放,也可以作为全局变量。
    }
    for (i=1;i<len;i++)
    {
        b=(a[i]+b>0)?(a[i]+b):0;
        if (b>max)
        {
            max=b;
        }
    }
    return max;
}

O(N)时间复杂度,O(N)空间复杂度。

2,在一个正整数序列中求和最大的非相邻子序列(序列任两元素在原序列里都不相邻)。

方法:递归

int cal(int h1,int length,unsigned int* b)//当length>2时,h1需要从-2开始取
{
    if (length-h1==1)//递归到最后一个数时,因为是正数,选上
        return b[h1];

    if (length-h1==2)//递归到最后两个数时,选大的一个
        return (b[h1]>b[h1+1]?b[h1]:b[h1+1]);
     if (length-h1==3)//递归到最后三个数时,选择中间一个或者两边的两个。
        return (b[h1]+b[h1+2]>b[h1+1]?b[h1]+b[h1+2]:b[h1+1]);
    if (h1>=0) //
        return b[h1]+(cal(h1+2,length,b)>cal(h1+3,length,b)?cal(h1+2,length,b):cal(h1+3,length,b));
    else//h1从-2开始,所以比较计算[0,length-1]和[1,length-1]的大值。
        return (cal(h1+2,length,b)>cal(h1+3,length,b)?cal(h1+2,length,b):cal(h1+3,length,b));
}

3.顺带一道EMC的递归题目:

int func(int i ,int N);

  其中i <= N,功能输出i递增到N再递减到i的整数,每行输出一个数。比如func(1,5)就是

1

2

3

4

5

4

3

2

1

  要求
1 只能有1个语句,即一个分号
2 不能使用do while until goto for if关键字,不能使用?:和逗号运算符
3 唯一能使用的库函数为printf

int func(int i,int N)
{
    return ((i==N&&printf("%d\n",i))||(printf("%d\n",i)&&func(i+1,N)&&printf("%d\n",i)));
}


4.输入一个链表的头结点,从尾到头反过来输出每个结点的值

链表结点定义如下:

struct ListNode

{

      int       m_nKey;

      ListNode* m_pNext;

};

分析:这是一道很有意思的面试题。该题以及它的变体经常出现在各大公司的面试、笔试题中。用递归来实现。要实现反过来输出链表,我们每访问到一个结点的时候,先递归输出它后面的结点,再输出该结点自身,这样链表的输出结果就反过来了。

void PrintListReversely(ListNodepListHead)

{      if(pListHead != NULL)

      {            // Print the next node first

            if (pListHead->m_pNext != NULL)

            {

                  PrintListReversely(pListHead->m_pNext);

            }

            // Print this node

            printf("%d"pListHead->m_nKey);

      }

}

5.用递归的方法实现两个有序链表的链接。

Node * MergeRecursive(Node *head1 , Node *head2) 

    if ( head1 == NULL ) 
        return head2 ; //如果head1为空,直接返回head2.
    if ( head2 == NULL) 
        return head1 ; //如果head2为空,直接返回head1.
    Node *head = NULL ; //用来储存head1和head2中的较小节点
    if ( head1->data < head2->data ) 
   { 
       head = head1 ; 
       head->next = MergeRecursive(head1->next,head2); 
   } 
   else 
   { 
      head = head2 ; 
      head->next = MergeRecursive(head1,head2->next); 
   } 
   return head ; 
}

6.用递归方法反转单链表:

Link ReverseList(Link *root, Link p)
{
    Link t = NULL;
    if(p->next != NULL)//如果没到链表尾部,递归调用
        t = ReverseList(root, p->next);
    else//如果已经到链表尾部了,把链表原来的第一节点的next改为指向NULL,把首结点改为原来的最后一个节点,
   {
        (*root)->next = NULL;
        *root = p;
    }
    if(t != NULL)
    {
        t->next = p;    
    }
    return p;
}

7.输入一个字符串,打印出该字符串中字符的所有排列

例如输入字符串abc,则输出由字符abc所能排列出来的所有字符串abcacbbacbcacabcba

分析 :这是一道很好的考查对递归理解的编程题,也比较难理解,递归加循环,反正把我是弄晕了...

void Permutation(char* pStr, char* pBegin);
void Permutation(char* pStr)
{
    Permutation(pStr, pStr);
}
void Permutation(char* pStr, char* pBegin)
{
    if(!pStr || !pBegin)
        return;
    if(*pBegin == '\0')
    {
        printf("%s\n", pStr);
    }
    else
    {
        for(char* pCh = pBegin; *pCh != '\0'; ++ pCh)
        {
            // swap pCh and pBegin
            char temp = *pCh;
            *pCh = *pBegin;
            *pBegin = temp;
            Permutation(pStr, pBegin + 1);
            // restore pCh and pBegin
            temp = *pCh;
            *pCh = *pBegin;
            *pBegin = temp;
        }
    }
}

8.在n个数中选取m(0<m<=n)个数的所有组合,问题可分解为:
1. 首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,直到从n-(m-1)个数中选取1个数为止。
2. 从n个数中选取编号次小的一个数,继续执行1步,直到当前可选编号最大的数为m。
很明显,上述方法是一个递归的过程,也就是说用递归的方法可以很干净利索地求得所有组合。
下面是递归方法的实现:
/// 求从数组a[1..n]中任选m个元素的所有组合。
/// a[1..n]表示候选集,n为候选集大小,n>=m>0。
/// b[1..M]用来存储当前组合中的元素(这里存储的是元素下标),
/// 常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。
void combine( int a[], int n, int m,  int b[], const int M )

 for(int i=n; i>=m; i--)   // 注意这里的循环范围
 {
  b[m-1] = i - 1;
  if (m > 1)
   combine(a,i-1,m-1,b,M);//从i-1个数种找m-1个数。
  else                    // m == 1, 输出一个组合
  {   
   for(int j=M-1; j>=0; j--)
    cout << a[b[j]] << " ";
   cout << endl;
  }
 }
}
这题不好写注释,还是自己跟踪调试下,比较好理解。
9,八皇后问题,题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。

由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标i和j,是不是i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]。

int g_number = 0;

void EightQueen()

{

    const int queens = 8;

    int ColumnIndex[queens];

    for(int i = 0; i < queens; ++ i)

        ColumnIndex[i] = i;

     Permutation(ColumnIndex, queens, 0);

}

void Permutation(int ColumnIndex[], int length, int index)

{

    if(index == length)

    {

        if(Check(ColumnIndex, length))

        {

            ++ g_number;

            PrintQueen(ColumnIndex, length);

        }

    }

    else

    {

        for(int i = index; i < length; ++ i)//下面和上面的排列问题完全一样了。

        {

            int temp = ColumnIndex[i];

            ColumnIndex[i] = ColumnIndex[index];

            ColumnIndex[index] = temp;

            Permutation(ColumnIndex, length, index + 1);

            temp = ColumnIndex[index];

            ColumnIndex[index] = ColumnIndex[i];

            ColumnIndex[i] = temp;

        }

    }

}

bool Check(int ColumnIndex[], int length)

{

    for(int i = 0; i < length; ++ i)

    {

        for(int j = i + 1; j < length; ++ j)

        {

            if((i - j == ColumnIndex[i] - ColumnIndex[j])|| (j - i == ColumnIndex[i] - ColumnIndex[j]))//检查是否任两个元素都不在同一对角斜线上

            return false;

        }

    }

    return true;

}

void PrintQueen(int ColumnIndex[], int length)

{

    printf("Solution %d\n", g_number);

    for(int i = 0; i < length; ++i)

      printf("%d\t", ColumnIndex[i]);

    printf("\n");

}

10.求一个二叉树的深度:

struct Node

{

      Node* left;

      Node* right;

};

int GetDepth(Node* root)

{

    if(NULL==root)

        return 0;

   int left_depth=GetDepth(root->left);

   int right_depth=GetDepth(root->right);

   return  left_depth>right_depth?left_depth+1:right_depth+1;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值