剑指offer总结

面试题12  打印1到最大的n 位数

思路:此题是一个大数问题,本质上可以归结为一个字符串的加1操作。需要注意溢出条件,动态分配内存的初始化不能忘记‘\0’   勿忘释放内存。打印大数时不能忘记前面的去零操作字符串的加法是从低位开始的number[n-1];

函数接口:voidPrint1ToMaxNDigits(int n)

Bool IncrementOne(char *number)//字符串加1 操作

Void PrintNumber(char *number)//打印数字字符串去零

面试题19二叉树的镜像

思路:二叉树的镜像本质上就是交换二叉树的左右子树。画个图示意一下,指针指向根节点。先序遍历二叉树,当根节点为NULL停止遍历,当根节点的左右子树为NULL停止遍历。其余交换左右子树即可

函数接口void MirrorBinaryTree(BinaryTreeNode *pRoot);

面试题20顺时针打印矩阵 

思路:对于mn列的矩阵需要打印元素的个数为m*n个,设立4个变量i1,i2,i3,i4, 从左到右,从上到下,从右到左,从下到上,顺时打印即可。循环终止条while(count<m*n)

函数接口:void PrintMatrixClockWisely(int** a,int row,int column);

面试题21包含min函数的栈 

思路:栈是先进后出的数据结构,栈的基本操作为入栈和出栈。设计包含min函数的栈只要在入栈和出栈后能找到min值就行,

设立一个辅助栈来存最小值,入栈时:当辅助栈为空或新入栈元素比辅助栈的栈顶元素要小时,则将新元素入辅助栈,否则将辅助栈的栈顶元素再次入栈;

出栈时只需将两个栈的栈顶元素出栈即可。

函数接口 template<classtype> void StackWithMIn::push(const type&value);

面试题22栈的压入弹出序列 

思路:根据弹出序列的顺序确定哪些元素已入栈。栈为空或是栈顶元素与弹出序列不一致,则将压入序列的元素入栈直到和栈顶元素和弹出序列相当等。大循环终止条件while(indexPop<length)

 函数接口: bool IsPopOrder(int*pushArray,int*popArray,int length);

面试题23从上往下打印二叉树

思路:本质为树的层序遍历,设立一个队列。从上到下访问即可

函数接口voidLevelOrder(BinarytreeNode* pRoot);

面试题24  二叉搜索树的后序遍历序列 

思路:二叉搜索树的性质满足根节点大于左子树的的节点,小于右子树的节点。后序遍历序列最后一个元素即为根节点,根据左子树的的值小于根结节点,遍历序列得出左子树的长度,和右子树的长度,同时检查右子树是否满足都大于根结节点这一性质,再递归。有点类似二叉树的建立

函数接口:boolIsPostOrder(int *sequence,int length);

面试题25二叉树中和为某一值的路径

思路:设立一个vector<int>Path记录路径,定义一个static int sum 累加和。先序遍历二叉树一直累加根结点到叶子结点的值,当结点是叶子结点同时和为指定值时输出路径

函数接口:

FindPath(BinaryTreeNode*pRoot,intexpectedSum,vector<int>&Path);

 

面试题26复杂链表的复制 

 思路:最笨的方法就是先复制原始链表上的每一个结点,然后再逐个设置每个结点的sibling 指针,每设置一个sibling 指针都得从新遍历一遍原始的单链表,或是在复制原始链表上的每个结点时把<N,N'>配对信息放到哈希表中

创新的解法:分三个步骤。第一步将复制原始链表的每一个结点N,创建出新节点N'并链接到N的后面,第二步设置每个复制节点N'sibling指针域,第三步将长链表拆分成两个链表

 

函数接口:  void CloneNodes(ComplexListNode *pHead);

Void ConnectSiblingNodes(ComplexListNode*pHead);

ComplexListNode* ReconnectSiblingNodes(ComplexListNode*pHead);

ComplexListNode*CloneComplexList(ComplexListNode*pHead);

面试题27二叉搜索树与双向链表  

思路:中序遍历一颗二叉搜索树的时候能够得到一个从小到大排序的有序序列。中序遍历二叉搜索树将其转换为双向链表。

