面试总结2

转载自:http://blog.csdn.net/v_JULY_v/article/details/6126406
第12题

题目:求1+2+…+n,
要求不能使用乘除法、for、while、if、else、switch、case等关键字
以及条件判断语句(A?B:C)。
//July、2010/10/19
-----------------
循环只是让相同的代码执行n遍而已,我们完全可以不用for和while达到这个效果。
比如定义一个类,我们new一含有n个这种类型元素的数组,

那么该类的构造函数将确定会被调用n次。我们可以将需要执行的代码放到构造函数里。

第13题:
题目:
输入一个单向链表,输出该链表中倒数第k个结点,
链表的倒数第0个结点为链表的尾指针。

//此题一出,相信,稍微有点 经验的同志,都会说到:
------------------------
设置两个指针p1,p2
首先p1和p2都指向head
然后p2向前走n步,这样p1和p2之间就间隔k个节点
然后p1和p2同……

#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>

struct ListNode
{
    char data;
    ListNode* next;
};
ListNode* head,*p,*q;
ListNode *pone,*ptwo;

ListNode* fun(ListNode *head,int k)
{
    pone = ptwo = head;
    for(int i=0;i<=k-1;i++)
        ptwo=ptwo->next;
    while(ptwo!=NULL)
    {
        pone=pone->next;
        ptwo=ptwo->next;
    }
    return pone;
}

int main()
{
    char c;
    head = (ListNode*)malloc(sizeof(ListNode));
    head->next = NULL;
    p = head;
    while(c !='0')
    {
        q = (ListNode*)malloc(sizeof(ListNode));
        q->data = c;
        q->next = NULL;
        p->next = q;
        p = p->next;
        c = getchar();
    }
    cout<<"---------------"<<endl;
    cout<<fun(head,2)->data<<endl;

    return 0;
}

第14 题:
题目:输入一个已经按升序排序过的数组和一个数字,
在数组中查找两个数,使得它们的和正好是输入的那个数字。
要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组1、2、4、7、11、15 和数字15。由于4+11=15,因此输出4 和11。
由于数组已经过升序排列,所以,难度下降了不少。
ANSWER:
Use two cursors. One at front and the other at the end. Keep track of the sum by moving the cursors.

bool FindTwoNumbersWithSum

