100道经典算法题(51-75)

51.和为n连续正数序列。
题目:输入一个正数n,输出所有和为n连续正数序列。

例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。
分析:这是网易的一道面试题。

52.二元树的深度。

题目:输入一棵二元树的根结点,求该树的深度。

从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

例如:输入二元树:
                                            10
                                          /     \
                                        6        14
                                      /         /   \
                                    4         12     16

输出该树的深度3。

二元树的结点定义如下:

struct SBinaryTreeNode // a node of the binary tree
{
      int               m_nValue; // value of node
      SBinaryTreeNode  *m_pLeft;  // left child of node
      SBinaryTreeNode  *m_pRight; // right child of node
};
分析:这道题本质上还是考查二元树的遍历。

 

53.字符串的排列。
题目:输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串
abc、acb、bac、bca、cab和cba。

分析:这是一道很好的考查对递归理解的编程题,
因此在过去一年中频繁出现在各大公司的面试、笔试题中。
全排列树

54.调整数组顺序使奇数位于偶数前面。

题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,
所有偶数位于数组的后半部分。要求时间复杂度为O(n)。

55.
题目:类CMyString的声明如下:
class CMyString
{
public:
      CMyString(char* pData = NULL);
      CMyString(const CMyString& str);
      ~CMyString(void);
      CMyString& operator = (const CMyString& str);

private:
      char* m_pData;
};
请实现其赋值运算符的重载函数,要求异常安全,即当对一个对象进行赋值时发生异常,对象的状态不能改变
考虑new异常。 分配内存空间时,内存空间不够时就会抛出该异常。

对该异常进行处理,利用try-catch模块函数,将内存分配语句放在try中,这样出现了异常就会立刻获得,从而转入匹配的catch块进行处理。catch的参数是异常类型,这里为std::bad_alloc。

56.最长公共字串。

题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,

则字符串一称之为字符串二的子串。

注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。

例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子串,
则输出它们的长度4,并打印任意一个子串。

分析:求最长公共子串(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,

因此一些重视算法的公司像MicroStrategy都把它当作面试题。

我们可以把该问题定义如下:

给出两个字符串S和T,S的长度为m,T的长度为n,找出S与T的最长公共子串。

假设S = “ABAB”,T=“BABA”,我们可以构造一个如下的矩阵:

    A B A B
  0 0 0 0 0
B 0 0 1 0 1
A 0 1 0 2 0
B 0 0 2 0 3
A 0 1 0 3 0

对于数组array[m][n],扩展为array[m+1][n+1],这样是为了方便初始化,减少循环中对边界的判断。

  • if( S[i] == T[j] ) array[i+1][j+1] = array[i][j] + 1
  • if( S[i] != T[j] ) array[i+1][j+1] = 0
以下给出源码:
复制代码
 1 #include <string.h>
 2 #include <stdlib.h>
 3 #include <stdio.h>
 4 
 5 const char * LongestCommonSubstring(const char * strA, const char * strB)
 6 {
 7     char * LCS = NULL;
 8     const size_t LengthA = strlen(strA);
 9     const size_t LengthB = strlen(strB);
10     size_t LCSLength = 0;
11     unsigned int PositionX = 0;
12     unsigned int PositionY = 0;
13     
14     int i, j;
15     int Matrix[LengthA + 1][LengthB + 1];;
16     
17     for(i = 0; i < LengthA ; ++i)
18     {
19         for(j = 0; j < LengthB ; ++j)
20         {
21             Matrix[i][j] = 0;
22         }
23     }    
24     
25     for(i = 0; i < LengthA; ++i)
26     {
27         for(j = 0; j < LengthB; ++j)
28         {
29             if(strA[i] == strB[j])
30             {
31                 if((i == 0)||(j == 0))
32                     Matrix[i][j] = 1;
33                 else                
34                     Matrix[i][j] = Matrix[i - 1][j - 1] + 1;
35             }
36             if(Matrix[i][j] > LCSLength)
37             {
38                 LCSLength = Matrix[i][j];
39                 PositionX = i;
40                 PositionY = j;
41             }
42         }
43     }        
44     
45     
46     LCS = (char *)malloc(LCSLength + 1);
47     int index = LCSLength - 1;
48     while(index >= 0)
49     {
50         LCS[index] = strA[PositionX];
51         --index;
52         --PositionX;
53     }
54     LCS[LCSLength] = '\0';    
55     
56     return LCS;
57 }
58 int main(int argc, char **argv) 
59 {
60     const char * strA = "abab";
61     const char * strB = "baba";
62     const char * LCS = LongestCommonSubstring(strA, strB);
63     printf("Longest Common Substring is %s\n", LCS);
64     return 0;
65 }
题目:某队列的声明如下:
template<typename T> class CQueue
{
public:
CQueue() {}
~CQueue() {} void appendTail(const T& node); // append a element to tail
void deleteHead(); // remove a element from head private:
T> m_stack1;
T> m_stack2;
}; 分析:从上面的类的声明中,我们发现在队列中有两个栈。
因此这道题实质上是要求我们用两个栈来实现一个队列。
相信大家对栈和队列的基本性质都非常了解了:栈是一种后入先出的数据容器,
因此对队列进行的插入和删除操作都是在栈顶上进行;队列是一种先入先出的数据容器,
我们总是把新元素插入到队列的尾部,而从队列的头部删除元素。

58.从尾到头输出链表。

题目:输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:
struct ListNode
{

      int       m_nKey;
      ListNode* m_pNext;
};
分析:这是一道很有意思的面试题。
该题以及它的变体经常出现在各大公司的面试、笔试题中。
解答:
方法一、先把链表反向,然后再从头到尾遍历一遍。

方法二、设一个栈,从头到尾遍历一次,把结点值压力栈中,再出栈打印。

方法三、递归。

59.不能被继承的类。
题目:用C++设计一个不能被继承的类。

分析:这是Adobe公司2007年校园招聘的最新笔试题。
这道题除了考察应聘者的C++基本功底外,还能考察反应能力,是一道很好的题目。
解答:把构造函数写成私有。

 

60.在O(1)时间内删除链表结点。

题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:

struct ListNode

{

      int        m_nKey;

      ListNode*  m_pNext;

};

函数的声明如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);