其中left指针域作为双向链表的前驱指针,right指针域作为双向链表的后继指针

函数接口:BinaryTreeNode* Convert(BinaryTreeNode*pRoot);

VoidConvertBinaryTreeNodeToList(BinaryTreeNode*pRoot,BinaryTreeNode**pLastNodeInList);

面试28字符串的排列组合

思路:字符串的全排列:分两个步骤:1)第一个步  把第一个字符依次和后面的字符交换  即求所有可能出现在第一个位置的的字符,即 2)第二部固定第一个字符求后面所有字符的全排列

函数接口:voidPermutation(char *pstr,char* pBegin)//pBegin 为当前做排列操作的第一个字符

字符串的组合

思路:长度为n的字符串中求m个字符的组合。将n个字符分为两部分,第一个字符和其余n-1个字符扫描第一个字符时我们有两种选择:一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符

函数接口:void Combination(char *pstr,int m,vector<char> &result)// //求字符串pstr中长度为m的组合 组合存在vector<char>  

 

面试题29数组中出现次数超过一半的数字

思路:最直观的思路就是对数组进行排序然后取中位数。时间复杂度取决于排序算法。或用Partition()函数找一个枢纽元素将数组分为两部分一部分比枢纽元素小,另一部分比枢纽元素大且枢纽元素的位置要在数组的中位数。

新颖的想法:利用数组的特点,顺序遍历数组的时候保存两个值一个数组中的数字一个是该数字出现的次数若数字相同则次数加1 数字不同则次数减一

注意点:函数的入参检查以及是否有元素出现次数超过数组元素的一半。

函数接口:bool CheckMoreThanHalf(int *a,int length,int value);

int MoreThanHalf(int *a,int length);

面试题30 最小的K个数  

思路:最直观的想法就是先排序再取前k个数,这种方法的时间复杂度取决于排序的算法。用partition() 函数找枢纽元素,是元素下标为k-1

//适合数据量比较大的算法:multiset<intgreater<int>> 容器存储k个变量并实时更新里面的元素

函数接口:void GetLeastKNumber(int *a,intlength,multiset<int,greater<int>>& leastNumbers,int k);

面试题31连续数组的最大和

思路:最笨最直接的做法枚举数组的所有子数组并求出它们的和。长度为n的数组一共有n*(n-1)/2个子数组时间复杂度为O(n^2)

// 遍历数组同时设立两个变量cursum maxsum cursum<=0 时跟新cursum 为当前元素值。每次更新最大值maxsum的值即可

//用中规中矩的动态规划法用函数f(i)表示以第i个数字结尾的子数组的最大和。目标函数为max(f(i)) 其中0<=i<n;

//f(i)=a[i]  if(i==0||f(i-1)<=0)

//f(i)=f[i-1]+a[i] if(i!=0&&f(i-1)>0)

函数接口:GetGreastSumOfSubArray(int*a,int length);

面试题33把数组排成最小的数

思路:简单直观的思路是对数组中的元素进行全排列对全排列后组成的字符串取最小值即可。

//新颖的想法:用排序来解决这个问题,排序的大小的规则,当mn<nm 时认为m<n 用qsort(strnumbers,length,sizeof(char*),compare)

// 谓词函数  bool compare(const void *strNumber1,constvoid *strNumber2);

//注意:当字符串长度相同时可以用strcmp 来比较大小

接口函数:PrintMinNumber(int *a,int length);

Bool compare(const void * strNumber1,const void *strNumber2);

面试题34求第1500个丑数:

1是最小的丑数,思路只包含因子23,5 的数称为丑数  最直接的做法逐个判断一个数是否为丑数直到找到第1500个丑数为止。判断丑数的方法将一个数分别对2,3,5进行求余若能整除则连续求余若最后结果为1 则是丑数

//while(num%2==0){num=num/2}

//比较好的做法:丑数只包含因子2,3,5 所以丑数是另一个丑数的乘以235 的结果(1除外)设立另一个临时数组用来存储丑数的值,从丑数1 开始,分别乘以2,3,5 因子取最小值,逐个扩大丑数的范围。同时更新乘2 3 5的索引值使其始终为超过以求出丑数中最大值中的最小索引

//nextUglyIndex,pMultiply2,pMultiply3

函数接口:GetUglyNumber(intindex);

 

面试题35在字符串中找出第一个只出现一次的字符

思路:最简单的思路从头遍历字符串,没遍历一个字符将该字符和该字符之后的所有字符进行比较如果都不同则是第一个只出现一次的字符

更简单的方法:用数组构造一个简单的哈希表Hash[256] 遍历整个字符串统计每个字符出现的次数,再次从头到尾遍历整个字符串当出现次数为1时即是一个个只只出现一次的字符。

函数接口char FirstNotRepeat

同类型题目互为变位词:如果两个单词中出现的字母相同,并且每个字母出现的次数也相同。那么这两个单词互为变位词。完成一个函数,判断输入两个单词是不是互为变位词。

思路:用数组构造简单的哈希表,遍历第一个字符串统计每个字符出现的次数。遍历第二个字符,每扫描一个字符哈希表对应值减一。遍历完第二个字符后。再遍历哈希表。若表中所有值都为0则两个字符串互为变位词

面试题36数组中的逆序对

思路:最简单的方法。从头到尾遍历数组,每遍历一个数时,逐个比较该数字和它后面的数字的大小累加逆序对。

新颖的办法:先把数组分隔成子数组,先统计出子数组内部的逆序对的数目,再统计出两个相邻数组之间的逆序对的数目统计逆序对的过程是一个归并排序的过程在统计相邻数组的逆序对时,从大的往小的比,如果第一个数组的数字小于或等于第二个数组的数字则不构成逆序对,否则累加逆序对的个数。

函数接口 intinversePairs(int *data,int length);

Int  inversePairsCore(int *data,intlength,int start,int end,*temp)

面试题37两个链表的第一个公共结点

解题思路:1,最暴力的解法每遍历一个结点循环遍历另一个链表的每一个结点  2设立两个辅助栈将链表的每个结点入栈从表尾开始比较结点,最后一个相等的结点即为第一个公共结点

比较好的方法:先遍历每个单链表得到len1,len2,让较长的单链表先走|len2-len1|然后两个表一起遍历第一个结点相同的即为第一个公共结点

函数接口:intGetListLength(ListNode*pHead)

int ListNode* FindFirstCommonNode(ListNode*pHead1,ListNode*pHead2);

面试题38数字在排序数组中出现的次数

思路:最简单的办法就是顺序遍历。或用二分查找得到数组中第一个值为k的下标first,和最后一个个值为k 的下标last 得到长度为last-first+1

看到排序数组第一反应想到二分查找;

二分查找可以用来查找排序数组中值为k的位置或值为k第一次出现的位置或值为k时最后一次出现的位置;

函数接口  int GetFirstK(int *data,int length,int k,intstart,int end);

Int GetLaskK(int *data,int length,int k,int start,int end);

GetNumberOfK(int *dat)

查找关键代码:if(data[mid]==k){if(mid>0&&data[mid-1]!=k||mid==0)}

面试题39二叉树的深度

思路:最直接的想法根节点到叶子结点的最长路径即为二叉树的深度。新颖的方法将二叉树分为三部分左子树和右子树,根结点树的深度为左子树深度和右子树深度中的最大值加先序遍历二叉树得到每个结点的相对深度

函数接口 intTreeDepth(BinaryTreeNode *pRoot);

 

类似题目判断一颗二叉树是否为平衡二叉树

思路:最直接的思路是先序遍历二叉树若根节点为空则为平衡二叉树,否则算出结点左右子树的深度,看是否满足平衡因子小于1

好的方法:用后序遍历的方式遍历整棵二叉树,遍历得到每个结点的左右子结点的深度判断是否平衡同时得到当前结点的深度。最后遍历到根结点时就判断得到了整棵二叉树是不是平衡二叉树

函数接口:bool IsBalaned(BinaryTreeNode*pRoot,int*pDepth)

面试题40 一个整型数组里除了两个数字以外其他数字都出现了两次请写出程序找出这两个只出现一次的数字。时间复杂度O(n)

