在面试中,我们经常被问到Coding的问题,要求用伪码或者某种语言解决一个问题,由于平时我们都依赖IDE来debug找错,所以很容易写出有错误的程序,而且没有调试,导致有些错误极难被发现,下面列举一些常见错误,并以三个题目的实践作为例子告诉自己:错误很容易发生!!!请注意验证自己的程序。
- 边界条件:
- 循环变量的起始点、结束点和增减是否正确;
- 递归调用的结束条件;
- 对于输入,是否考虑可能很大或者很小的情况,比如,对某个序列进行rotate,这个次数如果是特别大,至少打过序列长度,这种情况考虑了吗;
- 空间边界,内存、字符串、数组的大小对吗,是否存在差1和越界情况
- 数据结构边界,链表的头和尾正确处理了吗,指针指向的对吗?
- 输入假设:
- 如果输入的是指针,你是否考虑指针为NULL的情况;
- 如果输入字符串或者数组长度为0,会如何?
- 如果输入是数组或者字符串,你是如何知道其长度的;
- 如果输入的是字符串,并对字符串进行某种处理,你是否处理了可能的特殊字符,比如空格等;是否考虑了大小写问题?
- 如果设计到字符串到数字的转换,你是否考虑了负数、是否考虑了浮点数;
- 内存分配和释放
- 计算需要的内存大小时,是否考虑了若是字符串,需要多分配一个字符大小;是否考虑了重叠内存?
- 分配的大小是否合适;
- 分配的内存由谁释放,是否已经释放;
- 指针
- 指针的移动是否正确,到底应该移动多大?
引用的文章中列举很多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); }
注释:
- for/while等循环的开始和结束条件;
- 递归的结束条件,递归参数都需要考虑作为结束条件
问题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);
}
#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,-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;
}
{
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;
}
注释:
- 没有考虑数字是负数以及是浮点数的情况;
- 还有一些被检查出来的错误:有个地方忘了i++,有个++i的地方错用微i++;
引用: