动态规划和递归试题整理
2011-09-08 15:44
267 查看
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(ListNode* pListHead)
{ 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,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
分析 :这是一道很好的考查对递归理解的编程题,也比较难理解,递归加循环,反正把我是弄晕了...
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;
}
方法:动态规划
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(ListNode* pListHead)
{ 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,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
分析 :这是一道很好的考查对递归理解的编程题,也比较难理解,递归加循环,反正把我是弄晕了...
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;
}
相关文章推荐
- 动态规划的一些整理
- 递归、分治和动态规划的关系
- 动态规划——递归转动态规划三部曲
- 动态规划之最大连续子数组(递归备忘录写法)
- 动态规划与递归非递归
- POJ1141 Brackets Sequence (dp动态规划,递归)
- 动态规划之最长公共子串(递归的备忘录写法)
- 动态规划和递归
- 较为复杂的动态规划整理
- POJ1141 Brackets Sequence (dp动态规划,递归)
- POJ1141 Brackets Sequence (dp动态规划,递归)
- 较为复杂的动态规划整理
- [原]POJ1141 Brackets Sequence (dp动态规划,递归)
- 动态规划试题:合唱队形
- 动态规划-试题(1)-扔玻璃珠
- 斐波那契系列问题的递归和动态规划
- 从暴力递归到动态规划的转换(推荐)
- 我的第一个动态规划程序(试图用递归求斐波拉契数)
- HDU 2041 超级楼梯 简单DP动态规划 递归
- leetcode 139. Word Break 深度优先遍历DFS按照index递归搜索 + 很棒的动态规划DP做法