思路:异或的作用:任何一个数字异或它自身都为0,异或0都为它自身,异或1为位取反。从头到尾异或数组中的每一个数字结果为数组中两个只出现一次的数字的异或结果。由于这两个数字一定不一样,在异或的结果中找到第一个为1的位置,记为第n位根据这个1的位置将数组的中的数字分成两组。一组第n位为1,另一组第n位为0.

函数接口 intFindFirstBitsIs1(int num);//返回num 右边第一个数字为1的索引。

Bool IsBit1(int num,int indexBit);//判断右边第indexbit是否为1

FindNumbersAppearOnce(int data[],int length,int *num1,int*num2)//找到的两个数用num1,和num2返回;

面试题41输入一个递增排序的数组和一个数字s,在数组中查找两个数使它们的和正好是s如果有多对数字的和等于s输出任意一对即可。

思路:最简单的办法逐个遍历数组中数,每遍历一个数时将该数与后面的数相加判断是否和为s

好的点方法:设立两个指针,第一个指向第一个数字,第二个指针指向最后一个,和==sum的跳出循环,cursum 小于sum的第一个指针后移,否则最后一个指针前移

i=0,j=length-1   whilei<j{}

函数接口: boolFindNumbersWithSum(int data[],int length,int sum,int*num1,int*num2)

类似题目输入一个正整数s 打印出所有和为s的连续正整数(至少含有两个数)

思路穷举法:1s 之间的所有连续整数相加判断即可

intmiddle=(sum+1)/2;

    for(inti=1;i<=middle;i++)

    {

        intstart=i,end=i;

       intcursum=0;

       while(end<=middle)

函数接口:voidFindContinuousSequence(int sum);

Void PrintContinousSequence(int start,int end);

面试题42翻转单词顺序VS左旋字符串

对于翻转单词顺序思路:比较简单的思路用getline 读进字符串,用istringstream 流分隔单词然后用栈存储单词后再输出即可。

或整体翻转字符串后再单独翻转单词单独翻转单词时从头遍历字符串设立两个指针pBegin,pEnd 使其一直保持指向一个单词的第一个字母和最后一个字母

函数接口 void Reverse(char*pBegin,char*pEnd);

Char *ReverseWord(char *str);

关键码:while(*pBegin!=’\0’) {if(*pBegin==’ ’)else if(*pEnd==’’||*pEnd==’\0’){}else{}}

对于旋转字符串:字符的的左旋操作是把字符串前面的若干个字符转移到字符串的尾部思路:最暴力的方法:每次循环左移移位数,左旋多少个字符就循环左移多少位。

好的方法:三步翻转搞定,先翻转左旋字符的个数再反转剩余的数,再整体翻转;

函数接口:voidReverse(char*pBegin,char *pEnd);

Char* LeftRotateString(char*pStr,int n);

关键码:Reverse(pFirstStart,pFirstEnd);Reverse(pSecondStart,pSecondEnd);Reverse(pFirstStart,pSecondEnd);

面试题43 n个骰子的点数:把n个骰子扔在地上所有骰子朝上的一面的点数之和为s 输入n打印出s的所有可能的值出现的概率。

思路:n个骰子点数和的最小值为n 点数和的最大值为6*n  n个骰子的组合总数为6^n  统计出每个点数和出现的次数再除以这个总数即得到每个点数出现的概率。

可以用递归的方法来实现将n个骰子分成两部分,第一个骰子和剩余n-1个骰子,每个骰子有点数有六种情况,分别将其累加到剩余骰子的点数中。

函数接口:voidProbability(int originalN,int current,int sum,*Probabilities);//originalN 为骰子个数,current为骰子剩余个数

Void Probability(int number,int*Probabilities);

面试题44:扑克牌的顺子:从扑克牌中随机抽5张牌,判断是不是顺子,即5张牌是不是连续的,2~10为数字本身,A1J11 Q12K13而大小王可以看成任意的数字

思路:大小王用0表示,输入数组的范围为0~13 对输入的数组统计0的个数,然后进行排序设立两个游标smallIndex=numberOfZero,bigIndex=smallIndex+1遍历数组统计相邻数的间隔,如果有对子则直接返回不是顺子,若遍历完间隔数小于等于0的个数则是顺子否则不是顺子

函数接口: boolIsContinuous(int *numbers,int length);

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值