剑指Offer

来源:互联网 发布:js点击显示隐藏div层 编辑:程序博客网 时间:2024/06/06 05:24

一、面试题

(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;elsereturn 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)。第二种方法:因为已知了要删除的结点,那么他的下一个结点也是知道的,既然这样的话就可以用下一个节点的值替换目标结点的值,然后把目标结点的下一个删除,也就达到了删除目标结点的作用。但是如果是删除最后一个结点的话,就只能顺序删除了。因为它没有下一个结点了。当单向链表只有一个结点时候,这个结点既是头结点也是尾结点,那么在删除完这个结点之后需要将头结点置为NULL
struct 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;elsereturn 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;}elsethrow 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 1struct 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 ;elsereturn 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);elsepMergeHead->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;elsereturn 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;}elsethrow 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;}elsecout<<"No"<<endl;return 0;}



 
原创粉丝点击