[TOC]笔记:qxlx 邮箱 qxlxiii@gmail.com
剑指offer
汇总
【数组】
3.数组中重复的数字
1.hashSet 2.数组下标法(鸽巢机制)
4.二维数组中的查找
1.暴力
2.线性查找 从右上角查找 小于列-- 大于行++
5.替换空格
1.stringbuffer
21. 调整数组顺序使奇数位于偶数前面
1.双指针 2.先分开 在合并
29.顺时针打印矩阵
左到右 上到下 右到左 下到上
39.数组中出现次数超过一半的数字
投票法
62.圆圈中最后剩下的数字
ans = (ans+m)%i
【二分查找】
11.旋转数组的最小数字【高频】
1.二分
53 - I. 在排序数组中查找数字 I
1.二分
53 - II. 0~n-1中缺失的数字
1.二分
57.和为S的两个数字
1.二分+双指针
【dp】
42.连续子数组的最大和
1.dp
49.丑树
1.dp
【分治】
33.二叉搜索树的后序遍历序列
1.分治 根据数组最后数据判断为当前的根节点,然后分别求左子树和右子树。
【二叉树】
7.重建二叉树
1.前序+中序 递归解决
26.树的子结构
1.递归调用 recur
27.二叉树的镜像
1.递归 2.栈结构
28.对称的二叉树
1.递归 判断左右子节点
32-1 从上到下打印二叉树-左右顺序
1,队列 和下面的类似
32-2 从上到下打印二叉树-层序遍历
1.队列 2.递归
32-3 从上到下打印二叉树-左右+逆序
1.队列 + flag 逆序标志
37.序列化二叉树
1.序列化 二叉树-》字符串 2.反序列化 字符串-》二叉树
55-1 二叉树的深度
1.dfs
55-2 平衡二叉树
1.dfs flag标志
68-1 二叉搜索树的最近公共祖先
1.根据BST的特点 DFS
68-2 二叉树的最近公共祖先
1.dfs 递归左右子节点
【栈-队列】
9.用两个栈实现队列【高频】
1.in out 两个栈 删除的三种情况
30.包含min函数的栈
1.data 和 min栈 保证minValue的值的变化
59.队列的最大值
1.两个双端队列 一个存储数据 一个存储最大值。
59.2 滑动窗口最大值 【高频】【面试之前AC】
1.双端队列
【递归-dp】
10-1.斐波那契额数列
1.递归 2.数组 3.dp
10-2.青蛙跳台阶【高频】
1.递归 2,记忆化
10-3 变态跳台阶
1.f(n) = 2^(n-1)
【回溯】
12.矩阵中的路径
1.回溯
13.机器人的路径
1.回溯 穷举所有路径 排除掉不符合条件的路径
34.二叉树中的合围某一值的路径 【面试之前必看】
1.回溯算法
38.字符串的排列 【面试之前必看】
1.回溯算法
【位运算】
15.二进制中的1的个数
1.n&1 n>>>1 2.n&=(n-1)
64.求1+2+…+n
1.&&判断
【字符串】
50.第一个只出现一次的字符
1.hash思想
58 - I. 翻转单词顺序
1.字符串 先分隔 在逆序拼接
58 - II. 左旋转字符串
1.先逆序前半部分 在逆序后半部分 最后逆序整个。
67.把字符串转换成整数
1.注意符号 以及溢出
【堆】
40.最小的k个数
1.堆 TOP(K)问题
41.数据流中的中位数
1.大顶堆 和 小顶堆 结合使用
【排序】
【dfs】
54.二叉搜索树的第K大节点
1.左中右为有序的递增序列 右中左就位递减序列 --k + dfs就可以
【链表】
6.从尾到头打印链表
1.栈 2.递归 3.头插法
18.删除链表的节点
1.迭代查找 2.快慢指针删除法 pre.next = fast.next
22.链表中倒数第K个节点
1.快慢指针 快指针走k步
24.反转链表
1.背下来
25.合并两个排序的链表
1.前置pre + 遍历 l1 l2
35.复杂链表的复制
1.hashMap存储Node 在依次遍历查找next 和 randomNode
52.两个链表的公共节点
1.两个指针 谁先走完 走对方的 直到相遇。
31.栈的压入、弹出序列面试题31. 栈的压入、弹出序列
难度中等27
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
``` 输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1] 输出:true 解释:我们可以按以下顺序执行: push(1), push(2), push(3), push(4), pop() -> 4, push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
```
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2] 输出:false 解释:1 不能在 2 之前弹出。
th:用栈模拟
time:O(n)
space:O(n)
//用stack存储pushed的数据 poped pop 如果相等stack.pop public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack stack = new Stack();
int index = 0;
//遍历pushed for(int i = 0,len = pushed.length ; i < len;i++){
stack.push(pushed[i]);
//如果pushed 和 popped 相等 stack pop while(!stack.isEmpty() && index < len && stack.peek() == popped[index]){
stack.pop();
index++;
}
}
//如果栈为null 是 return stack.isEmpty();
}
32-1 从上到下打印二叉树 ★面试题32 - I. 从上到下打印二叉树
难度中等10
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
例如: 给定二叉树: [3,9,20,null,null,15,7],
``` 3 / \ 9 20 / \ 15 7
```
返回:
[3,9,20,15,7]
th:一个队列存储遍历的节点,先存储root结点,从队列中取出来,如果不为null 将左子节点 和右子节点分别存储起来。依次循环遍历。先存储左子节点 在存储有子节点 。层序遍历。
time:O(n)
space:O(n)
//一个队列存储结点, //list存储值 public int[] levelOrder(TreeNode root) {
Queue queue = new LinkedList<>();
List list = new ArrayList<>();
queue.add(root);
while(!queue.isEmpty()){
int cnt = queue.size();
while(cnt-- > 0){
TreeNode t = queue.poll();
if(t == null){
continue;
}
list.add(t.val);
queue.add(t.left);//左子节点 queue.add(t.right);//右子节点 }
}
int [] ret = new int[list.size()];
for(int i=0;i
ret[i] = list.get(i);
}
return ret;
}
32-2 从上到下打印二叉树 II ★面试题32 - II. 从上到下打印二叉树 II
难度简单17
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
例如: 给定二叉树: [3,9,20,null,null,15,7],
``` 3 / \ 9 20 / \ 15 7
```
返回其层次遍历结果:
[ [3], [9,20], [15,7] ]
//递归 List> list = new ArrayList();
public List> levelOrder(TreeNode root) {
recur(root,0);
return list;
}
public void recur(TreeNode root,int k){
//终止条件 if(root != null){
if(list.size()<=k){
list.add(new ArrayList());
}
list.get(k).add(root.val);
recur(root.left,k+1);
recur(root.right,k+1);
}
}
2.队列+迭代
public List> levelOrder(TreeNode root) {
List> result = new ArrayList<>();
Queue queue = new LinkedList<>();
//添加根节点 queue.add(root);
while(!queue.isEmpty()){
List list = new ArrayList<>();
int cnt = queue.size();
while(cnt-- > 0){
TreeNode t = queue.poll();//弹出首节点 if(t == null){
continue;
}
list.add(t.val);
queue.add(t.left);
queue.add(t.right);
}
if(list.size() != 0){
result.add(list);
}
}
return result;
}
32-3 从上到下打印二叉树 III ★面试题32 - III. 从上到下打印二叉树 III
难度中等15
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
例如: 给定二叉树: [3,9,20,null,null,15,7],
``` 3 / \ 9 20 / \ 15 7
```
返回其层次遍历结果:
[ [3], [20,9], [15,7] ]
思路:其实和前边的相同 添加一个标志位 第一次不需要反转,第二次需要反转,第三次不需要反转,依序就可以反转。
public List> levelOrder(TreeNode root) {
List> result = new ArrayList<>();
Queue queue = new LinkedList<>();
queue.add(root);
boolean reverse = false;//设置是否需要反转 while(!queue.isEmpty()){
List list = new ArrayList<>();
int cnt = queue.size();
while(cnt-- > 0){
TreeNode t = queue.poll();
if(t == null){
continue;
}
list.add(t.val);
queue.add(t.left);
queue.add(t.right);
}
if(reverse){
Collections.reverse(list);
}
reverse = !reverse;
if(list.size() != 0)
result.add(list);
}
return result;
}
33.二叉搜索树的后序遍历序列 ★面试题33. 二叉搜索树的后序遍历序列
难度中等39
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5 / \ 2 6 / \ 1 3
示例 1:
输入: [1,6,3,2,5] 输出: false
示例 2:
输入: [1,3,2,6,5] 输出: true
1.递归-分治
时间复杂度:O(n^2) 每次调用 recur(i,j) 减去一个根节点,因此递归占用 O(N) ;最差情况下(即当树退化为链表),每轮递归都需遍历树所有节点,占用 O(N)
空间复杂度 O(N) : 最差情况下(即当树退化为链表),递归深度将达到 NN 。
//递归 --分治 //后序遍历 左右根 特点左子树节点的值均小于根节点 右子树节点的值均大于根节点 //由此可知 数组中最后一个元素就是跟节点。通过两次for 找到第一个大于root.val的值 //将数组划分成 小于根节点的 和 大于根节点的 两部分,通过递归调用 就可以逐一比较。 public boolean verifyPostorder(int[] postorder) {
if(postorder == null || postorder.length == 0){
return true;
}
return verify(postorder,0,postorder.length-1);
}
public boolean verify(int [] postorder,int first,int last){
if(last-first<=1){ //如果数组中只有一个元素 说明是有序的。 return true;
}
int curIndex = first;
int rootVal = postorder[last];//根节点 //找到第一个大于根节点的值 下标 while(curIndex < last && postorder[curIndex]<=rootVal ){
curIndex++;
}
for(int i = curIndex;i
//如果右子节点小于根节点 不是BST if(postorder[i]
return false;
}
}
return verify(postorder,first,curIndex-1) && verify(postorder,curIndex,last-1);
}
34.二叉树中和为某一值的路径 ★面试题34. 二叉树中和为某一值的路径
难度中等27
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例: 给定如下二叉树,以及目标和 sum = 22,
``` 5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1
```
返回:
[ [5,4,11,2], [5,8,4,5] ]
//回溯 // 递归解决 终止条件节点为null 返回 // 当target=0 并且左右子节点都为null的时候 进行数据添加。 // 否则的话 直接递归左右子节点。 List> ret = new ArrayList<>();
public List> pathSum(TreeNode root, int sum) {
pathSum(root,sum,new ArrayList());
return ret;
}
public void pathSum(TreeNode root,int target,List path){
if(root == null){
return;
}
path.add(root.val);
target -= root.val;
if(target == 0 && root.left == null && root.right == null){
//这里为什么要创建一个List 因为如果直接添加path.是添加的引用,后期的修改会影响path的数据 ret.add(new ArrayList<>(path));
}else{
pathSum(root.left,target,path);
pathSum(root.right,target,path);
}
//回溯,状态重置,只添加不删除就是错的路径了,因为整个递归过程用的是同一个收集器path path.remove(path.size()-1);
}
35.复杂链表的复制 ★面试题35. 复杂链表的复制
难度中等33
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 1:
``` 输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]] 输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
```
示例 2:
``` 输入:head = [[1,1],[2,1]] 输出:[[1,1],[2,1]]
```
示例 3:
``` 输入:head = [[3,null],[3,0],[3,null]] 输出:[[3,null],[3,0],[3,null]]
```
示例 4:
输入:head = [] 输出:[] 解释:给定的链表为空(空指针),因此返回 null。
public Node copyRandomList(Node head) {
if(head == null) return null;
Node node = head;
Map map = new HashMap();
//1.loop copy all node in map while(node!=null){
map.put(node,new Node(node.val));
node = node.next;
}
node = head;
//2.loop assign next and random pointer while(node!=null){
map.get(node).next = map.get(node.next);//next map.get(node).random = map.get(node.random);
node = node.next;
}
return map.get(head);
}
36. 二叉搜索树与双向链表难度中等31
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
//time : O(n) //space : O(n) Node pre,head;
//1.树的有序遍历为中序遍历。 //2.递归左子树后 将 节点的pre 和 right left关联 //3.dfs完毕后 将首位节点相连接。 public Node treeToDoublyList(Node root) {
if(root == null)
return null;
dfs(root);
//将首位指针相连接 head.left = pre;
pre.right = head;
return head;
}
void dfs(Node cur){
//1.终止条件 if(cur == null)
return;
//2.递归左子树 dfs(cur.left);
//3. pre节点 和 cur的 next 和 pre 相连接。 // 等价于 node.right为next节点 node.left为pre节点 // 如果是pre==null 说明第一次遍历 head = cur if(pre != null)
pre.right = cur;
else
head = cur;
cur.left = pre;
pre = cur;
// 递归右子树 dfs(cur.right);
}
37. 序列化二叉树 ★剑指 Offer 37. 序列化二叉树
难度困难44
请实现两个函数,分别用来序列化和反序列化二叉树。
示例:
``` 你可以将以下二叉树:
1
/ \ 2 3 / \ 4 5
序列化为 "[1,2,3,null,null,4,5]" ```
/** 思路:1、序列化 创建一个builder 用 , 进行分隔 如果是null 则用 x 进行标记。Tree -> 字符串2、反序列化 使用双端队列进行添加搜索元素,x 说明是一个Null 跳过,否则的话 就直接 递归查找递归root.left 和 root.right*/
//构建字符串 private static final String spliter = ",";
private static final String NN = "x";
// Encodes a tree to a single string. // 构建二叉树序列化-》BST>String public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
buildString(root,sb);
return sb.toString();
}
private void buildString(TreeNode root,StringBuilder sb){
//root == null if(root == null){ // x, sb.append(NN).append(spliter);
}else{
sb.append(root.val).append(spliter);
buildString(root.left,sb);
buildString(root.right,sb);
}
}
// Decodes your encoded data to tree. public TreeNode deserialize(String data) {
Deque nodes = new LinkedList<>();
nodes.addAll(Arrays.asList(data.split(spliter)));
return buildTree(nodes);
}
public TreeNode buildTree(Deque nodes){
String val = nodes.remove();
if(val.equals(NN)) return null;
else{
TreeNode node = new TreeNode(Integer.valueOf(val));
node.left = buildTree(nodes);
node.right = buildTree(nodes);
return node;
}
}
38.字符串的排列 ★面试题38. 字符串的排列
难度中等38
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc" 输出:["abc","acb","bac","bca","cab","cba"]
回溯
//dfs time :O(N!) space : O(N^2) List ret = new LinkedList<>();
char [] ch;
public String[] permutation(String s) {
if(s == null){
return new String[0];
}
ch = s.toCharArray();
dfs(0);
return ret.toArray(new String[ret.size()]);
}
private void dfs(int x){
//终止条件 if(x == ch.length-1){
ret.add(String.valueOf(ch));
return;
}
HashSet hashSet = new HashSet<>();
for(int i=x;i
//去重 -》剪枝操作 if(hashSet.contains(ch[i])) continue;
hashSet.add(ch[i]);
//交换 swap(i,x);
dfs(x+1);
//撤回交换 swap(x,i);
}
}
private void swap(int x,int i){
char tmp = ch[x];
ch[x] = ch[i];
ch[i] = tmp;
}
39.数组中出现次数超过一半的数字 ★面试题39. 数组中出现次数超过一半的数字
难度简单19
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2
1.哈希表
//哈希表 //1.将数组中元素作为key value为出现的次数 //2.遍历哈希表中所有key 找出出现最大的次数 // time :O(n) 遍历数组n次 // space : 将元素存储到哈希表中所占用的空间 public int majorityElement(int[] nums) {
if(nums == null || nums.length == 0){
return -1;
}
Map results = new HashMap<>();
for(int i=0;i
if(!results.containsKey(nums[i]))
results.put(nums[i],1);
else
results.put(nums[i],results.get(nums[i])+1);
}
Map.Entry majorityEntry = null;
for (Map.Entry entry : results.entrySet()) {
if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {
majorityEntry = entry;
}
}
return majorityEntry.getKey();
}
2.排序
// time : (OlogN) 数组排序需要的时间 // space : (logN) 数组排序需要的外部存储空间 语言自身的排序空间是OlogN // ps : 当数组中有大于一半以上的数据时,如果将数组进行排序,那么中间下标位置一定是众数的下标。 // 无论是偶数 还是 奇数。 // 众数 : 在一组数据中出现次数最多的数字。 public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
3.Boyer-Moore 投票算法
// Boyer-Moore 投票算法 // 核心思想 // 如果候选人不是maj 则 maj,会和其他非候选人一起反对 会反对候选人,所以候选人一定会下台(maj==0时发生换届选举) // 如果候选人是maj , 则maj 会支持自己,其他候选人会反对,同样因为maj 票数超过一半,所以maj 一定会成功当选 // time : O(n) 一次loop array // space : O(1) public int majorityElement(int[] nums) {
if(nums == null || nums.length == 0) return -1;
int count = 0;
Integer candidate = null;
for(int num : nums){
if(count == 0){
candidate = num;//更换候选人 }
//count += ((num == candidate) ? 1 : -1); count += (num == candidate) ? 1 : -1;
}
return candidate;
}
40.最小的K个数 ★面试题40. 最小的k个数
难度简单76
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
``` 输入:arr = [3,2,1], k = 2 输出:[1,2] 或者 [2,1]
```
示例 2:
输入:arr = [0,1,2,1], k = 1 输出:[0]
Top(k) 问题的解法 一般可以用堆 或者 类似快排的思想。
1.堆
// time : O(k)// space : O(nlogk)//使用一个堆 固定大小 将数组中所有的元素 全部添加到堆中 根据一定的条件 才可以添加 删除// 最后剩余的就是要的数据public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0) {
return new int[0];
}
// 使用一个最大堆(大顶堆) // Java 的 PriorityQueue 默认是小顶堆,添加 comparator 参数使其变成最大堆 Queue heap = new PriorityQueue<>(k, (i1, i2) -> Integer.compare(i2, i1));
for (int e : arr) {
// 当前数字小于堆顶元素才会入堆 if (heap.isEmpty() || heap.size() < k || e < heap.peek()) {
heap.offer(e);
}
if (heap.size() > k) {
heap.poll(); // 删除堆顶最大元素 }
}
// 将堆中的元素存入数组 int[] res = new int[heap.size()];
int j = 0;
for (int e : heap) {
res[j++] = e;
}
return res;
}
41.数据流中的中位数 ★面试题41. 数据流中的中位数
难度困难26
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:
输入: ["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"] [[],[1],[2],[],[3],[]] 输出:[null,null,null,1.50000,null,2.00000]
Queue A,B;
/** initialize your data structure here. */
public MedianFinder() {
A = new PriorityQueue<>();//小顶堆 B = new PriorityQueue<>((x,y)->(y-x));//大顶堆 }
//保证 中间数 一个子啊大顶堆 一个在小顶堆 public void addNum(int num) {
if(A.size()!=B.size()){
A.add(num);
B.add(A.poll());
}else{
B.add(num);
A.add(B.poll());
}
}
public double findMedian() {
return A.size() != B.size() ? A.peek():(A.peek()+B.peek())/2.0;
}
42.连续子数组的最大和 ★面试题42. 连续子数组的最大和
难度简单42
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
public int maxSubArray(int[] nums) {
if(nums.length == 0 || nums == null){
return -1;
}
int greatestSum = Integer.MIN_VALUE;
int sum = 0;
for(int num: nums){
//如果负数 从新开始 不断累加 求出最大值。 sum = sum< 0 ? num : sum + num;
greatestSum = Math.max(greatestSum,sum);
}
return greatestSum;
}
43.1~n整数中1出现的次数剑指 Offer 43. 1~n整数中1出现的次数
难度中等70
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12 输出:5
示例 2:
输入:n = 13 输出:6
public int countDigitOne(int n) {
int res = 0;
for(long m = 1 ; m<=n ;m*=10){
long a = n/m, b = n % m;
res+=(a+8)/10*m+(a%10 == 1 ? b+1:0);
}
return res;
}
48.最长不含重复字符的子字符串面试题48. 最长不含重复字符的子字符串
难度中等31
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
``` 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
```
示例 2:
``` 输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
```
示例 3:
输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
47.礼物的最大价值面试题47. 礼物的最大价值
难度中等34
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
示例 1:
输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 12 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
//采用dp 如果是递归的话 会出现很多重复性计算,复杂度比较高 //而dp每次选取最优的解,time : O(n*m) public int maxValue(int[][] grid) {
if(grid.length == 0 || grid == null || grid[0].length == 0){
return 0;
}
int n = grid[0].length;
int [] dp = new int [n];
for(int [] g : grid){
dp[0]+=g[0];
for(int i=1;i
dp[i] = Math.max(dp[i],dp[i-1])+g[i];
}
}
return dp[n-1];
}
49.丑数 ★面试题49. 丑数
难度中等26
我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:1 是丑数。
n 不超过1690。
//dp问题 //思路 我们知道丑数是可以被2 or 3 or 5 连续整除的。如果==1 就是丑数 // 初始化 dp[0] = 1; 1*2=2 1*3=3 1*5 =5 min = 2 // dp[1] = 2 a=1; // 2*2 = 4 1*3 = 3 1*5 = 5 min = 3 // dp[2] = 3 b=3; //如此反复进行计算 dp方程 : dp[Math.min(n2,Math.min(n3,n5))] //time :O(n) //space : O(n) public int nthUglyNumber(int n) {
if(n<6) return n;
int [] dp = new int [n];
dp[0] = 1;
int a = 0, b = 0, c = 0;
for(int i=1;i
int n2 = dp[a] * 2,n3 = dp[b] * 3, n5 = dp[c] * 5;
dp[i] = Math.min(n2,Math.min(n3,n5));
if(dp[i] == n2) a++;
if(dp[i] == n3) b++;
if(dp[i] == n5) c++;
}
return dp[n-1];
}
50.第一个出现一次的字符面试题50. 第一个只出现一次的字符
难度简单14
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。
示例:
``` s = "abaccdeff" 返回 "b"
s = "" 返回 " " ```
1.哈希表
/**1.哈希表a.使用hashmap来存储 key为字符,value为true or false1)true -> 该字符为一个2) false -> 该字符不为一个b.loop map 找到第一个数量为1的字符 returnc.直接 return '';time : O(n)space : O(n)*/
public char firstUniqChar(String s) {
Map dataMap = new HashMap<>();
char [] chs = s.toCharArray();
for(char ch: chs){
dataMap.put(ch,!dataMap.containsKey(ch));
}
for(char ch : chs){
if(dataMap.get(ch))
return ch;
}
return ' ';
}
2.有序hash表
/** 2.有序hash表思路:hash表有去重的特点,当数据比较大的时候 我们直接从hash中查找第一个value为true的值就可以了。time : O(n) 而前者为O(2n)*/
public char firstUniqChar(String s) {
Map dic = new LinkedHashMap<>();
char[] sc = s.toCharArray();
for(char c : sc)
dic.put(c, !dic.containsKey(c));
for(Map.Entry d : dic.entrySet()){
if(d.getValue()) return d.getKey();
}
return ' ';
}
3.数组hash表
public char firstUniqChar(String s) {
if(s==null||"".equals(s)){
return ' ';
}
int counts[] = new int[256];
for(int i = 0;i
counts[s.charAt(i)]++;
}
for(int i = 0;i
if(counts[s.charAt(i)]==1){
return s.charAt(i);
}
}
return ' ';
}
51.数组中的逆序对面试题51. 数组中的逆序对
难度困难164
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4] 输出: 5
52.两个链表的第一个公共节点 ★面试题52. 两个链表的第一个公共节点
难度简单37
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
``` 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Reference of the node with value = 8 输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
```
示例 2:
``` 输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Reference of the node with value = 2 输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
```
示例 3:
``` 输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 解释:这两个链表不相交,因此返回 null。
```
注意:如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
/** 1.双指针--链表一般多考虑双指针解法思路: A和B先走 谁先走完 走对方的路,直到相遇如果没有交叉点,当A为null B也为null 返回null 符合time : O(m+n)space : O(1)*/
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode listA = headA,listB = headB;
while(listA != listB){
listA = listA == null ? headB : listA.next;
listB = listB == null ? headA : listB.next;
}
return listA;
}
53.在排序数组中查找数字 I★面试题53 - I. 在排序数组中查找数字 I
难度简单23
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8 输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6 输出: 0
1.二分查找
//search问题 先O(n)>二分 //思路 使用二分查找右边界 和 左边界 right-left-1 //time : O(logn) //space : O(1) public int search(int[] nums, int target) {
return helper(nums,target)-helper(nums,target-1);
}
public int helper(int [] nums,int tar){
int i = 0, j = nums.length-1;
while(i<=j){
int m = i+(j-i)/2;
if(nums[m]<=tar) i = m+1;
else j = m-1;
}
return i;
}
53 - II. 0~n-1中缺失的数字★面试题53 - II. 0~n-1中缺失的数字
难度简单16
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
``` 输入: [0,1,3] 输出: 2
```
示例 2:
输入: [0,1,2,3,4,5,6,7,9] 输出: 8
//有序数组 考虑使用二分查找 // 求出midNum // time : O(logn) // space : O(1) public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;
while(i<=j){
int m = (i+j)/2;
if(nums[m]==m) i = m+1; //nums[m] == m 从0开始 所以下标值对应m else
j = m -1 ;
}
return i;
}
54.二叉树的第K大节点 ★面试题54. 二叉搜索树的第k大节点
难度简单21
给定一棵二叉搜索树,请找出其中第k大的节点。
示例 1:
输入: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \ 2 输出: 4
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3 5 / \ 3 6 / \ 2 4 / 1 输出: 4
//中序遍历为顺序的 右根左就位 从高到低 k记录的是第几大元素。当k==0就可以直接返回。 int res,k;
public int kthLargest(TreeNode root, int k) {
if(root == null) return -1;
this.k = k;
dfs(root);
return res;
}
private void dfs(TreeNode root){
if(root == null) return;
dfs(root.right);
if(k<0) return;
if(--k == 0) res = root.val;
dfs(root.left);
}
55 - I. 二叉树的深度 ★剑指 Offer 55 - I. 二叉树的深度
难度简单27
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3 / \ 9 20 / \ 15 7
dfs
public int maxDepth(TreeNode root) {
return root == null ? 0 : Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
55.-II 平衡二叉树 ★面试题55 - II. 平衡二叉树
难度简单19
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3 / \ 9 20 / \ 15 7
返回 true 。 示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
``` 1 / \ 2 2 / \ 3 3 / \ 4 4
```
返回 false 。
private boolean isBalanced = true;//默认为true
public boolean isBalanced(TreeNode root) {
dfs(root);
return isBalanced;
}
private int dfs(TreeNode root){
if(root == null || !isBalanced){
return 0;
}
int left = dfs(root.left); //计算左子节点长度 int right = dfs(root.right);//计算右子节点长度 if(Math.abs(left-right)>1){//如果左右子节点长度差大于1 则为false isBalanced = false;
}
return 1+Math.max(left,right);
}
57.和为s的两个数字 ★面试题57. 和为s的两个数字
难度简单18
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
``` 输入:nums = [2,7,11,15], target = 9 输出:[2,7] 或者 [7,2]
```
示例 2:
输入:nums = [10,26,30,31,47,60], target = 40 输出:[10,30] 或者 [30,10]
二分查找
//二分查找 binary search // time : O(n) // space : O(1) public int[] twoSum(int[] nums, int target) {
int i = 0, j = nums.length-1;
while(i
if(nums[i]+nums[j]
else if(nums[i]+nums[j]>target) j--;
else if(nums[i]+nums[j] == target){
return new int[]{nums[i],nums[j]};
}
}
return new int [0];
}
57 - II. 和为s的连续正数序列面试题57 - II. 和为s的连续正数序列
难度简单98
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
``` 输入:target = 9 输出:[[2,3,4],[4,5]]
```
示例 2:
输入:target = 15 输出:[[1,2,3,4,5],[4,5,6],[7,8]]
双指针
//双指针 //这类属于窗口问题,i j分别指向前后 当sum小于target j++ sum大于target i++ 不断缩减区 间。 time : O(n) space : O(n) public int[][] findContinuousSequence(int target) {
if(target<0) return new int [0][0];
int i = 1;//慢指针 int j = 1;//快指针 int sum = 0;
List result = new ArrayList<>();
while(i<=target/2){
if(sum
j++;
}else if(sum > target){//如果大于i++ sum-=i;
i++;
}else{
int [] arr = new int [j-i];
for(int k=i;k
arr[k-i] = k;
}
result.add(arr);
sum-=i;
i++;
}
}
return result.toArray(new int[result.size()][]);
}面试题58 - I. 翻转单词顺序
难度简单13
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 1:
``` 输入: "the sky is blue" 输出: "blue is sky the"
```
示例 2:
``` 输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
```
示例 3:
输入: "a good example" 输出: "example good a" 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
//time : O(n) space : O(m) //按照 " " 划分数组 逆序拼接到strs 如果包含 "" 跳过。 public String reverseWords(String s) {
String [] str = s.split(" ");
String strs = "";
//注意split后多出的是空的字符串数组元素即("")而非(" ") for(int i=str.length-1;i>=0;i--){
if(!str[i].equals("")){
strs+=str[i]+" ";
}
}
return strs.trim();
}
58 - II. 左旋转字符串★面试题58 - II. 左旋转字符串
难度简单24
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
``` 输入: s = "abcdefg", k = 2 输出: "cdefgab"
```
示例 2:
``` 输入: s = "lrloseumgh", k = 6 输出: "umghlrlose"面试题58 - II. 左旋转字符串 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2 输出: "cdefgab" 示例 2:
输入: s = "lrloseumgh", k = 6 输出: "umghlrlose" ```
// time : O(n) space : O(1) // 思路 abcd n = 2 ab >ba cd > dc badc cdab public String reverseLeftWords(String s, int n) {
if(s.length() < n) return s;
char [] ch = s.toCharArray();
//转换前半部分 reverse(ch,0,n-1);
//转换后半部分 reverse(ch,n,s.length()-1);
//转换整体 reverse(ch,0,s.length()-1);
return new String(ch);
}
private void reverse(char []ch,int i,int j){
while(i
swap(ch,i++,j--);
}
}
//交换 private void swap(char [] ch,int i,int j){
char tmp = ch[i];
ch[i] = ch[j];
ch[j] = tmp;
}
59.-1 滑动窗口的最大值 ★面试题59 - I. 滑动窗口的最大值
难度简单41
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
``` 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5,6,7] 解释:
滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7 ```
提示:
你可以假设 k总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
1.穷举法
//1.穷求法 public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0 || nums == null || k == 0)
return new int[0];
//移动的次数为 length-k+1 int [] array = new int [nums.length-k+1];
for(int i=0;i
int maxNum = Integer.MIN_VALUE;
for(int j = i;j
maxNum = Math.max(maxNum,nums[j]);
}
array[i] = maxNum;
}
return array;
}
2.双端队列
private ArrayDeque arrayDeque = new ArrayDeque<>();
private int [] nums;
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || nums.length * k == 0){
return new int [0];
}
this.nums = nums;
int n = nums.length;
int [] arr = new int [n-k+1];
int maxIndex = 0;
for(int i=0;i
cleanDeq(i,k);
arrayDeque.addLast(i);
maxIndex = nums[maxIndex] > nums[i] ? maxIndex : i;
}
arr[0] = nums[maxIndex];
for(int i=k;i
cleanDeq(i,k);
arrayDeque.addLast(i);
arr[i-k+1] = nums[arrayDeque.getFirst()];
}
return arr;
}
private void cleanDeq(int i,int k){
if(!arrayDeque.isEmpty()&&arrayDeque.getFirst() == i-k){
arrayDeque.removeFirst();
}
while(!arrayDeque.isEmpty()&&nums[i]>nums[arrayDeque.getLast()]){
arrayDeque.removeLast();
}
}
59 - ||队列的最大值 ★面试题59 - II. 队列的最大值
难度中等92
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
``` 输入: ["MaxQueue","push_back","push_back","max_value","pop_front","max_value"] [[],[1],[2],[],[],[]] 输出: [null,null,null,2,1,2]
```
示例 2:
输入: ["MaxQueue","pop_front","max_value"] [[],[],[]] 输出: [null,-1,-1]
双端队列
Deque que;
Deque deq;
public MaxQueue() {
que = new LinkedList<>();//add and dele deq = new LinkedList<>();//find max value }
public int max_value() {
return que.size()>0 ? deq.peek() : -1;
}
public void push_back(int value) {
que.offer(value);
while(deq.size()>0 && deq.peekLast()
deq.pollLast();
}
deq.offer(value);
}
public int pop_front() {
int tmp = que.size()>0? que.poll() : -1;
//deq size > 0 and tmp 包含 maxValue 返回 deq的最大值 if(deq.size()>0 && tmp == deq.peek()){
deq.poll();
}
return tmp;
}面试题60. n个骰子的点数
难度简单46
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
示例 1:
``` 输入: 1 输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
```
示例 2:
输入: 2 输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.
61.扑克牌中的顺子面试题61. 扑克牌中的顺子
难度简单24
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
示例 1:
输入: [1,2,3,4,5] 输出: True
示例 2:
输入: [0,0,1,2,5] 输出: True
/*a.最大元素-nums[joker] 小于 5 不可以匹配,或者当前和后一个元素相等 不能成顺子*/
public boolean isStraight(int[] nums) {
int joker = 0;
Arrays.sort(nums);
for(int i=0;i<4;i++){
if(nums[i]==0){
joker++;
}else if(nums[i]==nums[i+1]){
return false;
}
}
return nums[4]-nums[joker] < 5;
}面试题62. 圆圈中最后剩下的数字
难度简单139
0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3 输出: 3
示例 2:
输入: n = 10, m = 17 输出: 2
public int lastRemaining(int n, int m) {
int res=0;
for(int i=2;i<=n;i++)
res=(res+m)%i;
return res;
}
64.求1+2+…+n ★面试题64. 求1+2+…+n
难度中等66
求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
``` 输入: n = 3 输出: 6
```
示例 2:
输入: n = 9 输出: 45
public int sumNums(int n) {
int sum = n;
boolean b = (n > 0) && ((sum += sumNums(n - 1)) > 0);
return sum;
}
63.股票的最大利润面试题63. 股票的最大利润
难度中等21
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:
输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
public int maxProfit(int[] prices) {
int n = prices.length;
if(n<2) return 0;
int dp_i_0 = 0, dp_i_1 = Integer.MIN_VALUE;
for(int i=0;i
dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]);
dp_i_1 = Math.max(dp_i_1,-prices[i]);
}
return dp_i_0;
}
65.不用加减乘除做加法面试题65. 不用加减乘除做加法
难度简单26
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
示例:
输入: a = 1, b = 1 输出: 2
/** 可以将元素进行划分 相加 和 进位操作。^ 相加 &进位*/
public int add(int a, int b) {
int tmp = 0;
while(a!=0){
tmp = a ^ b; // 011 ^ 1000 1011 a = (a & b) << 1; // 1011 10110 b = tmp;
}
return b;
}
66.构建乘积数组面试题66. 构建乘积数组
难度简单16
给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
示例:
输入: [1,2,3,4,5] 输出: [120,60,40,30,24]
public int[] constructArr(int[] a) {
if(a.length == 0) return new int[0];
int length = a.length;
int [] b = new int [length];
b[0] = 1;
int tmp = 1;
for(int i=1;i
b[i] = b[i-1] * a[i-1];
}
for(int i=length-2;i>=0;i--){
tmp *= a[i+1];
b[i] *= tmp;
}
return b;
}
67.把字符串转换成整数 ★面试题67. 把字符串转换成整数
难度中等9
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42" 输出: 42
public int strToInt(String str) {
char [] chs = str.trim().toCharArray();
if(chs.length == 0) return 0;
int res = 0,budry = Integer.MAX_VALUE/10;
int i=1,sign = 1;
if(chs[0] == '-') sign = -1;
else if(chs[0] !='+') i = 0;
for(int j=i;j
if(chs[j] '9') break;
if(res > budry || res == budry && chs[j] > '7')
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
res = res * 10 +(chs[j] - '0');
}
return sign * res;
}面试题68 - I. 二叉搜索树的最近公共祖先
难度简单36
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 输出: 6 解释: 节点 2 和节点 8 的最近公共祖先是 6。
//1.BST的特点是左子树的值小于root.val 右子树的值大于root.val //2.如果p.val < root.val && q.val < root.val 从左子树递归查找 //3.如果p.val > root.val && q.val > root.val 从右子树递归查找 //4.否则的话 返回root public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(p.val < root.val && q.val < root.val){
return lowestCommonAncestor(root.left,p,q);
}
if(p.val > root.val && q.val > root.val){
return lowestCommonAncestor(root.right,p,q);
}
return root;
}面试题68 - II. 二叉树的最近公共祖先
难度简单77
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 输出: 3 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
//1. root 为null p 或者 q等于root 返回根节点 //2.递归查找左子节点 左子节点替换成root //3.递归查找右子节点 右子节点替换成root //4.a.left and right为null 返回root // b.left == null or right == null 返回 right or left public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
return left == null ? right : right == null ? left : root;
}