一、面试题
(1)斐波那契数列
long long Fibonacci(int n)
{
int result[2]={0,1};
if(n<=1)
return result[n];
long long fibone = 1;
long long fibtwo =0;
long long fibN = 0;
for(int i =2;i<=n;++i)
{
fibN= fibone+fibtwo;
fibtwo = fibone;
fibone = fibN;
}
return fibN;
}
int main()
{
long long value = Fibonacci(6);
cout<<value<<endl;
}
(二)连续最大子序列的和
int FindMaxSum(int * data,int length)
{
if(data == NULL || length<0)
return 0;
int cursum =0;
int greatsum = 0x80000000;
for(int i =0;i<length;++i)
{
if(cursum<0)//重点,当他小于零时,舍去这个数。
cursum = data[i];
else
{
cursum += data[i];
}
if(cursum>greatsum)
greatsum=cursum;
}
return greatsum;
}
int main()
{
int data[]={1.-2,3,10,-4,7,2,-5};
int value = FindMaxSum(data,sizeof(data)/sizeof(int));
cout<<value<<endl;
return 0;
}
三、旋转数组的最小值
旋转之后的数组其实是俩个排序的数组,因为在排序数组中我们可以用二分查找,时间复杂度是O(nlogn),
而且最小的那个数字一定是俩个排序数组的分界点。
int MinInorder(int *numbers,int left,int right)
{
int min = numbers[left];
for(int i = left+1; i<= right;++i)
{
if(numbers[i] < min)
{
min = numbers[i];
}
}
return min;
}
int Min(int* numbers,int length)
{
if(numbers ==NULL || length <0)
return 0;
int index1 =0;
int index2 = length-1;
int indexmid = index1;
while(numbers[index1] >= numbers[index2])
{
if(index2-index1 == 1)
{
indexmid = index2;
break;
}
indexmid = (index1+index2)/2;
if(numbers[index1] == numbers[indexmid] && numbers[index1] == numbers[index2])
return MinInorder(numbers,index1,index2);/////1/////////////////////
if(numbers[indexmid] >numbers[index1])
index1= indexmid;
if(numbers[indexmid] < numbers[index2])
index2 = indexmid;
}
return numbers[indexmid];
}
int main()
{
int numbers[]={1,1,1,0,1,};
int value = Min(numbers,sizeof(numbers)/sizeof(int));
cout<<value<<endl;
return 0;
}
至于为什么会有1,是因为考虑到这种情况:1 0 1 1 1或者1 1 1 0 1这俩种情况下,就不能在进行二分法查找了。
四、面试题11: 计算数值的整数次方(指数为负数,0的负数次幂是不合法的)我们一般只考虑了指数为正数的情况;
bool g_invalidinput = false;
double PowUnsigned(double base,int absexp)
{
double result = 1.0;
for(int i =1;i<= absexp;++i)
result *= base;
return result;
}
bool equal(double base,double value)
{
if(base-value> -0.000001 && base-value < 0.000001)
return true;
else
return false;
}
double Pow(double base,int exp)
{
if(equal(base,0.0) && exp <0)
{
g_invalidinput = true;
return 0.0;
}
double absexp = (unsigned int)(exp);
if(exp <0)
absexp = (unsigned int)(-exp);
double value = PowUnsigned(base,absexp);
if(exp< 0)
value = 1.0/value;
return value;
}
int main()
{
int result = Pow(2,3);
cout<<result<<endl;
return 0;
}
面试12:打印1到最大的n位数;
1、首先想到的就是先计算出最大的n位数max,然后在从1到max,但是如果数字很大,不管是int还是long long 类型都不够存储。
所以改用字符串存储,并且用字符串的没一位表示0-9之间的某个字符,用来表示数字中的一位,因为数字最大是n位,所以需要
n+1位的字符串,并且第n+1位是'\0';当数字不够n位时,数字的前半段补0;
Increment函数是将字符串的数字每一次+1;
Print函数因为在打印的时候可能会出现098,我们只需要打印98,所以对字符串的打印也需要做特殊处理。
bool Increment(char *number)
{
bool isoverflow = false;
int nTakeOver = 0;
int length = strlen(number);
for(int i = length-1;i>=0;--i)
{
int nSum = number[i]+nTakeOver-'0';
if(i == length-1)
nSum++;//每一次给字符串的数字+1;
if(nSum>=10)
{
if(i == 0)
isoverflow = true;//只有第一个字符(下标为0)的基础上产生进位的时,就已经是最大的N位数了。
else
{
nSum-=10;
nTakeOver =1;
number[i]= nSum+'0';
}
}
else
{
number[i] = nSum+'0';
break;
}
}
return isoverflow;
}
void Print(char *number)
{
bool isbegining0 = true;
int length = strlen(number);
for(int i =0;i<length;++i)
{
if(isbegining0 && number[i] !='0')
isbegining0=false;
if(!isbegining0)
printf("%c",number[i]);
}
cout<<" ";
/* for(int i =0;i<length;++i)
{
if(number[i] !='\0')
printf("%c",number[i]);
}
这是错误的,因为本意是碰到第一个非零的值开始打印,
如果写成这个样子,后面再碰到0就不会打印了,很明显就错了;
*/
}
void Print1toMax(int n)
{
if(n<=0)
return ;
char * number = new char[n+1];
memset(number,'0',n);
number[n]='\0';
while(!Increment(number))
{
Print(number);
}
}
int main()
{
Print1toMax(3);
return 0;
}
面试题:在O(1)时间内删除结点
给定单向链表的头结点和一个指定的结点,在O(1)时间内删除该节点;
分析:在单向链表中,要删除一个指定的结点,最常规的方法是在链表中顺序查找目标节点的前一个结点,
然后让前一个结点的next指针指向目标节点的下一个结点。但是这样做的时间复杂度是O(1)。
第二种方法:因为已知了要删除的结点,那么他的下一个结点也是知道的,既然这样的话就可以用下一个节点的值替换目标
结点的值,然后把目标结点的下一个删除,也就达到了删除目标结点的作用。但是如果是删除最后一个结点的话,就只能顺序删除了。
因为它没有下一个结点了。当单向链表只有一个结点时候,这个结点既是头结点也是尾结点,那么在删除完这个结点之后需要将头结点置为NULLstruct listNode
{
int m_nvalue;
listNode* next;
};
void DeleteNode(listNode** head,listNode* ptobedelete)
{
if(head == NULL)
return;
if(*head==ptobedelete)//链表只有一个结点,删除头结点;
{
delete ptobedelete;
*head = NULL;
}//链表的结点有很多个,要删除最后一个结点
else if(ptobedelete->next == NULL)
{
listNode* pr = *head;
while(pr->next!= ptobedelete)
pr = pr->next ;
pr->next= NULL;
delete ptobedelete;
ptobedelete = NULL;
}//删除中间结点
else
{
listNode* pnext = ptobedelete->next;
ptobedelete->m_nvalue=pnext->m_nvalue;
ptobedelete->next=pnext->next;
delete pnext;
pnext=NULL;
}
}
面试题:实现一个函数将字符串中的每个字符都替换成%20.
//如果是从头开始的话,空格后面的字符每次都需要向后移动,所以时间复杂度是0(n^2);
//若从尾开始,并且先计算出空格的数目,那样每个字符只需要移动一次时间复杂度是O(n);
void ReplaceBlank(char string[],int length)
{
if(string == NULL || length<0)
return;
int orignallength=0;
int numberofblack=0;
int i=0;
while(string[i] !='\0')
{
++orignallength;
if(string[i] == ' ')
++numberofblack;
++i;
}
int newlength=orignallength-numberofblack+numberofblack*3-1;
int indexoforignal=orignallength-1;
int indexofnew = newlength;
while(indexoforignal >=0 && indexofnew >indexoforignal)
{
if(string[indexoforignal] != ' ')
string[indexofnew--] = string[indexoforignal--];
else
{
string[indexofnew--]='0';
string[indexofnew--]='2';
string[indexofnew--]='%';
--indexoforignal;
}
}
}
int main()
{
char string[20]="we";
int length = sizeof(string)/sizeof(char);
ReplaceBlank(string,length);
for(int i=0;i<length;++i)
cout<<string[i]<<endl;
return 0;
}
面试题:从尾到头打印链表
//1.一般的思路是将链表遍历一遍,先将链表逆置,然后在从头到位打印结点;
//这样做有一个问题是打印一般是只读操作,如果按照1思路就会把链表的结构修改了
//2.定义一个栈,先遍历一遍链表然后依次存入栈中,然后从栈中把元素取出,就是把链表逆置了。
#include<stack>
struct listnode
{
int m_nkey;
listnode* m_pnext;
};
void PrintListRevers(listnode* head)
{
stack<listnode*> st;
listnode* pnode = head;
while(pnode !=NULL)
{
st.push(pnode);
pnode =pnode->m_pnext;
}
while(!st.empty())
{
pnode = st.top();
cout<<pnode->m_nkey<<"--->";
st.pop();
}
}
面试题:重建二叉树
//重建二叉树
//给定二叉树的前序遍历和中序遍历请重建该二叉树
struct BinaryTreeNode
{
int m_pvalue;
BinaryTreeNode* m_pleft;
BinaryTreeNode* m_pright;
};
BinaryTreeNode* constructcore(int *startpreorder,int *endpreorder,int *startinorder,int *endinorder);
BinaryTreeNode* construct(int *preorder,int *inorder,int length)
{
if(preorder == NULL || inorder == NULL || length<=0)
return NULL;
else
return constructcore(preorder,preorder+length-1,inorder,inorder+length-1);
}
BinaryTreeNode* constructcore(int *startpreorder,int *endpreorder,int *startinorder,int *endinorder)
{
int rootvalue = startpreorder[0];
BinaryTreeNode* root = new BinaryTreeNode();
root->m_pvalue = rootvalue;
root->m_pleft = root->m_pright = NULL;
if(startpreorder == endpreorder)
{
if(*startpreorder == *endpreorder)
{
return root;
}
else
throw std::exception("Inval input");
}
//在中序遍历中找到根结点
int *rootinorder = startinorder;
while(rootinorder<= endinorder && *rootinorder!= rootvalue)
++rootinorder;
if(rootinorder == endinorder && * rootinorder != *endinorder)
{
throw std::exception("Invalid input");
}
int length = rootinorder-startinorder;
int * leftpreorderend = startpreorder+length;
if(length > 0)
{
//构建左子树
root->m_pleft = constructcore(startpreorder+1,leftpreorderend,startinorder,rootinorder-1);
}
if(startpreorder +length <endpreorder)
{
//构建右字树
root->m_pright = constructcore(leftpreorderend+1,endpreorder,rootinorder+1,endinorder);
}
return root;
}
int main()
{
int preorder[]={1,2,4,7,3,5,6,8};
int inorder[]={4,7,2,1,5,3,8};
int length = sizeof(preorder)/sizeof(int);
BinaryTreeNode* root= construct(preorder,inorder,length);
return 0;
}
面试题14 调整数组顺序,使得奇位数位于偶数前面
题目:给定一个数组,实现一个函数,使得数组的奇数位在前面,偶数位在后面。
一般的思路:
1、扫描数组,每次遇到偶数,把该偶数后面的数据迁移,
2、并且再次判断这个位置的数字,执行步骤1;
这样做的时间复杂度是O(n^2);
提高效率的算法:
用俩个指针,一个指向数组的开头,它指向后移动,第二个指针初始化指向数组的最后一个,它指向前移动,
在俩个指针相遇的之前,第一个指针总是位于第二个指针的前面,如果第一个指针指向偶数,第二个指针指向奇数,则交换俩个数字。
#include<iostream>
using namespace std;
void Reorderoddevent(int *pData,int length)
{
if(pData == NULL && length<0)
return;
int *pBegin = pData;
int *pEnd = pData+length-1;
while(pBegin<pEnd)
{
if((*pBegin % 2==0) && (*pEnd %2 ==1))
{
int temp=*pBegin;
*pBegin=*pEnd;
*pEnd = temp;
pBegin++;
pEnd--;
}
pBegin++;
}
}
int main()
{
int data[]={1,4.7,11,8,13,2,10,9,5,19};
int length = sizeof(data)/sizeof(int);
Reorderoddevent(data,length);
for(int i =0;i<length;++i)
{
cout<<data[i]<<" ";
}
return 0;
}
面试题 15 链表中倒数第K个结点
#if 1
struct ListNode
{
int m_nValue;
ListNode* m_pnext;
};
ListNode* FindKtotail(ListNode * phead,unsigned int x)
{
if(phead == NULL || x == 0)//我们规定倒数第1结点是尾结点。
return NULL;
ListNode* pAhead = phead;
ListNode*pBehind = phead;
for(int i=0;i<x-1;++i)
{
if(pAhead->m_pnext !=NULL)//链表的总结点数一定要大于倒数的第x个结点;
pAhead = pAhead->m_pnext ;
else
return NULL;
}
while(pAhead->m_pnext != NULL)
{
pAhead = pAhead->m_pnext ;
pBehind = pBehind->m_pnext ;
}
return pBehind;
}
#endif
面试题17 合并俩个已经排序的链表,使得新链表的顺序依然有序。应该考虑代码的健壮性,当输入的其中之一的链表是空链表的时候
应当返回另一个链表。
struct ListNode
{
int m_nvalue;
ListNode* m_pnext;
};
ListNode* Merge(ListNode* pHead1,ListNode* pHead2)
{
if(pHead1 ==NULL)
return pHead2;
else if(pHead2 == NULL)
return pHead1;
ListNode* pMergeHead = NULL;
if(pHead1->m_nvalue <pHead2->m_nvalue )
pMergeHead->m_pnext = Merge(pHead1->m_pnext ,pHead2);
else
pMergeHead->m_pnext =Merge(pHead1,pHead2->m_pnext );
return pMergeHead;
}
面试题18 , 树的子结构
//重建二叉树
//给定二叉树的前序遍历和中序遍历请重建该二叉树
struct BinaryTreeNode
{
int m_pvalue;
BinaryTreeNode* m_pleft;
BinaryTreeNode* m_pright;
};
BinaryTreeNode* constructcore(int *startpreorder,int *endpreorder,int *startinorder,int *endinorder);
BinaryTreeNode* construct(int *preorder,int *inorder,int length)
{
if(preorder == NULL || inorder == NULL || length<=0)
return NULL;
else
return constructcore(preorder,preorder+length-1,inorder,inorder+length-1);
}
BinaryTreeNode* constructcore(int *startpreorder,int *endpreorder,int *startinorder,int *endinorder)
{
//前序遍历的第一个值为根结点。
int rootvalue = startpreorder[0];
BinaryTreeNode* root = new BinaryTreeNode();
root->m_pvalue = rootvalue;
root->m_pleft = root->m_pright = NULL;
if(startpreorder == endpreorder)
{
if(*startpreorder == *endpreorder)
{
return root;
}
else
throw std::exception("Inval input");
}
//在中序遍历中找到根结点
int *rootinorder = startinorder;
while(rootinorder<= endinorder && *rootinorder!= rootvalue)
++rootinorder;
if(rootinorder == endinorder && * rootinorder != *endinorder)
{
throw std::exception("Invalid input");
}
int length = rootinorder-startinorder;
int * leftpreorderend = startpreorder+length;
if(length > 0)
{
//构建左子树
root->m_pleft = constructcore(startpreorder+1,leftpreorderend,startinorder,rootinorder-1);
}
if(startpreorder + length <endpreorder)
{
//构建右字树
root->m_pright = constructcore(leftpreorderend+1,endpreorder,rootinorder+1,endinorder);
}
return root;
}
bool DoesTree1hasTree2(BinaryTreeNode* phead1,BinaryTreeNode* phead2)
{
if(phead2==NULL)
return true;
if(phead1 ==NULL)
return false;
if(phead1->m_pvalue != phead2->m_pvalue)
return false;
else
{
return DoesTree1hasTree2(phead1->m_pleft ,phead2->m_pleft)&&DoesTree1hasTree2(phead1->m_pright ,phead2->m_pright);
}
}
判断是否有子树
bool HasSubTree(BinaryTreeNode* pHead1,BinaryTreeNode* pHead2)
{
bool result = false;
if(pHead1!=NULL && pHead2 != NULL)
{
if(pHead1->m_pvalue == pHead2->m_pvalue )
{
result = DoesTree1hasTree2(pHead1,pHead2);
}
if(!result)
{
result = HasSubTree(pHead1->m_pleft ,pHead2);
}
if(!result)
{
result = HasSubTree(pHead1->m_pright ,pHead2);
}
}
return result;
}
int main()
{
int preorder[]={1,2,4,7,3,5,6,8};
int inorder[]={4,7,2,1,5,3,6,8};
int length = sizeof(preorder)/sizeof(int);
BinaryTreeNode* root = construct(preorder,inorder,length);
int preorder1[]={3,5,6,8};
int inorder1[]={5,3,6,8};
int length1 = sizeof(preorder)/sizeof(int);
BinaryTreeNode* root1 = construct(preorder1,inorder1,length1);
bool result = HasSubTree(root,root1);
if(result)
{
cout<<"yes"<<endl;
}
else
cout<<"No"<<endl;
return 0;
}