剑指offer答案全集.docx
《剑指offer答案全集.docx》由会员分享,可在线阅读,更多相关《剑指offer答案全集.docx(118页珍藏版)》请在冰点文库上搜索。
![剑指offer答案全集.docx](https://file1.bingdoc.com/fileroot1/2023-4/29/4420aade-ad9f-4f2c-8a14-3f546c7ee0fd/4420aade-ad9f-4f2c-8a14-3f546c7ee0fd1.gif)
剑指offer答案全集
剑指offer答案全集
按牛客网上的顺序来。
2016.06.03
●二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
输入描述:
array:
待查找的二维数组
target:
查找的数字
输出描述:
查找到返回true,查找不到返回false
代码:
classSolution{
public:
boolFind(vector>array,inttarget){
introw=array.size();//行数
if(row==0)returnfalse;
intcol=array[0].size();//列数
if(col==0)returnfalse;
inti=0,j=col-1;//从右上角开始
while(i=0)
{
if(target==array[i][j])
returntrue;//找到,立即退出
elseif(target>array[i][j])i++;//向下一行
elsej--;//向前一列
}
if(i==row||j<0)returnfalse;//没找到
returntrue;//此句执行不到,只是为了编译没问题
}
}
分析:
时间复杂度O(m+n),m和n分别是行数和列数。
空间复杂度O
(1)。
思想是从右上角开始判断,每次判断可以排除一行或一列。
●替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。
例如,当字符串为WeAreHappy.则经过替换之后的字符串为We%20Are%20Happy。
代码:
//length为牛客系统规定字符串输出的最大长度,固定为一个常数
classSolution{
public:
voidreplaceSpace(char*str,intlength){
if(NULL==str)return;
char*p=str;
intsize=0;//记录str长度
intblankNum=0;//记录空格个数
while(*p!
='\0')//一直找到字符串尾部
{
if(*p=='')blankNum++;
size++;
p++;
}
if(blankNum==0)return;//没有空格,直接返回
intnewSize=size-blankNum+blankNum*3;//计算新字符串大小
//if(newSize>length)return;
char*end=str+newSize;//
*end='\0';//最后一个字符置空
end--;//往前一步
p=str+size-1;//原字符串的最后一个字符处
while(p>=str)
{
if(*p=='')
{//当前字符是空格,则替换
*end--='0';
*end--='2';
*end--='%';
}
else
{//否则,不替换
*end--=*p;
}
p--;//依次向前
}
}
}
分析:
时间复杂度O(N),N为字符串长度。
思想是先遍历一次得到字符串的长度和空格个数,计算出替换后的字符串长度,这样会在原字符串后面空出一段;然后从后往前,碰到空格就替换。
鉴于义军的话:
就在牛客网上的那个编辑器里写代码,不要在VS里面写,因为VS有自动补全功能,而面试的时候是没有自动补全的。
所以从现在起,不在VS中写代码了,可能会在VS里查看有哪些成员函数。
2016.06.04
●从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
代码:
/**
*structListNode{
*intval;
*structListNode*next;
*ListNode(intx):
*val(x),next(NULL){
*}
*};
*/
classSolution{
public:
vectorprintListFromTailToHead(structListNode*head){
vectorvec;
printTmp(head,vec);
returnvec;
}
voidprintTmp(ListNode*head,vector&vec){
if(NULL==head)return;
printTmp(head->next,vec);
vec.push_back(head->val);
}
};
分析:
递归,时间复杂度O(N),N为链表长度。
因为要返回一个vector,所以必须另外写个函数,以便把vector作为参数。
●重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
代码:
/**
*Definitionforbinarytree
*structTreeNode{
*intval;
*TreeNode*left;
*TreeNode*right;
*TreeNode(intx):
val(x),left(NULL),right(NULL){}
*};
*/
classSolution{
public:
structTreeNode*reConstructBinaryTree(vectorpre,vectorin){
if(pre.size()==0||in.size()==0)returnNULL;
intsize=pre.size();
returnconstructTmp(pre,in,0,size-1,0,size-1);
}
TreeNode*constructTmp(vector&pre,vector&in,intprei,intprej,intini,intinj){
if(prei>prej||ini>inj)returnNULL;
TreeNode*node=newTreeNode(pre.at(prei));
inti=ini;
while(in.at(i)!
=pre.at(prei))i++;
intlen=i-ini;
node->left=constructTmp(pre,in,prei+1,prei+len,ini,i-1);
node->right=constructTmp(pre,in,prei+len+1,prej,i+1,inj);
returnnode;
}
};
分析:
时间复杂度O(nlogn),n为节点个数。
递归的思想。
2016.06.05
●用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。
队列中的元素为int类型。
代码:
classSolution
{
public:
voidpush(intnode){
stack1.push(node);
}
intpop(){
while(!
stack1.empty()){
stack2.push(stack1.top());
stack1.pop();
}
intret=stack2.top();
stack2.pop();
while(!
stack2.empty()){
stack1.push(stack2.top());
stack2.pop();
}
returnret;
}
private:
stackstack1;
stackstack2;
};
分析:
push时间复杂度为O
(1),pop时间复杂度为O(n)。
需要注意的是栈的操作push、pop(并不返回元素)、top、empty。
●旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:
给出的所有元素都大于0,若数组大小为0,请返回0。
代码:
classSolution{
public:
intminNumberInRotateArray(vectorrotateArray){
intsize=rotateArray.size();
if(size==0)return0;
intlow=0,high=size-1,mid;
while(lowif(rotateArray[low]else{
mid=(low+high)/2;
if(rotateArray[mid]>rotateArray[low])low=mid;
elseif(rotateArray[mid]low=mid;
break;
}elsehigh=mid;
}
}
returnrotateArray[low];
}
};
分析:
线性扫描就不说了,此题的本意是利用二分查找,时间复杂度为O(logn)。
思想主要是根据arr[low]、arr[mid]、arr[high]的大小关系,判断最小元素在前半段还是后半段。
需要注意的是红色代码处,这种情况发生在:
5,6,1,2,3,4
此代码运行时间为20ms,感觉还是有点慢哎!
前面几题的运行时间都是0的。
下面用线性扫描试试,看看运行时间为多少:
classSolution{
public:
intminNumberInRotateArray(vectorrotateArray){
intsize=rotateArray.size();
if(size==0)return0;
intmin=rotateArray[0];
for(inti=1;iif(rotateArray[i]returnmin;
}
};
结果时间也是20ms,呃呃呃。
。
。
不知什么情况了!
O(logn)和O(n)的运行时间是一样的。
●斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
代码:
classSolution{
public:
intFibonacci(intn){
if(n==0)return0;
inttmp=1;
intret=0;
for(inti=1;i<=n;i++){
ret+=tmp;
tmp=ret-tmp;
}
returnret;
}
};
分析:
1)拒绝用递归,因为会导致大量的重复计算。
2)初始条件为f(0)=0,f
(1)=1,f
(2)=1,递推公式f(n)=f(n-1)+f(n-2),n>2
3)为了少用一个变量,采用了红色代码部分。
●跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。
求该青蛙跳上一个n级的台阶总共有多少种跳法。
代码:
classSolution{
public:
intjumpFloor(intn){
if(n<=0)return0;
inttmp=1;
intret=0;
for(inti=1;i<=n;i++){
ret+=tmp;
tmp=ret-tmp;
}
returnret;
}
};
分析:
递推(跟斐波那契数列一样)。
设共有f(n)种方法,则有:
1)如果第一次跳1级台阶,那么接下来有f(n-1)种跳法;
2)如果第一次跳2级台阶,那么接下来有f(n-2)种跳法;
第一次要么跳1级,要么跳2级,所以只用上面两种情况,故总跳法数f(n)=f(n-1)+f(n-2)
初始条件:
f
(1)=1,f
(2)=2
●变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。
求该青蛙跳上一个n级的台阶总共有多少种跳法
代码:
classSolution{
public:
intjumpFloorII(intn){
if(n<=0)return0;
inttmp=1;
intret=0;
for(inti=1;i<=n;i++){
ret+=tmp;
tmp=ret;
}
returnret;
}
};
分析:
跟上一题一样道理,递推。
递推公式:
f(n)=f(n-1)+f(n-2)+f(n-3)+…..+f
(1)
初始条件:
f(0)=1,f
(1)=1,f
(2)=2,f(3)=4…,这里加了一个f(0)=1,使得更好的服从递推。
●矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。
请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
代码:
classSolution{
public:
intrectCover(intn){
if(n<=0)return0;
inttmp=1;
intret=1;
for(inti=2;i<=n;i++){
ret+=tmp;
tmp=ret-tmp;
}
returnret;
}
};
分析:
递推公式f(n)=f(n-1)+f(n-2),f
(1)=1,f
(2)=2
●二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。
其中负数用补码表示。
代码:
classSolution{
public:
intNumberOf1(intn){
intsum=0;
while(n){
sum++;
n&=(n-1);
}
returnsum;
}
};
分析:
红色代码每执行一次,可以消去二进制中的一个1。
●数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。
求base的exponent次方。
代码:
classSolution{
public:
doublePower(doublebase,intexponent){
if((base<=0.000001&&base>=-0.000001)&&exponent<0)return0.0;
if(exponent>0)returnPowerTmp(base,exponent);
elsereturn1.0/PowerTmp(base,-exponent);
}
doublePowerTmp(doublebase,intex){
if(ex==0)return1.0;
if(ex==1)returnbase;
doubleret=Power(base,ex/2);
if(ex&0x1)returnret*ret*base;
elsereturnret*ret;
}
};
分析:
时间复杂度为O(logn),n为指数。
1)首先判断base和exponent是否合法:
0的负数次幂不合法,0的0次幂默认为1.
2)考虑exponent正负,如果小于0,返回倒数;
3)如果exponent为奇数,则power(base,ex)=power(base,ex/2)*power(base,ex/2)*base;
如果exponent为偶数,则power(base,ex)=power(base,ex/2)*power(base,ex/2);
折半比循环快。
●调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
代码:
classSolution{
public:
voidreOrderArray(vector&array){
intsize=array.size();
if(size==0)return;
intodd=0,even=0;//odd为奇数下标,even为第一个偶数下标
while(array[even]&0x1)even++;//找到第一个偶数
odd=even+1;//从第一个偶数后面开始找到第一个奇数
while(oddwhile(odd(array[odd]&0x1))odd++;//找到下一个奇数
if(odd>=size)break;//如果没有奇数了,及时退出
//将[even,odd-1]之间的偶数向后移一位
inttmp=array[odd];
for(inti=odd;i>even;i--)
array[i]=array[i-1];
array[even]=tmp;//把奇数放到前面
even++;
odd++;
}
}
};
分析:
此题要是可以改变相对位置,则有时间复杂度为O(n)、空间复杂度为O
(1)的方法。
但是要保证相对位置不变,要么时间复杂度为O(n)、空间复杂度为O(n),要么时间复杂度为O(n*n)、空间复杂度为O
(1)。
本代码的时间复杂度为O(n*n),但运行时间为0ms。
。
。
那么此题的意义何在?
●链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
代码:
/*
structListNode{
intval;
structListNode*next;
ListNode(intx):
val(x),next(NULL){
}
};*/
classSolution{
public:
ListNode*FindKthToTail(ListNode*pListHead,unsignedintk){
if(NULL==pListHead||k==0)returnNULL;
ListNode*p=pListHead;
inti=1;
while(i++p=p->next;
if(NULL==p)returnNULL;//防止链表长度小于k
}
ListNode*node=pListHead;
while(p->next){//p->next为空时,p才指向最后一个节点
node=node->next;
p=p->next;
}
returnnode;
}
};
分析:
时间复杂度O(n),n为节点个数。
思路:
先让一个指针走k-1步,然后另一个指针从头开始,两个指针同步向后走,当第一个指针走到最后一个节点时,第二个指针正好是倒数第k个节点。
注意一下几个地方:
1)链表为空;
2)k为0
3)链表长度小于k;
4)当第一个指针到达最后一个节点时,而不是指向空时,第二个指针才指向倒数第k个节点。
●反转链表
输入一个链表,反转链表后,输出链表的所有元素。
代码:
/*
structListNode{
intval;
structListNode*next;
ListNode(intx):
val(x),next(NULL){
}
};*/
classSolution{
public:
ListNode*ReverseList(ListNode*pHead){
if(NULL==pHead)returnNULL;
if(pHead->next==NULL)returnpHead;
ListNode*node=pHead->next;
ListNode*head=ReverseList(node);
node->next=pHead;
pHead->next=NULL;
returnhead;
}
};
分析:
递归实现。
精髓在于如何返回头结点(原来的尾节点):
一旦找到尾节点,就一直返回它,不让它参与任何操作。
下面是进一步简化的代码:
classSolution{
public:
ListNode*ReverseList(ListNode*pHead){
if(NULL==pHead)returnNULL;
if(pHead->next==NULL)returnpHead;
ListNode*head=ReverseList(pHead->next);
pHead->next->next=pHead;
pHead->next=NULL;
returnhead;
}
};
●合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
代码:
/*
structListNode{
intval;
structListNode*next;
ListNode(intx):
val(x),next(NULL){
}
};*/
classSolution{
public:
ListNode*Merge(ListNode*pHead1,ListNode*pHead2){
if(NULL==pHead1)returnpHead2;
if(NULL==pHead2)returnpHead1;