在面试中,我们经常被问到Coding的问题,要求用伪码或者某种语言解决一个问题,由于平时我们都依赖IDE来debug找错,所以很容易写出有错误的程序,而且没有调试,导致有些错误极难被发现,下面列举一些常见错误,并以三个题目的实践作为例子告诉自己:错误很容易发生!!!请注意验证自己的程序。

  1. 边界条件:
    1. 循环变量的起始点、结束点和增减是否正确;
    2. 递归调用的结束条件; 
    3. 对于输入,是否考虑可能很大或者很小的情况,比如,对某个序列进行rotate,这个次数如果是特别大,至少打过序列长度,这种情况考虑了吗;
    4. 空间边界,内存、字符串、数组的大小对吗,是否存在差1和越界情况
    5. 数据结构边界,链表的头和尾正确处理了吗,指针指向的对吗?
  2. 输入假设:
    1. 如果输入的是指针,你是否考虑指针为NULL的情况;
    2. 如果输入字符串或者数组长度为0,会如何?
    3. 如果输入是数组或者字符串,你是如何知道其长度的;
    4. 如果输入的是字符串,并对字符串进行某种处理,你是否处理了可能的特殊字符,比如空格等;是否考虑了大小写问题?
    5. 如果设计到字符串到数字的转换,你是否考虑了负数、是否考虑了浮点数;
  3. 内存分配和释放
    1. 计算需要的内存大小时,是否考虑了若是字符串,需要多分配一个字符大小;是否考虑了重叠内存?
    2. 分配的大小是否合适;
    3. 分配的内存由谁释放,是否已经释放;
  4. 指针
    1. 指针的移动是否正确,到底应该移动多大?

 

引用的文章中列举很多Code Review时会被发现的问题,可以作为一个参考。

问题1: 对一个数组,只能swap相邻的两个元素,给定一个数组,和可以交换的次数,求最小的数组。比如{7,6,5,4,3,2,1},如果只交换一次,那其最小值是{6,7,5,4,3,2,1},也就是假定索引小的权重更大。

#include <iostream>
#include <assert.h>

using namespace std;

void GetSmallestSwap(int *src, int len, int start,int swaplimit) // start and end are index, [0, len-1]
{
    if(swaplimit ==0 || start>=(len-1)) //递归的结束条件,一般递归参数都可能成为结束条件的一部分,否则,那个参数可能就不需要作为递归参数,否则无法处理1,2,3,4的情况
        return;
    assert(swaplimit>0);
    int j = start + swaplimit;
    if(j>=len) j = len-1;
    int min = src[start];
    int IndexofMin = start;
    for(int k = start;k<=j;k++) //今天犯得错误都在这一行,k要从start开始,而不是0开始;k要到==j结束,而不是<j结束,因为j是包含在内的
    {
        if(src[k]<min)
        {
            min = src[k];
            IndexofMin = k;
        }
    } // find the first smallest num in the array
    int swaps = 0;
    for(int k = IndexofMin;k>start;k--)
    {
        swap(src[k],src[k-1]);
        swaps++;
    }
    GetSmallestSwap(src, len, start+1, swaplimit - swaps);
}

int main(int argc, char **argv)
{
    int a[] ={7,6,5,4,3,2,1};
    GetSmallestSwap(a, 7,0,11);
}

 

注释:

  1. for/while等循环的开始和结束条件;
  2. 递归的结束条件,递归参数都需要考虑作为结束条件

 

问题2: Two sorted array. Find kth smallest element: O(logK):

 

#include <iostream>
#include <assert.h>

using namespace std;

