剑指offer解题思路总结(笔记)

本文总结了剑指offer中的经典算法题目,包括二维数组查找、替换空格、链表操作、二叉树构建、栈与队列的应用、斐波那契数列等,讲解了解题思路与优化方法,旨在提升编程能力。
摘要由CSDN通过智能技术生成

1.二维数组中的查找思路:
从左下或右上开始,以左下为例,若该数比目标小,则右移,若比目标大,则上移。时间复杂度O(n),暴力法求解时间复杂度为O(n2)。
2.替换空格思路:
先遍历一遍统计空格个数,然后进行第二遍遍历(从后向前),定义两个指针p1和p2,一个指向原始字符串末尾,一个指向替换后的字符串末尾,若p1指向非空格则将p1复制到p2中,然后向前,若遇到空格则p2用对应符号代替,p1向前一格,p2向前三格。时间复杂度O(n),暴力法求解时间复杂度为O(n2)。
3.从头到尾打印链表
先正向输出到vector中,再reverse。第二种思路用栈来收集链表元素。
4.重建二叉树
前序遍历为12473568,中序遍历为47215368,则可知1为根结点,247为1的左子树,3568为1的右子树,递归即可得到完整二叉树。
5.用两个栈实现队列
栈1用来入,栈2用来出,入队操作即入栈1,出队操作先看栈2是否为空,若不为空则栈2栈顶元素出栈,若空则先将栈1所有元素压入栈2再栈2栈顶元素出栈。
6.旋转数组中的最小数字
二分查找即可,left=头,right=尾,如果mid>left则要找的数在右半边,若mid<right则要找的数在左半边。
7.斐波那契数列
1)递归:时间复杂度较大,超时。2)用一个数组记录把每次结果都记录下来。3)用三个变量
8.跳台阶
思路同上
9.变态跳台阶
递归后找到通项公式
10.矩形覆盖
同7、8题,递归时间复杂度过大。
11.二进制中1的个数
bitset函数,或者与1按位与,然后1左移,一位一位判断是不是1。
12.数值的整数次方
pow函数,或沿用上题思路,从最低位开始,遇到1,乘以底数,否则不乘,指数每过一位,底数要翻倍。
13.调整数组顺序使奇数位于偶数前面
利用插排的思想,先找到第一个偶数,然后遍历后面的数,若遇到奇数,向前交换到第一个偶数之前,在这个位置基础上继续向后遍历,直到没有奇数,时间复杂度为O(n2);第二种思路是用两个队列,遇到奇数进一个队列,遇到偶数进一个队列,遍历完后再出队列,时间复杂度和空间复杂度都是O(n)。
14.链表中倒数第k个节点
快慢指针
15.反转链表
第一种思路为用栈,可以保留原链表,第二种思路为快慢指针,快指针比慢指针快1,注意先拆掉最左端节点的next。
16.合并两个排序的链表
归并排序
17.树的子结构
两个函数,函数1判断root2是否为root1的子结构,函数2判断root2是否是root1的子结构且子结构根节点为root1,函数1返回函数2(root1,root2)||函数1(root1->left,root2)||函数1(root1->right,root2),函数2返回函数2(root1->left,root2->left)&&函数2(root1->right,root2->right),函数1root2为空时返回false,函数2root2为空时返回true。
18.二叉树的镜像
先交换根节点的左右子节点,然后左右节点递归调用该函数19.顺时针打印矩阵
分为向右,向下,向左,向上四个方向分别循环,循环条件为转折点的特征,注意向右打印的转折点,以及n1的向量较为特殊。
20.包含min函数的栈
用一个栈来保存数据,另一个栈来维护最小值,进栈时若小于等于最小值栈的栈顶元素,则将该数也压入最小值栈,出栈时若等于最小值栈栈顶元素,则一起出栈。
21.栈的压入,弹出序列
定义一个辅助栈,以第一个序列压入,定义一个指针指向popV的第一个元素即为当前该出栈的元素,当栈顶元素不为当前该出栈的元素时,压入下一个元素,当栈顶元素为当前该出栈的元素时,出栈,当前该出栈的元素指针右移,循环判断,循环结束后,若辅助栈为空,则为可能的出栈顺序,否则不是。第二种思路:在入栈顺序中找到第一个该出栈的,则这个元素之前的出栈顺序一定为反向,判断出栈顺序中这几个元素顺序是否合理,若合理,则将这几个元素删除,递归判断剩下的,例如1,2,3,4,5和4,5,3,2,1,先判断1,2,3的顺序是否合理,在判断4,5和4,5是否满足条件,但是递归调用空间复杂度过大。
22.从上往下打印二叉树
层次遍历二叉树
23.二叉搜索树的后序遍历序列
一个序列若为一个二叉搜索树的后序遍历序列,则最后一个节点一定为根节点,则从第一个节点开始找,找到第一个大于根节点的,左面即为左子树,右面即为右子树,然后遍历右子树是否均大于根节点,然后再递归判断左子树和右子树。
24.二叉树中和为某一值的路径
利用二叉树的先序递归遍历(再定义一个函数),当和等于期望值时判断是否还有子节点,若有,则返回上一层,若没有子节点,则输出路径,注意每次返回前将当前和与当前路径也回退。
25.复杂链表的复制
将原链表每个节点复制后插入到该节点后面,然后再循环复制随机指针,最后将链表拆分成两个链表。
26.二叉搜索树与双向链表
使用中序遍历(左根右),遍历到某一个节点则将上一个遍历节点(定义一个全局变量来记录)的right指向它,将当前节点的左指针指向上一个节点即可建立该链表,最后注意返回最左面的指针。遍历可用递归遍历也可用非递归遍历。
27.字符串的排列
先进行全排列,然后排序去重。全排列时,将第一位与后面各位交换,列出第一位所有可能的情况,然后后面的元素递归进行全排列。或者遍历字符串选出最小的字母,然后剔除,剩余的字母进行递归与第一个字母组合。
28.数组中出现次数超过一半的数字
先排序,然后定义一个指针it和一个统计数字出现次数的变量num,指针向前走,判断若和前一个数相同,则num+1,否则将num置为1,当num符合条件便返回,循环结束后没找到则返回0。排序复杂度O(n2),另一种思路,设置一个value设置一个count,然后遍历,如果当前的数等于value则count++,否则count–,若count=0,则将当前的数设为value继续遍历,遍历完在遍历一遍统计value的个数,若大于长度一半则输出value否则输出0。
29.最小的k个数
快排,去重(若有重复的话),取前k个数。另一种思路:前k个数建立一个大顶堆,新数若大于堆顶就舍弃,若小于堆顶就将堆顶删除,新数入堆。
30.连续子数组的最大和
动态规划:dp[i]表示以i为终点的连续最大和(连续),则dp[i]=arrayi或array[i]+dpi-1,结果取dp[i]中最大值。
31.从1到n整数中1出现的次数
将数字转化为字符串统计1出现的次数(stringstream)。
32.把数组排成最小的数
将数组中的数变为字符串,对字符串进行排序,交换条件是两个字符串拼接后的两个新字符串比较大小(字典序)例如“12”和“132”,比较“12132”和“13212”大小。
33.丑数
维护四个队列,丑数队列,乘以2的队列,乘以3的队列,乘以5的队列,每次取丑数队列末尾与2,3,5相乘放入对应队列,在三个队列头中取最小放入丑数队列,并弹出该数,队列头相同时都需弹出,直到丑数队列数量达到要求。
34.第一个只出现一次的字符
创建一个大小为52的数组,遍历字符串统计字母频率,再遍历一遍找到第一个频率为1的字母,停止遍历,返回位置(在给字母标序号时注意’a’-‘Z’!=1)。
35.数组中的逆序对
将数组归并排序,归并时,每当右指针后移,则说明有逆序对,又因为左右两边已经分别有序,所以逆序对数res=res+mid-left+1,时间复杂度为O(NlogN)。
36.两个链表的第一个公共结点
暴力法:将两个链表的结点进行双循环,时间复杂度为O(mn)。辅助栈:若有公共结点,拓扑为Y型,可以将两个链表结点进栈,从后向前比较第一个不相同的位置。
37.数字在排序数组中出现的次数
二分找到目标k,然后向左右搜索出现过几次k。
38.二叉树的深度
利用递归遍历来计算,或者TreeDepth(根)=max(TreeDepth(左)+1,TreeDepth(右)+1)来递归计算。
39.平衡二叉树
先计算左右子树高度差是否不大于1,再递归判断左右子树是否为平衡二叉树(如果左边不是便不用判断右边)。
40.数组中只出现一次的数字
将数组循环一遍建立一个map,key为数组中的数,value为该数出现的次数,再循环一遍map即可。
41.和为s的连续正数序列
使用高低指针,连续和大于sum时,低指针前进1,小于sum时,高指针前进1,等于sum时,将序列输出,结束条件为left+left+1>sum。
42.和为S的两个数字
高低指针,低指针指向第一个数,高指针指向最后一个数,两数之和大于sum时,高指针左移,小于sum时,低指针右移,等于时将序列输出,此时为乘积最小。
43.左旋转字符串
先将左移位数对长度求模,左移n位,即将左半边n位放到右边。
44.翻转单词顺序列
先利用stringstream分词(getline),分词后翻转,注意首字母为空格的情况和字符串中有多余空格的情况。
45.扑克牌顺子
先进行排序,再将0剔除,若所剩元素只有一个则可以组成顺子,否则,若有重复元素则不可,若无重复元素,最大值-最小值<5则可以组成顺子,否则不可。
46.孩子们的游戏(圆圈中最后剩下的数)
约瑟夫环问题,用循环链表解决。
47.求1+2+3+…+n
利用递归,结束判断利用短路求值原理。
48.不用加减乘除做加法
两数异或求不考虑进位的结果,两数相与并左移一位求进位结果,将求得的结果继续循环上述步骤直到进位为0。
49.把字符串转换成整数
使用强制转换int(str[i]-‘0’)来将每一位的字符转换为数字,然后将整个字符串转换为数,注意溢出,非法,正负等问题。50.数组中重复的数字
扫描数组,浏览过一个数,将对应位数上的数-length,之后再浏览到相同的数会发现对应位数上的数为负数,时间复杂度为O(n)51.构建乘积数组构建两个数组left和right,left从上到下为连乘,right从下到上为连乘,建立好之后,B[i]=left[i]right[n-i-1],时间复杂度为O(n)
52.正则表达式
匹配当模式中的第二个字符不是“
”时: 1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。 2、如果字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。而当模式中的第二个字符是“
”时:如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以返回3种匹配方式的结果: 1、模式后移2字符,相当于x被忽略; 2、字符串后移1字符,模式后移2字符; 3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为可以匹配多位;跳出循环时,当字符和模式都达到末尾或者字符达到末尾模式达到“X*”可返回true,其余返回false。
53.表示数值的字符串
先判断字符串首的正负号,之后一位一位判断,’.‘之后不能出现’.’,‘e’之后不能出现’.‘和’e’,'e’后面一位可出现正负号,'e’不能作为最后一位。
54.字符流中第一个不重复的字符
用一个128的数组记录每个字符出现的次数,再用一个队列记录每个字符出现的次序(第一次出现就进队列),之后检测队首,如果只出现了一次就返回该字符,否则就删除继续检测。
55.链表中环的入口结点
快慢指针,快指针走2,慢指针走1,若有环一定相遇于环内,令一个指针在相遇点,另一个在链表头,都每次走1,相遇点便是环的入口(定理不再证明),要注意空链表和只有一个结点的链表的特殊情况。
56.删除链表中重复的结点
两个指针,后面的指针指向不重复的最后一个节点,前面的指针指向重复的最后一个节点。(设置头节点以免12结点需要删除,注意最后节点为重复结点的情况)。
57.二叉树的下一个结点
根据中序遍历的特性,一个结点被浏览过后,他的左子树全被浏览过。1、该节点为空则返回空。2、该节点右节点为空,若该节点为父节点的左节点则返回父节点,否则向上找到作为父节点左节点的结点并返回父节点。3、若该节点右节点不为空,则从右节点开始一直向左找到子节点并返回。
58.对称的二叉树
另写一个函数判断两个二叉树是否镜像,判断标准是递归判断1的左子树和2的右子树是否镜像,1的右子树和2的左子树是否镜像。判断对称时只需判断左右子树是否镜像即可。
59.按之字形顺序打印二叉树
类似于树的层次遍历,使用两个栈,奇数层栈的子节点进入偶数层时先左后右,偶数层栈的子节点进入奇数层时先右后左,遍历奇数层还是偶数层可通过栈空和标志位进行判断。60.把二叉树打印成多行
二叉树的层次遍历,可以用两个队列和一个标志位来判断遍历到哪一层。
61.序列化二叉树
通过前序递归来序列化,反序列化时,遇到数值时,建立一个新的节点,然后递归建立左子树,右子树,遇到‘#’时,跳过,返回空节点,int转化为string可以通过函数to_string(),字符流转为int可以通过temp=temp10+(str-’\0’)。递归函数中要使得指针随着递归一直变化,则可以用引用。
62.二叉搜索树的第k个结点
用中序遍历即可,中序遍历二叉搜索树为从小到大顺序。
63.数据流中的中位数
用priority_queue构造一个大顶堆和小顶堆,插入数据时,奇数个时,插入大顶堆,将大顶堆的最大值(根节点)插入小顶堆;偶数个时,插入小顶堆,将小顶堆最小值(根节点)插入大顶堆,从而保证大顶堆中元素个数等于小顶堆元素个数或者少1,且大顶堆元素均小于小顶堆,取中位数时,偶数个便取大小顶堆的根节点平均数,奇数个便取小顶堆根节点。
64.滑动窗口的最大值
两个指针遍历,时间复杂度为O(n2)。
65.矩阵中的路径
遍历矩阵中的字符作为起始字符,递归寻找。1.如果当前字符与路径不匹配或者走过,返回false;2.如果当前字符越界,返回false;3.如果当前字符与路径匹配且未走过,递归匹配下一字符(上下左右)与剩下的路径,返回他们的相与结果。注意需要一个标志位数组来记录哪些字符走过。(不要传引用进去,递归函数不需要改变上一层函数中的标志状态)。
66.机器人的运动范围
与上题类似递归遍历一遍即可。
67.剪绳子数学规律:当n=4时可以分成2
2,n=5时可以分成2
3,n更大时便可以分的更细,所以每一段只能是2或3,且至多有两段是2,因为222可以分成33,所以可以对3取模,然后用乘方运算,注意因为m>1,所以2要分解成11,3要分解成1*2。动态规划:设要求的结果为dp[n],则递推关系为dp[i]=max{dp[0]*dp[i]…dp[i/2]dp[i/2]}=max(j=1-i/2){dp[j]*dp[i-j]}。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值