( int data[],           // 已经排序的 数组 unsigned int length,  // 数组长度     int sum,              //用户输入的 sum  int& num1,            // 输出符合和等于sum的第一个数 int& num2             // 第二个数 ) {        bool found = false;     if(length < 1)         return found;         int begin = 0;     int end = length - 1;         while(end > begin)     {         long curSum = data[begin] + data[end];                 if(curSum == sum)         {             num1 = data[begin];             num2 = data[end];             found = true;             break;         }         else if(curSum > sum)             end--;         else             begin++;     }     return found; }


第15题:
题目:输入一颗二元查找树,将该树转换为它的镜像,
即在转换后的二元查找树中,左子树的结点都大于右子树的结点。
用递归和循环两种方法完成树的镜像转换。  
例如输入:
   8
  / /
6  10
/ / / /
5 7 9 11

输出:
    8
   / /
  10  6
/ / / /
11 9 7  5

定义二元查找树的结点为:
struct BSTreeNode // a node in the binary search tree (BST)
{
  int m_nValue; // value of node
  BSTreeNode *m_pLeft; // left child of node
  BSTreeNode *m_pRight; // right child of node
};
//就是递归翻转树,有子树则递归翻转子树。

递归解法:
void Revertsetree(list *root)
{
    if(!root)
       return;
    list *p;

    p=root->leftch;
    root->leftch=root->rightch;
    root->rightch=p;

    if(root->leftch)
      Revertsetree(root->leftch);
    if(root->rightch)
      Revertsetree(root->rightch);
}

循环解法:

由于递归的本质是编译器生成了一个函数调用的栈,
因此用循环来完成同样任务时最简单的办法就是用一个辅助栈来模拟递归。

首先我们把树的头结点放入栈中。
在循环中,只要栈不为空,弹出栈的栈顶结点,交换它的左右子树。

如果它有左子树,把它的左子树压入栈中;
如果它有右子树,把它的右子树压入栈中。

这样在下次循环中就能交换它儿子结点的左右子树了。

void Revertsetree(list *phead)
{
    if(!phead)
       return;

    stack<list*> stacklist;
    stacklist.push(phead);         //首先把树的头结点放入栈中。

    while(stacklist.size())
    //在循环中,只要栈不为空,弹出栈的栈顶结点,交换它的左右子树
    {
      list* pnode=stacklist.top();
      stacklist.pop();
  
      list *ptemp;
      ptemp=pnode->leftch;
      pnode->leftch=pnode->rightch;
      pnode->rightch=ptemp;

      if(pnode->leftch)
        stacklist.push(pnode->leftch);   //若有左子树,把它的左子树压入栈中
      if(pnode->rightch)
        stacklist.push(pnode->rightch);  //若有右子树,把它的右子树压入栈中
    }
}

第16题
题目:输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。
例如输入

      8
    /  /
   6    10
  //     //
5  7   9  11

输出8   6   10   5   7   9   11。
//题目不是我们所熟悉的,树的前序,中序,后序。即是树的层次遍历。

/*308 楼 panda_lin 的回复,说的已经很好了。:)
利用队列,每个单元对应二叉树的一个节点.
1:输出8, 队列内容: 6, 10
2:输出6,  6的2个子节点5,7入队列。队列的内容:10, 5, 7
3:输出10,10的2个子节点9,11入队列。队列的内容:5,7,9,11。
4:输出5 ,5没有子节点。队列的内容:7,9,11
5:。。。

由于STL已经为我们实现了一个很好的deque(两端都可以进出的队列),
我们只需要拿过来用就可以了。


我们知道树是图的一种特殊退化形式。
同时如果对图的深度优先遍历和广度优先遍历有比较深刻的理解,

将不难看出这种遍历方式实际上是一种广度优先遍历。
因此这道题的本质是在二元树上实现广度优先遍历。

#include <deque>
#include <iostream>
using namespace std;

struct BTreeNode // a node in the binary tree
{
      int         m_nValue; // value of node
      BTreeNode  *m_pLeft;  // left child of node
      BTreeNode  *m_pRight; // right child of node
};
BTreeNode* pListIndex;
BTreeNode* pHead;

void PrintFromTopToBottom(BTreeNode *pTreeRoot)
{
      if(!pTreeRoot)
            return;

      // get a empty queue
      deque<BTreeNode *> dequeTreeNode;

      // insert the root at the tail of queue
      dequeTreeNode.push_back(pTreeRoot);

      while(dequeTreeNode.size())
      {
            // get a node from the head of queue
            BTreeNode *pNode = dequeTreeNode.front();
            dequeTreeNode.pop_front();

            // print the node
            cout << pNode->m_nValue << ' ';

            // print its left child sub-tree if it has
            if(pNode->m_pLeft)
                  dequeTreeNode.push_back(pNode->m_pLeft);
            // print its right child sub-tree if it has
            if(pNode->m_pRight)
                  dequeTreeNode.push_back(pNode->m_pRight);
      }
}

// 创建二元查找树
void addBTreeNode(BTreeNode * & pCurrent, int value)
{
    if (NULL == pCurrent)
    {
        BTreeNode * pBTree = new BTreeNode();
        pBTree->m_pLeft = NULL;
        pBTree->m_pRight = NULL;
        pBTree->m_nValue = value;
        pCurrent = pBTree;

    }
    else 
    {
        if ((pCurrent->m_nValue) > value)
        {
            addBTreeNode(pCurrent->m_pLeft, value);
        }
        else if ((pCurrent->m_nValue) < value)
        {
            addBTreeNode(pCurrent->m_pRight, value);
        }
    }
}

int main()
{
    BTreeNode * pRoot = NULL;
    pListIndex = NULL;
    pHead = NULL;
    addBTreeNode(pRoot, 8);
    addBTreeNode(pRoot, 6);
    addBTreeNode(pRoot, 5);
    addBTreeNode(pRoot, 7);
    addBTreeNode(pRoot, 10);
    addBTreeNode(pRoot, 9);
    addBTreeNode(pRoot, 11);
    PrintFromTopToBottom(pRoot);
    return 0;
}

第17题:
题目:在一个字符串中找到第一个只出现一次的字符。
如输入abaccdeff,则输出b。  
这道题是2006年google的一道笔试题。


思路剖析:由于题目与字符出现的次数相关,我们可以统计每个字符在该字符串中出现的次数.
要达到这个目的,需要一个数据容器来存放每个字符的出现次数。

在这个数据容器中可以根据字符来查找它出现的次数,
也就是说这个容器的作用是把一个字符映射成一个数字。

在常用的数据容器中,哈希表正是这个用途。
由于本题的特殊性,我们只需要一个非常简单的哈希表就能满足要求。

由于字符(char)是一个长度为8的数据类型,因此总共有可能256 种可能。
于是我们创建一个长度为256的数组,每个字母根据其ASCII码值作为数组的下标对应数组的对应项,
而数组中存储的是每个字符对应的次数。

这样我们就创建了一个大小为256,以字符ASCII码为键值的哈希表。
我们第一遍扫描这个数组时,每碰到一个字符,在哈希表中找到对应的项并把出现的次数增加一次。
这样在进行第二次扫描时,就能直接从哈希表中得到每个字符出现的次数了。

#include <iostream.h>
#include <string.h>


char FirstNotRepeatingChar(char* pString)
{
      if(!pString)
            return 0;

      const int tableSize = 256;
      unsigned int hashTable[tableSize];
      for(unsigned int i = 0; i < tableSize; ++ i)
            hashTable[i] = 0;

      char* pHashKey = pString;
      while(*(pHashKey) != '/0')
            hashTable[*(pHashKey++)] ++;

      pHashKey = pString;
      while(*pHashKey != '/0')
      {
            if(hashTable[*pHashKey] == 1)
                  return *pHashKey;

            pHashKey++;
      }

      return *pHashKey;
}

int main()
{
    cout<<"请输入一串字符:"<<endl;
    char s[100];
    cin>>s;
    char* ps=s;
    cout<<FirstNotRepeatingChar(ps)<<endl;
    return 0;
}

第19题:
题目:定义Fibonacci数列如下:  
  /   0                 n=0
f(n)= 1                 n=1,2
  / f(n-1)+f(n-2)       n>2

输入n,用最快的方法求该数列的第n项。

非递归解法:
#include <iostream.h>

int Fibona(int n)
{
    int m;
    if(n==0)
        return 0;
    else if(n==1||n==2)
        return 1;
    else
    {
        m=Fibona(n-1)+Fibona(n-2);
        return m;
    }
}

int main()
{
    cout<<"-----------------"<<endl;
    cout<<Fibona(17)<<endl;
    return 0;
}

第20题:
题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。
例如输入字符串"345",则输出整数345。
-----------------------------
此题一点也不简单。不信,你就先不看一下的代码,
你自己先写一份,然后再对比一下,便知道了。

1.转换的思路:每扫描到一个字符,我们把在之前得到的数字乘以10再加上当前字符表示的数字。
这个思路用循环不难实现。
2.由于整数可能不仅仅之含有数字,还有可能以'+'或者'-'开头,表示整数的正负。
如果第一个字符是'+'号,则不需要做任何操作;如果第一个字符是'-'号,
则表明这个整数是个负数,在最后的时候我们要把得到的数值变成负数。
3.接着我们试着处理非法输入。由于输入的是指针,在使用指针之前,
我们要做的第一件是判断这个指针是不是为空。
如果试着去访问空指针,将不可避免地导致程序崩溃。
4.输入的字符串中可能含有不是数字的字符。
每当碰到这些非法的字符,我们就没有必要再继续转换。

最后一个需要考虑的问题是溢出问题。由于输入的数字是以字符串的形式输入,
因此有可能输入一个很大的数字转换之后会超过能够表示的最大的整数而溢出。

enum Status {kValid = 0, kInvalid};
int g_nStatus = kValid;

int StrToInt(const char* str)
{
      g_nStatus = kInvalid;
      long long num = 0;

      if(str != NULL)
      {
            const char* digit = str;

            // the first char in the string maybe '+' or '-'
            bool minus = false;
            if(*digit == '+')
                  digit ++;
            else if(*digit == '-')
            {
                  digit ++;
                  minus = true;
            }

            // the remaining chars in the string
            while(*digit != '/0')
            {
                  if(*digit >= '0' && *digit <= '9')
                  {
                        num = num * 10 + (*digit - '0');

                        // overflow 
                        if(num > std::numeric_limits<int>::max())
                        {
                              num = 0;
                              break;
                        }

                        digit ++;
                  }
                  // if the char is not a digit, invalid input
                  else
                  {
                        num = 0;
                        break;
                  }
            }

            if(*digit == '/0')
            {
                  g_nStatus = kValid;
                  if(minus)
                        num = 0 - num;
            }
      }
      return static_cast<int>(num);
}

/在C语言提供的库函数中,函数atoi能够把字符串转换整数。
//它的声明是int atoi(const char *str)。该函数就是用一个全局变量来标志输入是否合法的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值