int FindKthElement(int *src1,int len1, int start1, int end1, // both index are inclusive [start, end]
                   int *src2,int len2, int start2, int end2,
                   int k) // if found, return 0, else, return 1;
{
    int len1_0 = end1 - start1 + 1;
    int len2_0 = end2 - start2 + 1;
    if(k==0) return 1;
    if((len1_0+len2_0)<k)
        return 1;
    assert((end2>=start2)||(end1>=start1));
    if(k==1) //错误,之前没有检查k为1的情况,因为k=1时,我们在下面代码中并没有处理,或者对j的处理可以确保其可以处理k为1的情况,比如j = k/2>1?k/2:1;但这时还得处理k=0的特殊情况
    {
        if(src2[start2]>src1[start1])
            cout<<src1[start1]<<endl;
        else
            cout<<src2[start2]<<endl;
        return 0;
    }
    if(end1<start1 && end2>=start2)
    {
        cout<<src2[start2]<<endl;
        //src2[start2] is the K
    }
    else if (end2<start2 && end1>=start1)
    {
        cout<<src1[start1]<<endl;
        //src1[start1] is the K
    }

    int j = k/2;
    /*if(j<1)   //如果使用这种方式处理k=1的情况,而k=1,j也=1,是不符合j=k/2这个规律的,这样后面的处理会改变逻辑,并不是很好的特殊情况处理方法
        j = 1;
*/
    int reduced1,reduced2;
    int l, m;
    if(len1_0>=j && len2_0>=j)
    {
        l = start1+j;
        m = start2+j;
        reduced1=reduced2 = j;
    }
    else if(len1_0>=j && len2_0<j)
    {
        l = start1+j;
        m = end2+1;
        reduced1 = j;
        reduced2 = len1_0;
    }
    else if(len2_0>=j && len1_0<j)
    {
        l = end1+1;
        m = start2+j;
        reduced1=len1_0;
        reduced2 = j;
    }
    if(src1[l-1]>src2[m-1])
        return FindKthElement(src1,len1,start1,end1,src2,len2,m,end2, k-reduced2);//end1处出了错误,开始写成了l,我们每次尽量减少看k/2个数据,但有时减少的要比k/2的少
    else if(src1[l-1]<src2[m-1])
        return FindKthElement(src1,len1,l,end1,src2,len2,start2,end2, k-reduced1);
    else
    {
        //output it
        cout<<src1[k-1]<<endl;
        return 0; // the src1[k-1] or src2[m-1] is the kth element;
    }

}

int FindKthElementInArray(int *src1, int len1, int *src2,int len2, int k)
{
    return FindKthElement(src1,len1,0,len1-1,src2,len2,0,len2-1,k);
}
int main(int argc, char **argv)
{
    int a1[]={5,6};
    int a2[]={1,2,3,4,7,8};
    FindKthElementInArray(a1,2,a2,6,6);
}

注释:

  1. 递归调用的结束条件不完整。 在这个题目中,我们需要递归参数有更多的分析,否则很容易出错。
  2. 递归调用应该使用哪个值作为参数, 哪个该+1,-1,哪个该不动,都要搞清楚了;

问题2: 将一个数学表达式构造为二叉树,数字和运算符是树中的节点

 

binarytreenode * buildBSTfromSubStringInBracket(char *src,int len, int begin, int *endpos)
{
    if(src[begin] != '(') return NULL;
    stack<binarytreenode *> strstack;
    for(int i = begin+1;i<len;)
    {
        int endpos = 0;
        int digitTemp = 0;
        binarytreenode *subtree = NULL;
        if(src[i] == '(')
        {
            subtree = buildBSTfromSubStringInBracket(src, len, i, endpos)
            i = endpos;
            if(strstack.size()>0 && strstack.top() is * or / operator)
            {
                binarytreenode *t1 = strstack.pop();
                binarytreenode *t2 = strstack.pop();
                t1->left = t2;
                t1->right = subtree;
                strstack.push(t1);
            }
            else
            {
                strstack.push(subtree);
            }
        }
        else if (src[i] == ')')
        {
            binarytreenode *t1 = strstack.pop();
            while(strstack.size()>0)
            {
                if(t1 is not operator)
                {
                    binarytreenode *t2 = strstack.pop();
                    assert(t2 is a operator);
                    binarytreenode *t3 = strstack.pop();
                    assert(t2 is not a operator);
                    t2->left = t3;
                    t2->right = t1;
                    t1 = t2;
                }
            }
            *endpos = ++i;
            return t1;
        }
        else
        {
            if(src[i] is a digit)
            {
                while(src[i] is a digit)
                {
                    int dt = src[i] to didit;
                    digitTemp = 10*digitTemp +dt;
                    i++;
                }
                binarytreenode *t = new binarytreenode(digitTemp);
                if(strstack.size()>0 && strstack.top() is * or / operator)
                {
                    binarytreenode *t1 = strstack.top();
                    strstack.pop();
                    binarytreenode *t2 = strstack.top();
                    strstack.pop();
                    t1->left = t2;
                    t1->right = t;
                    strstack.push(t1);
                }
                else // is - or +;
                {
                    strstack.push(t);'
                    digitTemp = 0;
                }
            }
            else if(src[i] is a operator)
            {
                binarytreenode *t = new binarytreenode(src[i]);
                strstack.push(t);
                i++;
            }else if(src[i] is space)
            {
                i++;
            }
        }
    }
    binarytreenode *t1 = strstack.pop();
    while(strstack.size()>0)
    {
        binarytreenode *t2 = strstack.pop();
        binarytreenode *t3 = strstack.pop();
        t2->left = t3;
        t2->right = t1;
        t1 = t2;
    }
    return t1;
}

 

注释:

  1. 没有考虑数字是负数以及是浮点数的情况;
  2. 还有一些被检查出来的错误:有个地方忘了i++,有个++i的地方错用微i++;

引用:

http://blog.csdn.net/softstars/article/details/2249234