分析:这是一道广为流传的Google面试题,能有效考察我们的编程基本功,还能考察我们的反应速度,

更重要的是,还能考察我们对时间复杂度的理解。
解答:

在链表中删除一个结点,最常规的做法是从链表的头结点开始,顺序查找要删除的结点,找到之后再删除。由于需要顺序查找,时间复杂度自然就是O(n) 了。

我们之所以需要从头结点开始查找要删除的结点,是因为我们需要得到要删除的结点的前面一个结点。我们试着换一种思路。我们可以从给定的结点得到它的下一个结点。这个时候我们实际删除的是它的下一个结点,由于我们已经得到实际删除的结点的前面一个结点,因此完全是可以实现的。当然,在删除之前,我们需要需要把给定的结点的下一个结点的数据拷贝到给定的结点中。此时,时间复杂度为O(1)。

上面的思路还有一个问题:如果删除的结点位于链表的尾部,没有下一个结点,怎么办?我们仍然从链表的头结点开始,顺便遍历得到给定结点的前序结点,并完成删除操作。这个时候时间复杂度是O(n)。那题目要求我们需要在O(1)时间完成删除操作,我们的算法是不是不符合要求?实际上,假设链表总共有n个结点,我们的算法在n-1总情况下时间复杂度是O(1),只有当给定的结点处于链表末尾的时候,时间复杂度为O(n)。那么平均时间复杂度[(n-1)*O(1)+O(n)]/n,仍然为O(1)。
#include <iostream>
using namespace std;

typedef struct LNode{
     int data;
     LNode *next;
}LNode, *List;

void InsertList(List &l, int data)//头插入节点
{
     List head;
     LNode *p=new LNode;
     p->data=data;
     if(NULL==l)
          p->next=NULL;
     else
          p->next=l;
     l=p;
}

void PrintList(List l)//打印链表
{
     LNode *p=l;
     while(p)
     {
          cout<<p->data<<" ";
          p=p->next;
     }
     cout<<endl;
}

void DeleteNode(List &l, LNode *toBeDeleted)//删除链表l中的toBeDeleted节点
{
     LNode *p;
     if(!l || !toBeDeleted)
          return;

     if(l==toBeDeleted)//若删除的节点时表头
     {
          p=l->next;
          delete toBeDeleted ;
          l=p;         
     }
     else
     {
          if(toBeDeleted->next==NULL)//若删除的节点时最后一个节点
          {
               p=l;
               while(p->next!=toBeDeleted)
                    p=p->next;
               p->next=NULL;
               delete toBeDeleted;
          }
          else//删除节点时中间节点
          {
               p=toBeDeleted->next;
               toBeDeleted->data=p->data;
               toBeDeleted->next=p->next;
               delete p;

          }

     }

}

void main()
{
     List l=NULL;
     LNode *p;
     int n;
     InsertList(l, 3);
     InsertList(l, 7);
     InsertList(l, 12);
   
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenxintao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值