题库来源:牛客网
《7天刷完剑指offer》系列只提供解题思路,代码实现见牛客网
数值的整数次方
题目:
思路
解法1: 暴力法。exponent个base相乘,循环exponent次。时间复杂度:O(n),空间复杂度:O(1)
解法2: 快速幂。已知 b a s e e = ( b a s e e 2 ) 2 base^e = (base^{\frac{e}{2}})^2 basee=(base2e)2, 如果e 是偶数。当e是奇数, b a s e e = ( b a s e e 2 ) 2 ∗ b a s e base^e = (base^{\frac{e}{2}})^2 * base basee=(base2e)2∗base。注意当e是负数的时候, b a s e e = ( 1 b a s e ) − e base^e = (\frac{1}{base})^{-e} basee=(base1)−e
1)递归:每次折半递归
2)非递归:利用二进制的特性,假设求 3 9 3^9 39, 9的二进制是1001。则 3 9 = 1 ∗ 3 + 0 ∗ 3 2 + 0 ∗ 3 4 + 1 ∗ 3 8 3^9 = 1 * 3 + 0* 3^2 + 0 * 3^4 + 1 * 3^8 39=1∗3+0∗32+0∗34+1∗38。依次计算 3 , 3 2 , 3 4 , 3 8 3, 3^2, 3^4, 3^8 3,32,34,38, 只有对应的二进制位数是1的时候,把幂加到结果里面。
调整数组顺序使奇数位于偶数前面
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路
解法1: 暴力解法。创建一个新数组,遍历一遍原数组,把奇数加到新数组里面,然后再遍历一遍,把偶数加进去。把新数组的值赋给原数组。
解法2: in-place,双指针。指针i 表示下一个奇数可以放的位置,指针j 用于获得下一个奇数的值。
指针j的移动方式
- 遇到偶数,j++
- 遇到奇数,先把[i, j - 1]内的数据整体后移,然后把j位置的数插入到指针i 位置。接着i++。
- 直到数据遍历结束
链表中倒数第k个结点
题目:输入一个链表,输出该链表中倒数第k个结点。
思路:快慢指针。两个指针起始指向head, 然后移动快指针k步,接着快慢指针一起移动,直到快指针指向null。 注意,当k > 链表长度,则返回null。
反转链表
题目:输入一个链表,反转链表后,输出新链表的表头。
思路
解法1: 找个中间值存放链表中的节点,比如stack,然后依次反转
解法2: in-place反转。通过pre, curr, nxt三个节点指针,记录反转前后的关系。
pre表示反转后的最后一个节点
curr表示反转前的一个节点
nxt表示curr反转前的下一个节点
nxt = curr.next;
curr.next = pre;
pre = curr;
curr = nxt;
合并两个排序链表
题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路
解法1: 迭代。新建一个指针head表示合并后链表的头指针,新建一个指针curr表示当前链表上一次合并后的指针。那么curr.next 指向两个链表中较小的节点。然后链表的指针和curr分别指向下一个。
解法2:递归。递归返回的值是当前合并两个链表的头节点。递归的过程如果list1.val ≤ list2.val, 那么list1.next = merge(list1.next, list2)
树的子结构
题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路:
如果A根节点 = B根节点,判断A的左子树是否等于B的左子树,A的右子树是否等于B的右子树。
如果A根节点 != B根节, 判断B是不是A的左子树的子结构 或者B是不是A的右子树的子结构
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1 == null || root2 == null) return false;
return root1.val == root2.val && isEqual(root1.left, root2.left) && isEqual(root1.right, root2.right)
|| HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
}
public boolean isEqual(TreeNode root1, TreeNode root2){
if(root2 == null) return true;
if(root1 == null) return false;
return root1.val == root2.val &&
isEqual(root1.left, root2.left) && isEqual(root1.right, root2.right);
}
}
二叉树镜像
题目:操作给定的二叉树,将其变换为源二叉树的镜像。
比如: 源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
思路
递归,把根节点的左右子树交换一下,交换时用一个临时变量存放其中一个子树。
顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路
顺时针打印的规律
从左到右打印
从上到下打印
从右到左打印
从下到上打印
然后矩阵往里一圈,继续按照这个规律打印
定义四个变量确定打印范围up, down, left, right
从左到右打印。打印left~ right内的数,打印完up++,表示此行打印完不再使用,同时判断此时up有没有超出down
从上到下打印。打印up~down内的数,打印完right- -, 表示此列不再打印。判断left有没有超出right
从右到左打印。打印right ~ left内的数,打印完down- -, 表示此行不再打印。判断up有没有超出down
从下到上打印。打印down ~ up内的数,打印完left++,表示此列不再打印,判断left有没有超出right。
包含min函数的栈
题目
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路:
这道题用Java的Stack类实现栈。利用了Stack类的方法比如
.push():往栈添加元素
.pop():删除栈顶元素,并且返回该元素
.peek():返回栈顶元素,但不删除此元素
.empty():判断栈是否为空
这道题目考察重点是返回栈内最小元素,时间复杂度应为O(1)。元素每次入栈和出栈后都能够获得当前栈的最小元素。因此要借助一个辅助栈来存放最小值。比如
stack = [4, 6, 2, 3]
min_stack = [4, 4, 2, 2]
下一个入栈元素5,5 > min_stack栈顶元素2,则5入栈后,最小的元素还是2。此时
stack = [4, 6, 2, 3, 5]
min_stack = [4, 4, 2, 2, 2]
如果此时5出栈,min_stack对应的2也出栈,此时
stack = [4, 6, 2, 3]
min_stack = [4, 4, 2, 2]