剑指offer第二版题解
software_Manito
这个作者很懒,什么都没留下…
展开
-
二叉搜索树与双向链表(三十七)
题目输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。注意:需要返回双向链表最左侧的节点。例如,输入下图中左边的二叉搜索树,则输出右边的排序双向链表。算法:class Solution { public TreeNode convert(TreeNode root) { if(root == null) return root; Pair<TreeNode,TreeNode原创 2020-12-01 10:31:00 · 188 阅读 · 0 评论 -
复杂链表的复刻(三十六)
题目请实现一个函数可以复制一个复杂链表。在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。注意:函数结束后原链表要与输入时保持一致。算法思想:将当前节点复制一份并且插入到当前节点的后面,然后再链接虚拟节点,最后进行拆分。不能直接返回原链表,系统会判别,最后还要将原链表恢复。class Solution { public ListNode copyRandomList(ListNode head) { //第一次遍历原创 2020-05-24 11:21:52 · 204 阅读 · 0 评论 -
二叉树中和为某一值的路径(三十五)
题目输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。样例算法class Solution { List<List<Integer>> res = new ArrayList(); List<Integer> path = new ArrayList(); public List<List<Integer>> findPath(Tree原创 2020-05-24 11:04:23 · 201 阅读 · 0 评论 -
二叉搜索树的后序遍历序列(三十四)
题目输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。样例输入:[4, 8, 6, 12, 16, 14, 10]输出:true算法后序遍历的最后一个节点是是根节点,所以我们只要找出比根节点小的部分为左子树,然后判断右边部分是否存在比根节点小的值,如果有返回false,否则的话就进行递归调用。class Solution { int[] seq; public boole原创 2020-05-24 10:53:50 · 268 阅读 · 1 评论 -
之字形打印二叉树(三十三)
题目请实现一个函数按照之字形顺序从上向下打印二叉树。即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。样例算法相比于上一题,这道题仅需要增加一个标志位,我这里采用是一个整数,如果是奇数则正序打印,偶数翻转。时间复杂度 O(n)class Solution { public List<List<Integer>> printFromTopToBottom(TreeNode root) {原创 2020-05-23 09:28:42 · 188 阅读 · 0 评论 -
分行从上往下打印二叉树(三十二)
题目从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。样例算法BFS(O(n))依旧是宽度优先遍历,相比不分行从上到下打印,只需要记住这一层需要弹出队列多少次数即可,对应代码里面的n。当队列不为空时记录下当前队列的长度n,这个长度对应树的当前层有多少个节点相比较上一题,这次在一个循环里面,只弹出n个节点就好了。弹出过程中存储当前节点到temp数组弹出n个节点之后,将temp数组存入result时间复杂度每个节点遍历1遍,O(N)复杂度class Sol原创 2020-05-23 09:25:15 · 217 阅读 · 0 评论 -
不分行从上往下打印二叉树(三十一)
题目从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。样例算法(BFS) O(n)我们从根节点开始按宽度优先的顺序遍历整棵树,每次先扩展左儿子,再扩展右儿子。这样我们会:先扩展根节点;再依次扩展根节点的左右儿子,也就是从左到右扩展第二层节点;再依次从左到右扩展第三层节点;依次类推所以BFS的顺序就是这道题目要求的顺序。时间复杂度BFS时每个节点仅被遍历一次,所以时间复杂度是 O(n)。class Solution { public List<In原创 2020-05-23 09:21:52 · 191 阅读 · 0 评论 -
栈的压入、弹出序列(三十)
题目输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。注意:若两个序列长度不等则视为并不是一个栈的压入、弹出序列。若两个序列都为空,则视为是一个栈的压入、弹出序列。样例输入:[1,2,3,4,5][4,5,3,2,1]输出:true算法(栈) O(n)用一个新栈s来模原创 2020-05-22 14:56:26 · 201 阅读 · 0 评论 -
包含min函数的栈(二十九)
题目设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。push(x)–将元素x插入栈中pop()–移除栈顶元素top()–得到栈顶元素getMin()–得到栈中最小元素样例:MinStack minStack = new MinStack();minStack.push(-1);minStack.push(3);minStack.push(-4);minStack.getMin(); --> Returns -4.minStack.原创 2020-05-22 14:52:45 · 120 阅读 · 0 评论 -
顺时针打印矩阵(二十八)
题目输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。样例输入:[ [1, 2, 3, 4],[5, 6, 7, 8],[9,10,11,12]]输出:[1,2,3,4,8,12,11,10,9,5,6,7]算法(模拟) O(n^2)我们顺时针定义四个方向:上右下左。从左上角开始遍历,先往右走,走到不能走为止,然后更改到下个方向,再走到不能走为止,依次类推,遍历 n^2 个格子后停止。时间复杂度矩阵中每个格子遍历一次,所以总时间复杂度是 O(n^2)。cla原创 2020-05-22 14:47:59 · 124 阅读 · 0 评论 -
对称的二叉树(二十七)
题目请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。样例算法(二叉树,递归) O(n)递归判断两个子树是否互为镜像。两个子树互为镜像当且仅当:两个子树的根节点值相等;第一棵子树的左子树和第二棵子树的右子树互为镜像,且第一棵子树的右子树和第二棵子树的左子树互为镜像;时间复杂度从上到下每个节点仅被遍历一遍,所以时间复杂度是 O(n)。class Solution { public boolean isSymmetric(TreeNo原创 2020-05-21 15:04:25 · 131 阅读 · 0 评论 -
二叉树的镜像(二十六)
题目输入一个二叉树,将它变换为它的镜像。样例算法(二叉树,递归) O(n)我们可以发现镜像后的树就是将原树的所有节点的左右儿子互换!所以我们递归遍历原树的所有节点,将每个节点的左右儿子互换即可。时间复杂度原树仅被遍历一次,所以时间复杂度是 O(n)。class Solution { public void mirror(TreeNode root) { if(root == null) return ; if(root.left != null |原创 2020-05-21 15:01:11 · 187 阅读 · 0 评论 -
树的子结构(二十五)
题目输入两棵二叉树A,B,判断B是不是A的子结构。我们规定空树不是任何树的子结构。样例树A:树B:返回 true ,因为B是A的子结构。算法(二叉树,递归) O(nm)代码分为两个部分:遍历树A中的所有非空节点R;判断树A中以R为根节点的子树是不是包含和树B一样的结构,且我们从根节点开始匹配;对于第一部分,我们直接递归遍历树A即可,遇到非空节点后,就进行第二部分的判断。对于第二部分,我们同时从根节点开始遍历两棵子树:如果树B中的节点为空,则表示当前分支是匹配的,返回true原创 2020-05-21 14:57:20 · 227 阅读 · 0 评论 -
链表中倒数第k个节点(二十一)
题目输入一个链表,输出该链表中倒数第k个结点。注意:k >= 0;如果k大于链表长度,则返回 NULL;样例输入:链表:1->2->3->4->5 ,k=2输出:4算法先翻转链表然后遍历输出第k个节点。时间复杂度O(n)class Solution { public ListNode findKthToTail(ListNode pListHead, int k) { if(pListHead == null) return n原创 2020-05-20 09:47:36 · 155 阅读 · 0 评论 -
调整数组顺序使奇数位于偶数前面(二十)
题目输入一个整数数组,实现一个函数来调整该数组中数字的顺序。使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分。样例输入:[1,2,3,4,5]输出: [1,3,5,2,4]算法(双指针扫描) O(n)用两个指针分别从首尾开始,往中间扫描。扫描时保证第一个指针前面的数都是奇数,第二个指针后面的数都是偶数。每次迭代时需要进行的操作:第一个指针一直往后走,直到遇到第一个偶数为止;第二个指针一直往前走,直到遇到第一个奇数为止;交换两个指针指向的位置上的数,再进入下一层迭代原创 2020-05-20 09:45:04 · 132 阅读 · 0 评论 -
表示数值的字符串(十九)
题目请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。注意:小数可以没有整数部分,例如.123等于0.123; 小数点后面可以没有数字,例如233.等于233.0;小数点前面和后面可以有数字,例如233.666; 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;当e或E后面没有整数时原创 2020-05-20 09:41:23 · 177 阅读 · 0 评论 -
合并两个排序的链表(二十四)
题目输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。样例输入:1->3->5 , 2->4->5输出:1->2->3->4->5->5算法(二路归并) O(n)新建头部的保护结点dummy,设置cur指针指向dummy。若当前l1指针指向的结点的值val比l2指针指向的结点的值val小,则令cur的next指针指向l1,且l1后移;否则指向l2,且l2后移。然后cur指针按照上一部设置好的位置后移。原创 2020-05-20 09:35:02 · 146 阅读 · 0 评论 -
反转链表(二十三)
题目定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。样例输入:1->2->3->4->5->NULL输出:5->4->3->2->1->NULL算法class Solution { public ListNode reverseList(ListNode head) { ListNode NewHead = null,temp; while(head != null){原创 2020-05-20 09:32:37 · 142 阅读 · 0 评论 -
链表中环的入口结点(二十二)
题目给定一个链表,若其中包含环,则输出环的入口节点。若其中不包含环,则输出null。样例给定如上所示的链表:[1, 2, 3, 4, 5, 6]2注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。则输出环的入口节点3.算法(链表,快慢指针扫描) O(n)本题的做法比较巧妙。用两个指针 first,second 分别从起点开始走,first 每次走一步,second每次走两步。如果过程中 second 走到null,则说明不存在环。否则原创 2020-05-20 09:28:21 · 137 阅读 · 0 评论 -
删除链表中重复的节点(十七)
题目在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留。样例1输入:1->2->3->3->4->4->5输出:1->2->5样例2输入:1->1->1->2->3输出:2->3算法(线性扫描) O(n)为了方便处理边界情况,我们定义一个虚拟元素 dummy 指向链表头节点。然后从前往后扫描整个链表,每次扫描元素相同的一段,如果这段中的元素个数多于1个,则将整段元素直接删除原创 2020-05-20 09:19:44 · 118 阅读 · 0 评论 -
在O(1)时间删除链表结点(十六)
题目给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。假设链表一定存在,并且该节点一定不是尾节点。样例输入:链表 1->4->6->8删掉节点:第2个节点即6(头节点为第0个节点)输出:新链表 1->4->8算法(链表) O(1)由于是单链表,我们不能找到前驱节点,所以我们不能按常规方法将该节点删除。我们可以换一种思路,将下一个节点的值复制到当前节点,然后将下一个节点删除即可。时间复杂度只有常数次操作,所以时间复杂度是 O(1)。c原创 2020-05-20 09:11:29 · 136 阅读 · 0 评论 -
位运算技巧集合
位操作符& 与运算 两个位都是 1 时,结果才为 1,否则为 0,如 1 0 0 1 1& 1 1 0 0 11 0 0 0 1| 或运算 两个位都是 0 时,结果才为 0,否则为 1,如1 0 0 1 1| 1 1 0 0 11 1 0 1 1^ 异或运算,两个位相同则为 0,不同则为 1,如1 0 0 1 1^ 1 1 0 0 10 1 0 1 0~ 取反运算,0 则变为 1,1 则变为 0,如~ 1 0 0 1 10 1 1 0 0转载 2020-05-17 15:44:42 · 234 阅读 · 0 评论 -
旋转数组的最小数字(十)
题目把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个升序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。数组可能包含重复项。注意:数组内所含元素非负,若数组大小为0,请返回-1。样例输入:nums=[2,2,2,0,1]输出:0算法:(二分) O(n)为了便于分析,我们先将数组中的数画在二维坐标系中,横坐标表示数组下标,纵坐标表示数值,如下所示:图中水平的实线段表示相同元素。原创 2020-05-16 11:21:20 · 132 阅读 · 0 评论 -
机器人的运动范围(十二)
题目地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼n−1。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。但是不能进入行坐标和列坐标的数位之和大于 k 的格子。请问该机器人能够达到多少个格子?样例1输入:k=7, m=4, n=5输出:20样例2输入:k=18, m=40, n=40输出:1484解释:当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),原创 2020-05-17 12:00:30 · 199 阅读 · 0 评论 -
数值的整数次方(十五)
题目实现函数double Power(double base, int exponent),求base的 exponent次方。不得使用库函数,同时不需要考虑大数问题。注意:不会出现底数和指数同为0的情况当底数为0时,指数一定为正样例1输入:10 ,2输出:100样例2输入:10 ,-2输出:0.01算法二分法 如果exponent为偶数 Power(base,exponent)=Power(base,exponent/2)Power(base,exponent/2),如果原创 2020-05-17 11:53:01 · 145 阅读 · 0 评论 -
二进制中1的个数(十四)
题目输入一个32位整数,输出该数二进制表示中1的个数。注意:负数在计算机中用其绝对值的补码来表示。样例1输入:9 输出:2解释:9的二进制表示是1001,一共有2个1。样例2输入:-2 输出:31 解释:-2在计算机里会被表示成11111111111111111111111111111110,一共有31个1。算法使用n&(n-1)来计算n二进制中一的个数,这个操作会将原来最右边的1变成0时间复杂度O(logn)class Solution { publi原创 2020-05-17 11:47:13 · 259 阅读 · 0 评论 -
剪绳子(十三)
题目给你一根长度为 n 绳子,请把绳子剪成 m 段(m、n 都是整数,2≤n≤58 并且 m≥2)。每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]k[1] … k[m] 可能的最大乘积是多少?例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。样例输入:8输出:18算法:当n<5时,我们会发现,无论怎么剪切,乘积product <= n,n为4时,product最大为2*2=4;当n>=5时,可以证明2(n-2)原创 2020-05-17 11:13:59 · 215 阅读 · 0 评论 -
矩阵中的路径(十一)
题目请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。注意:输入的路径不为空;所有出现的字符均为大写英文字母;样例:matrix=[[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]]str=“BCCE” , return “true”str原创 2020-05-16 11:27:07 · 170 阅读 · 0 评论 -
斐波那契数列(九)
题目输入一个整数 n ,求斐波那契数列的第 n 项。假定从0开始,第0项为0。(n<=39)样例输入整数 n=5返回 5算法:(递推) O(n)这题的数据范围很小,我们直接模拟即可。当数据范围很大时,就需要采用其他方式了,可以参考 求解斐波那契数列的若干方法 。用两个变量滚动式得往后计算,a 表示第 n−1 项,b 表示第 n 项。则令 c=a+b表示第 n+1 项,然后让 a,b顺次往后移一位。时间复杂度分析总共需要计算 n次,所以时间复杂度是 O(n)。class So原创 2020-05-15 21:29:09 · 402 阅读 · 0 评论 -
用两个栈实现队列(八)
题目请用栈实现一个队列,支持如下四种操作:push(x) – 将元素x插到队尾;pop() – 将队首的元素弹出,并返回该元素;peek() – 返回队首元素;empty() – 返回队列是否为空;注意:你只能使用栈的标准操作:push to top,peek/pop from top, size 和 is empty;如果你选择的编程语言没有栈的标准库,你可以使用list或者deque等模拟栈的操作;输入数据保证合法,例如,在队列为空时,不会进行pop或者peek等操作;样例MyQu原创 2020-05-15 21:16:21 · 152 阅读 · 0 评论 -
二叉树的下一个节点(七)
题目给定一棵二叉树的其中一个节点,请找出中序遍历序列的下一个节点。注意:如果给定的节点是中序遍历序列的最后一个,则返回空节点;二叉树一定不为空,且给定的节点一定不是空节点;样例:假定二叉树是:[2, 1, 3, null, null, null, null], 给出的是值等于2的节点。则应返回值等于3的节点。算法分情况讨论1、如果当前节点存在右子树,那么右子树的最左侧节就是当前节点的后继节点。(例如F的后继节点是H)2、如果不存在右子树,就需要沿着father域向上查找,找到第一个是原创 2020-05-15 21:09:29 · 136 阅读 · 0 评论 -
从尾到头打印链表(五)
题目输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值。返回的结果用数组存储。样例输入:[2, 3, 5]返回:[5, 3, 2]算法先便利计算节点个数,再倒序存入数组,时间复杂的为O(n)./** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */clas原创 2020-05-14 12:20:59 · 93 阅读 · 0 评论 -
重建二叉树(六)
输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。注意:二叉树中每个节点的值都互不相同;输入的前序遍历和中序遍历一定合法;样例给定:前序遍历是:[3, 9, 20, 15, 7]中序遍历是:[9, 3, 15, 20, 7]返回:[3, 9, 20, null, null, 15, 7, null, null, null, null]返回的二叉树如下所示:39 2015 7算法前序遍历数组第一个元素为当前根,再中序遍历中查找这个元素,这个元素前面的元素就是左原创 2020-05-14 12:34:32 · 149 阅读 · 0 评论 -
替换空格(四)
题目请实现一个函数,把字符串中的每个空格替换成"%20"。你可以假定输入字符串的长度最大是1000。注意输出字符串的长度可能大于1000。样例输入:“We are happy.”输出:“We%20are%20happy.”class Solution { //双指针 时间复杂福O(n),不开辟新的空间,在原来的字符串上进行操作,空间复杂度较低 public static String replaceSpace3(StringBuffer str){ int sp原创 2020-05-14 12:16:01 · 99 阅读 · 0 评论 -
二维数组中的查找(三)
题目在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。样例输入数组:[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]如果输入查找数值为7,则返回true,如果输入查找数值为5,则返回false。算法第一种解法,暴力,不推荐,时间复杂度为O(n^2)public boolean Find(int target, int原创 2020-05-14 12:08:21 · 116 阅读 · 0 评论 -
不修改数组找出重复的数字(二)
题目给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。请找出数组中任意一个重复的数,但不能修改输入的数组。样例给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。返回 2 或 3。思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?算法(分治,抽屉原理) O(nlogn)这道题目主要应用了抽屉原理和分治的思想。抽屉原理:n+1 个苹果放在 n 个抽屉里,那么至少有一个抽屉中会放两个苹果。用在这个题目中就是,一共有 n+原创 2020-05-14 11:48:29 · 146 阅读 · 0 评论 -
找出数组中重复的数字(一)
算法(数组遍历) O(n)首先遍历一遍数组,如果存在某个数不在0到n-1的范围内,则返回-1。下面的算法的主要思想是把每个数放到对应的位置上,即让nums[i] = i。从前往后遍历数组中的所有数,假设当前遍历到的数是 nums[i]=x,那么:如果x != i && nums[x] == x,则说明 x 出现了多次,直接返回 x 即可;如果nums[x] != x,那我们就把 x 交换到正确的位置上,即 swap(nums[x], nums[i]),交换完之后如果nums[i原创 2020-05-14 11:23:51 · 305 阅读 · 0 评论