剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
这题一开始以为可以使用位运算,但是发现不能,浪费了时间
class Solution {
public int findRepeatNumber(int[] nums) {
int res=0;
Arrays.sort(nums);
for(int i=1;i<nums.length;i++){
if(nums[i]==nums[i-1]){
return nums[i];
}
}
return 0;
}
}
//set
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set=new HashSet<>();
for(int num:nums){
if(set.contains(num)){
return num;
}
set.add(num);
}
return -1;
}
}
剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右 非递减 的顺序排序,每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[ [1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30] ]
给定 target = 5
,返回 true
。
给定 target = 20
,返回 false
。
//matrix[0].length放while里面不然matrix为[]时会空指针
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i=matrix.length-1;
int j=0;
while(i>=0&&j<matrix[0].length){
if(matrix[i][j]>target) i--;
else if(matrix[i][j]<target) j++;
else if(matrix[i][j]==target) return true;
}
return false;
}
}
剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
示例 1:
输入:s = "We are happy." 输出:"We%20are%20happy."
class Solution {
public String replaceSpace(String s) {
StringBuilder sb=new StringBuilder();
for(int i=0;i<s.length();i++){
if(s.charAt(i)==' '){
sb.append("%20");
}else{
sb.append(s.charAt(i));
}
}
return sb.toString();
}
}
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2] 输出:[2,3,1]
class Solution {
public int[] reversePrint(ListNode head) {
int size=0;
ListNode temp=head;
while(temp!=null){
temp=temp.next;
size++;
}
temp=head;
int[] res=new int[size];
int i=size-1;
while(temp!=null){
res[i--]=temp.val;
temp=temp.next;
}
return res;
}
}
剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] Output: [3,9,20,null,null,15,7]
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(inorder,0,inorder.length-1,preorder,0,preorder.length-1);
}
public TreeNode build(int[] inorder,int inLeft,int inRight,int[] preorder,int preLeft,int preRight){
if(inLeft>inRight || preLeft>preRight) return null;
int rootValue=preorder[preLeft];
TreeNode root=new TreeNode(rootValue);
int rootIndex=0;
for(int i=inLeft;i<=inRight;i++){
if(rootValue==inorder[i]){
rootIndex=i;
break;
}
}
root.left=build(inorder,inLeft,rootIndex-1,preorder,preLeft+1,preLeft+rootIndex-inLeft);//前序的右边界不好确定 preLeft+rootIndex-inLeft
root.right=build(inorder,rootIndex+1,inRight,preorder,preLeft+1+rootIndex-inLeft,preRight);//根据上面的有边界可以确定前序的左边界preLeft+1+rootIndex-inLeft
return root;
}
}
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail
和 deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead
操作返回 -1 )
示例 1:
输入: ["CQueue","appendTail","deleteHead","deleteHead","deleteHead"] [[],[3],[],[],[]] 输出:[null,null,3,-1,-1]
class CQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public CQueue() {
stack1=new Stack<>();
stack2=new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.isEmpty()?-1:stack2.pop();
}
}
剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n
,求斐波那契(Fibonacci)数列的第 n
项(即 F(N)
)。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2 输出:1
//没有考虑取模
class Solution {
public int fib(int n) {
if(n<1) return n;
int[] dp=new int[n+1];
dp[0]=0;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
dp[i]=dp[i]%1000000007;
}
return dp[n];
}
}
剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n
级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2 输出:2
class Solution {
public int numWays(int n) {
if(n<1) return 1;
int[] dp=new int[n+1];
dp[0]=1;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
dp[i]=dp[i]%1000000007;
}
return dp[n];
}
}
剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers
,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2]
为 [1,2,3,4,5]
的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
示例 1:
输入:numbers =
[3,4,5,1,2]
输出:1
class Solution {
public int minArray(int[] numbers) {
int left=0;
int right=numbers.length-1;
while(left<right){
int mid=left+(right-left)/2;
if(numbers[mid]>numbers[right]) left=mid+1;
else if(numbers[mid]<numbers[right]) right=mid;
else right--;
}
return numbers[left];
}
}
class Solution {
public int minArray(int[] numbers) {
for(int i=1;i<numbers.length;i++){
if(numbers[i]<numbers[i-1]){
return numbers[i];
}
}
return numbers[0];
}
}
剑指 Offer 12. 矩阵中的路径
给定一个 m x n
二维字符网格 board
和一个字符串单词 word
。如果 word
存在于网格中,返回 true
;否则,返回 false
。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。
示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:true
class Solution {
public boolean exist(char[][] board, String word) {
int n=board.length;
int m=board[0].length;
char[] words=word.toCharArray();
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(dfs(board,words,i,j,0)){
return true;
}
}
}
return false;
}
public boolean dfs(char[][] board,char[] words,int i,int j,int index){
if(i<0||i>=board.length || j<0||j>=board[0].length || board[i][j]!=words[index]) return false;
if(index==words.length-1) return true;
board[i][j]='.';
boolean res=(dfs(board,words,i,j+1,index+1) || dfs(board,words,i+1,j,index+1) || dfs(board,words,i,j-1,index+1) || dfs(board,words,i-1,j,index+1));
board[i][j]=words[index];
return res;
}
}
剑指 Offer 14- I. 剪绳子
给你一根长度为 n
的绳子,请把绳子剪成整数长度的 m
段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1]
。请问 k[0]*k[1]*...*k[m-1]
可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1
class Solution {
public int cuttingRope(int n) {
int[] dp=new int[n+1];
for(int i=2;i<=n;i++){
for(int j=1;j<=i-j;j++){
dp[i]=Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
}
}
return dp[n];
}
}
剑指 Offer 15. 二进制中1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。
提示:
- 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
- 在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数
-3
。
示例 1:
输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
//位运算n&(n-1)表示删除n中最后一个1 本题采用逐个删除法
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res=0;
while(n!=0){
n=n&(n-1);
res++;
}
return res;
}
}
剑指 Offer 16. 数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入:x = 2.00000, n = 10 输出:1024.00000
class Solution {
public double myPow(double x, int n) {
double res=1.0;
long b=n;
if(b<0){
x=1/x;
b=-b;
}
while(b!=0){
if(b%2==1) res*=x;
x*=x;
b/=2;
}
return res;
}
}
剑指 Offer 17. 打印从1到最大的n位数
输入数字 n
,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
示例 1:
输入: n = 1 输出: [1,2,3,4,5,6,7,8,9]
class Solution {
public int[] printNumbers(int n) {
int size=1;
for(int i=0;i<n;i++){
size*=10;
}
int[] res=new int[size-1];
for(int i=0;i<size-1;i++){
res[i]=i+1;
}
return res;
}
}
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动
示例 1:
输入: head = [4,5,1,9], val = 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head==null) return null;
ListNode pre=new ListNode(0,head);
ListNode cur=pre;
while(cur!=null&&cur.next!=null){
if(cur.next.val==val){
cur.next=cur.next.next;
}
cur=cur.next;
}
return pre.next;
}
}
剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6
个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6
。这个链表的倒数第 3
个节点是值为 4
的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2. 返回链表 4->5.
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode pre=new ListNode(0,head);
ListNode slow=head;
ListNode fast=head;
for(int i=0;i<k;i++){
fast=fast.next;
}
while(fast!=null){
slow=slow.next;
fast=fast.next;
}
return slow;
}
}
剑指 Offer 24. 反转链表
义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
}
}
剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode pre=new ListNode(0);
ListNode cur=pre;
ListNode temp1=l1;
ListNode temp2=l2;
while(temp1!=null && temp2!=null){
if(temp1.val<temp2.val){
cur.next=temp1;
temp1=temp1.next;
}else{
cur.next=temp2;
temp2=temp2.next;
}
cur=cur.next;
}
cur.next=temp1!=null?temp1:temp2;
return pre.next;
}
}
剑指 Offer 19. 正则表达式匹配
请实现一个函数用来匹配包含'. '
和'*'
的正则表达式。模式中的字符'.'
表示任意一个字符,而'*'
表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"ab*ac*a"
匹配,但与"aa.a"
和"ab*a"
均不匹配。
示例 1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。
class Solution {
public boolean isMatch(String s, String p) {
boolean[][] dp=new boolean[s.length()+1][p.length()+1];
dp[0][0]=true;
for(int j=2;j<p.length()+1;j++){
if(p.charAt(j-1)=='*'){
dp[0][j]=dp[0][j-2];
}
}
for(int i=1;i<s.length()+1;i++){
for(int j=1;j<p.length()+1;j++){
if(p.charAt(j-1)!='*'){
if(s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='.') dp[i][j]=dp[i-1][j-1];
}else{
if(s.charAt(i-1)!=p.charAt(j-2)&&p.charAt(j-2)!='.'){
dp[i][j]=dp[i][j-2];
}else{
dp[i][j]=dp[i][j-2] || dp[i][j-1] || dp[i-1][j];
}
}
}
}
return dp[s.length()][p.length()];
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
示例:
输入:nums = [1,2,3,4] 输出:[1,3,2,4] 注:[3,1,2,4] 也是正确的答案之一。
class Solution {
public int[] exchange(int[] nums) {
int left=0;
int right=nums.length-1;
int temp=0;
while(left<right){
while(left<right&&nums[left]%2==1) left++;
while(left<right&&nums[right]%2==0) right--;
temp=nums[left];
nums[left]=nums[right];
nums[right]=temp;
}
return nums;
}
}
剑指 Offer 26. 树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/ \
4 5
/ \
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1] 输出:false
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A==null || B==null) return false;
if(A.val==B.val&&compareTree(A,B)) return true;
return isSubStructure(A.left,B) || isSubStructure(A.right,B);
}
public boolean compareTree(TreeNode A,TreeNode B){
if(B==null) return true;
if(B!=null&&A==null) return false;
if(A.val!=B.val) return false;
return compareTree(A.left,B.left)&&compareTree(A.right,B.right);
}
}
剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/ \
2 7
/ \ / \
1 3 6 9
镜像输出:
4
/ \
7 2
/ \ / \
9 6 3 1
方法一:利用函数本身的意义
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null) return null;
TreeNode left=mirrorTree(root.left);
TreeNode right=mirrorTree(root.right);
root.left=right;
root.right=left;
return root;
}
}
方法二:后序遍历
class Solution {
public TreeNode mirrorTree(TreeNode root) {
dfs(root);
return root;
}
public void dfs(TreeNode root){
if(root==null) return;
dfs(root.left);
dfs(root.right);
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
}
剑指 Offer 28. 对称的二叉树
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return compareTree(root.left,root.right);
}
public boolean compareTree(TreeNode left,TreeNode right){
if(left==null&&right==null) return true;
if(left!=null&&right==null) return false;
if(left==null&&right!=null) return false;
if(left.val!=right.val) return false;
return compareTree(left.left,right.right)&&compareTree(left.right,right.left);
}
}
剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length==0) return new int[0];
int n=matrix.length;
int m=matrix[0].length;
int[] res=new int[n*m];
int index=0;
int up=0;
int down=n-1;
int left=0;
int right=m-1;
while(true){
if(up<=down){
for(int j=left;j<=right;j++){
res[index++]=matrix[up][j];
}
up++;
}else{
break;
}
if(right>=left){
for(int i=up;i<=down;i++){
res[index++]=matrix[i][right];
}
right--;
}else{
break;
}
if(down>=up){
for(int j=right;j>=left;j--){
res[index++]=matrix[down][j];
}
down--;
}else{
break;
}
if(left<=right){
for(int i=down;i>=up;i--){
res[index++]=matrix[i][left];
}
left++;
}else{
break;
}
}
return res;
}
}
剑指 Offer 30. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例:
MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.min(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.min(); --> 返回 -2.
class MinStack {
Deque<Integer> stack1;
Deque<Integer> stack2;
/** initialize your data structure here. */
public MinStack() {
stack1=new ArrayDeque<>();
stack2=new ArrayDeque<>();
}
public void push(int x) {
stack1.push(x);
if(stack2.isEmpty() || stack2.peek()>=x) stack2.push(x);
}
public void pop() {
if(stack1.pop().equals(stack2.peek()))
stack2.pop();
}
public int top() {
return stack1.peek();
}
public int min() {
return stack2.peek();
}
}
剑指 Offer 31. 栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {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
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack=new Stack<>();
int i=0;
for(int num:pushed){
stack.push(num);
while(!stack.isEmpty()&&stack.peek()==popped[i]){
stack.pop();
i++;
}
}
return stack.isEmpty();
}
}
剑指 Offer 32 - I. 从上到下打印二叉树
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
例如:
给定二叉树: [3,9,20,null,null,15,7]
,
3 / \ 9 20 / \ 15 7
返回:
[3,9,20,15,7]
class Solution {
public int[] levelOrder(TreeNode root) {
if(root==null) return new int[0];
Queue<TreeNode> queue=new LinkedList<>();
List<Integer> list=new ArrayList<>();
list.add(root.val);
queue.offer(root);
while(!queue.isEmpty()){
int size=queue.size();
while(size-->0){
TreeNode node=queue.poll();
if(node.left!=null){
list.add(node.left.val);
queue.offer(node.left);
}
if(node.right!=null){
list.add(node.right.val);
queue.offer(node.right);
}
}
}
int[] res=new int[list.size()];
int i=0;
for(int num:list){
res[i++]=num;
}
return res;
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
例如:
给定二叉树: [3,9,20,null,null,15,7]
,
3 / \ 9 20 / \ 15 7
返回其层次遍历结果:
[ [3], [9,20], [15,7] ]
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null) return res;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int size=queue.size();
ArrayList<Integer> arr = new ArrayList<>();
while(size-->0){
TreeNode node = queue.poll();
arr.add(node.val);
if(node.left!=null){
queue.offer(node.left);
}
if(node.right!=null){
queue.offer(node.right);
}
}
res.add(arr);
}
return res;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
例如:
给定二叉树: [3,9,20,null,null,15,7]
,
3 / \ 9 20 / \ 15 7
返回其层次遍历结果:
[ [3], [20,9], [15,7] ]
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null) return res;
Queue<TreeNode> queue=new LinkedList<>();
boolean flag=false;
queue.offer(root);
while(!queue.isEmpty()){
int size=queue.size();
Deque<Integer> dq=new ArrayDeque<>();
while(size-->0){
TreeNode node = queue.poll();
if(flag){
dq.offerFirst(node.val);
}else{
dq.offerLast(node.val);
}
if(node.left!=null){
queue.offer(node.left);
}
if(node.right!=null){
queue.offer(node.right);
}
}
flag=!flag;
res.add(new ArrayList(dq));
}
return res;
}
}
剑指 Offer 33. 二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true
,否则返回 false
。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5 / \ 2 6 / \ 1 3
class Solution {
public boolean verifyPostorder(int[] postorder) {
int i=0;
int j=postorder.length-1;
return dfs(postorder,i,j);
}
public boolean dfs(int[] postorder,int i,int j){
if(i>=j) return true;
int p=i;
while(postorder[p]<postorder[j]) p++;
int m=p;
while(postorder[p]>postorder[j]) p++;
return p==j&&dfs(postorder,i,m-1)&&dfs(postorder,m,j-1);
}
}
剑指 Offer 34. 二叉树中和为某一值的路径
给你二叉树的根节点 root
和一个整数目标和 targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 输出:[[5,4,11,2],[5,8,4,5]]
class Solution {
List<List<Integer>> res=new LinkedList<>();
LinkedList<Integer> path=new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
if(root==null) return res;
backTravel(root,target);
return res;
}
public void backTravel(TreeNode root,int target){
if(root==null) return;
path.add(root.val);
target-=root.val;
if(root.left==null&&root.right==null&&target==0){
res.add(new LinkedList(path));
}
backTravel(root.left,target);
backTravel(root.right,target);
path.removeLast();
}
}
剑指 Offer 35. 复杂链表的复制
请实现 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]]
class Solution {
public Node copyRandomList(Node head) {
if(head==null) return null;
Map<Node,Node> map=new HashMap<>();
Node cur=head;
//1 复制链表
while(cur!=null){
map.put(cur,new Node(cur.val));
cur=cur.next;
}
//2 next和random指向
cur=head;
while(cur!=null){
map.get(cur).next=map.get(cur.next);
map.get(cur).random=map.get(cur.random);
cur=cur.next;
}
return map.get(head);
}
}
剑指 Offer 36. 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
class Solution {
Node head,pre;
public Node treeToDoublyList(Node root) {
if(root==null) return null;
dfs(root);
head.left=pre;
pre.right=head;
return head;
}
public void dfs(Node cur){
if(cur==null) return;
dfs(cur.left);
if(pre!=null){
pre.right=cur;
}else{
head=cur;
}
cur.left=pre;
pre=cur;
dfs(cur.right);
}
}
剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc" 输出:["abc","acb","bac","bca","cab","cba"]
class Solution {
Set<String> set=new HashSet<>();
StringBuilder sb=new StringBuilder();
boolean[] user;
public String[] permutation(String s) {
user=new boolean[s.length()];
backTravel(s);
return set.toArray(new String[set.size()]);
}
public void backTravel(String s){
if(sb.length()==s.length()){
set.add(sb.toString());
}
for(int i=0;i<s.length();i++){
if(user[i]) continue;
sb.append(s.charAt(i));
user[i]=true;
backTravel(s);
sb.deleteCharAt(sb.length()-1);
user[i]=false;
}
}
}
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2
//投票法
class Solution {
public int majorityElement(int[] nums) {
int target=0;
int count=0;
for(int num:nums){
if(count==0){
target=num;
count=1;
}else if(num==target){
count++;
}else{
count--;
}
}
return target;
}
}
//Map
class Solution {
public int majorityElement(int[] nums) {
Map<Integer,Integer> map=new HashMap<>();
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
int n=nums.length;
for(Map.Entry<Integer,Integer> m:map.entrySet()){
if(m.getValue()>n/2) return m.getKey();
}
return -1;
}
}
剑指 Offer 40. 最小的k个数
输入整数数组 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]
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res=new int[k];
PriorityQueue<Integer> pq=new PriorityQueue<>((a,b)->b-a);
for(int num:arr){
pq.add(num);
if(pq.size()>k){
pq.poll();
}
}
int i=0;
for(int num:pq){
res[i++]=num;
}
return res;
}
}
剑指 Offer 37. 序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树。
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
示例:
输入:root = [1,2,3,null,null,4,5] 输出:[1,2,3,null,null,4,5]
public class Codec {
String SEP=",";
String NULL="null";
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb=new StringBuilder();
serialize(root,sb);
return sb.toString();
}
public void serialize(TreeNode root,StringBuilder sb){
if(root==null) {
sb.append(NULL).append(SEP);
return;
}
sb.append(root.val).append(SEP);
serialize(root.left,sb);
serialize(root.right,sb);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
LinkedList<String> list=new LinkedList<>();
for(String s:data.split(",")){
list.add(s);
}
return deserialize(list);
}
public TreeNode deserialize(LinkedList<String> list){
if(list.isEmpty()) return null;
String first=list.removeFirst();
if(first.equals(NULL)) return null;
TreeNode root=new TreeNode(Integer.parseInt(first));
root.left=deserialize(list);
root.right=deserialize(list);
return root;
}
}
剑指 Offer 41. 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[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]
class MedianFinder {
PriorityQueue<Integer> large;
PriorityQueue<Integer> small;
/** initialize your data structure here. */
public MedianFinder() {
large=new PriorityQueue<>((a,b)->b-a);
small=new PriorityQueue<>();
}
public void addNum(int num) {
if(large.size()>=small.size()){
large.offer(num);
small.offer(large.poll());
}else{
small.offer(num);
large.offer(small.poll());
}
}
public double findMedian() {
if(large.size()>small.size()){
return large.peek();
}else if(large.size()<small.size()){
return small.peek();
}
return (large.peek()+small.peek())/2.0;
}
}
剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution {
public int maxSubArray(int[] nums) {
int[] dp=new int[nums.length];
dp[0]=nums[0];
int res=nums[0];
for(int i=1;i<nums.length;i++){
dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
if(dp[i]>res) res=dp[i];
}
return res;
}
}
剑指 Offer 44. 数字序列中某一位的数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。
示例 1:
输入:n = 3 输出:3
class Solution {
public int findNthDigit(int n) {
int digit=1;
long start=1;
long count=9;
while(n>count){
n-=count;
digit+=1;
start*=10;
count=9*digit*start;
}
long num=start+(n-1)/digit;
return Long.toString(num).charAt((n-1)%digit)-'0';
}
}
剑指 Offer 50. 第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例 1:
输入:s = "abaccdeff" 输出:'b'
class Solution {
public char firstUniqChar(String s) {
int[] count=new int[26];
for(char c:s.toCharArray()){
count[c-'a']++;
}
for(char c:s.toCharArray()){
if(count[c-'a']==1){
return c;
}
}
return ' ';
}
}
//Map
class Solution {
public char firstUniqChar(String s) {
Map<Character,Integer> map=new HashMap<>();
for(char c:s.toCharArray()){
map.put(c,map.getOrDefault(c,0)+1);
}
for(char c:s.toCharArray()){
if(map.containsKey(c)&&map.get(c)==1){
return c;
}
}
return ' ';
}
}
剑指 Offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"
class Solution {
public int translateNum(int num) {
String s=String.valueOf(num);
int[] dp=new int[s.length()+1];
dp[0]=dp[1]=1;
for(int i=2;i<s.length()+1;i++){
String temp=s.substring(i-2,i);
if(temp.compareTo("10")>=0&&temp.compareTo("25")<=0){
dp[i]=dp[i-1]+dp[i-2];
}else{
dp[i]=dp[i-1];
}
}
return dp[s.length()];
}
}
剑指 Offer 47. 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
示例 1:
输入:[ [1,3,1], [1,5,1], [4,2,1] ]
输出:12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
class Solution {
public int maxValue(int[][] grid) {
int n=grid.length;
int m=grid[0].length;
int[][] dp=new int[n][m];
dp[0][0]=grid[0][0];
for(int i=1;i<n;i++){
dp[i][0]=grid[i][0]+dp[i-1][0];
}
for(int j=1;j<m;j++){
dp[0][j]=grid[0][j]+dp[0][j-1];
}
for(int i=1;i<n;i++){
for(int j=1;j<m;j++){
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[n-1][m-1];
}
}
剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其
长度为 3。
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set=new HashSet<>();
int left=0;
int res=0;
for(int right=0;right<s.length();right++){
if(set.contains(s.charAt(right))){
while(set.contains(s.charAt(right))){
set.remove(s.charAt(left));
left++;
}
}
set.add(s.charAt(right));
res=Math.max(res,set.size());
}
return res;
}
}
剑指 Offer 49. 丑数
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12
是前 10 个丑数。
class Solution {
public int nthUglyNumber(int n) {
int[] dp=new int[n];
int a=0;
int b=0;
int c=0;
dp[0]=1;
for(int i=1;i<n;i++){
int n2=dp[a]*2;
int n3=dp[b]*3;
int n5=dp[c]*5;
dp[i]=Math.min(Math.min(n2,n3),n5);
if(n2==dp[i]) a++;
if(n3==dp[i]) b++;
if(n5==dp[i]) c++;
}
return dp[n-1];
}
}
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4] 输出: 5
class Solution {
public int reversePairs(int[] nums) {
int[] temp=new int[nums.length];
return mergeSort(nums,temp,0,nums.length-1);
}
public int mergeSort(int[] nums,int[] temp,int l,int r){
if(l>=r) return 0;
int m=(l+r)/2;
int res=mergeSort(nums,temp,l,m)+mergeSort(nums,temp,m+1,r);
int i=l;int j=m+1;
for(int k=l;k<=r;k++){
temp[k]=nums[k];
}
for(int k=l;k<=r;k++){
if(i==m+1){
nums[k]=temp[j++];
}else if(j==r+1 || temp[i]<=temp[j]){
nums[k]=temp[i++];
}else{
nums[k]=temp[j++];
res+=m-i+1;
}
}
return res;
}
}
剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A=headA;
ListNode B=headB;
while(A!=B){
A=A!=null?A.next:headB;
B=B!=null?B.next:headA;
}
return A;
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10]
, target = 8
输出: 2
class Solution {
public int search(int[] nums, int target) {
return searchNum(nums,target)-searchNum(nums,target-1);
}
public int searchNum(int[] nums,int target){
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]>target){
right=mid-1;
}else {
left=mid+1;
}
}
return left;
}
}
剑指 Offer 53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3] 输出: 2
class Solution {
public int missingNumber(int[] nums) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]>mid){
right=mid-1;
}else{
left=mid+1;
}
}
return left;
}
}
剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第 k
大的节点的值。
示例 1:
输入: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \ 2 输出: 4
//二叉搜索树中序遍历有顺序
class Solution {
List<Integer> list=new ArrayList<>();
public int kthLargest(TreeNode root, int k) {
dfs(root);
return list.get(list.size()-k);
}
public void dfs(TreeNode root){
if(root==null) return;
dfs(root.left);
list.add(root.val);
dfs(root.right);
}
}
剑指 Offer 55 - I. 二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7]
,
3 / \ 9 20 / \ 15 7
返回它的最大深度 3 。
class Solution {
public int maxDepth(TreeNode root) {
if(root==null) return 0;
int left=maxDepth(root.left);
int right=maxDepth(root.right);
int res=Math.max(left,right)+1;
return res;
}
}
剑指 Offer 55 - II. 平衡二叉树
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
int left=getHigh(root.left);
int right=getHigh(root.right);
return isBalanced(root.left)&&isBalanced(root.right) && Math.abs(left-right)<=1;
}
public int getHigh(TreeNode root){
if(root==null) return 0;
int left=getHigh(root.left);
int right=getHigh(root.right);
return Math.max(left,right)+1;
}
}
剑指 Offer 56 - I. 数组中数字出现的次数
一个整型数组 nums
里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6] 输出:[1,6] 或 [6,1]
class Solution {
public int[] singleNumbers(int[] nums) {
int n=0;
int m=1;
for(int num:nums){
n^=num;
}
while((n&m)==0){
m<<=1;
}
int x=0;int y=0;
for(int num:nums){
if((num&m)!=0) x^=num;
else y^=num;
}
return new int[]{x,y};
}
}
剑指 Offer 56 - II. 数组中数字出现的次数 II
在一个数组 nums
中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3] 输出:4
class Solution {
public int singleNumber(int[] nums) {
int one=0;
int two=0;
for(int num:nums){
one=one^num& ~two;
two=two^num& ~one;
}
return one;
}
}
剑指 Offer 57. 和为s的两个数字
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
输入:nums = [2,7,11,15], target = 9 输出:[2,7] 或者 [7,2]
//双指针
class Solution {
public int[] twoSum(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int sum=nums[left]+nums[right];
if(sum>target){
right--;
}else if(sum<target){
left++;
}else{
return new int[]{nums[left],nums[right]};
}
}
return new int[0];
}
}
//map 效率慢
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map=new HashMap<>();
for(int num:nums){
if(map.containsKey(target-num)){
return new int[]{num,target-num};
}
map.put(num,map.getOrDefault(num,0)+1);
}
return new int[0];
}
}
剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target
,输出所有和为 target
的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9 输出:[[2,3,4],[4,5]]
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> res=new ArrayList<>();
int i=1;int j=2;int s=3;
while(i<j){
if(s==target){
int[] ans=new int[j-i+1];
for(int k=i;k<=j;k++){
ans[k-i]=k;
}
res.add(ans);
}
if(s>=target){
s-=i;
i++;
}else{
j++;
s+=j;
}
}
return res.toArray(new int[0][]);
}
}
剑指 Offer 58 - I. 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 1:
输入: "the sky is blue
" 输出: "blue is sky the
"
class Solution {
public String reverseWords(String s) {
s.trim();
int i=s.length()-1;
int j=i;
StringBuilder sb=new StringBuilder();
while(i>=0){
while(i>=0&&s.charAt(i)!=' ') i--;
sb.append(s.substring(i+1,j+1)+" ");
while(i>=0&&s.charAt(i)==' ') i--;
j=i;
}
return sb.toString().trim();
}
}
剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2 输出: "cdefgab"
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder sb=new StringBuilder(s);
reverse(sb,0,n-1);
reverse(sb,n,s.length()-1);
reverse(sb,0,s.length()-1);
return sb.toString();
}
public void reverse(StringBuilder s,int left,int right){
while(left<right){
char temp=s.charAt(left);
s.setCharAt(left,s.charAt(right));
s.setCharAt(right,temp);
left++;
right--;
}
}
}
剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 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
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length==0 || k==0) return new int[0];
Deque<Integer> deque=new LinkedList<>();
int[] res=new int[nums.length-k+1];
for(int j=0,i=1-k;j<nums.length;i++,j++){
if(i>0&&deque.peekFirst()==nums[i-1]) deque.removeFirst();
while(!deque.isEmpty()&&deque.peekLast()<nums[j]){
deque.removeLast();
}
deque.addLast(nums[j]);
if(i>=0){
res[i]=deque.peekFirst();
}
}
return res;
}
}
剑指 Offer 63. 股票的最大利润
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:
输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
class Solution {
public int maxProfit(int[] prices) {
int[] dp=new int[2];
dp[0]=Integer.MIN_VALUE;
dp[1]=0;
for(int i=0;i<prices.length;i++){
dp[0]=Math.max(dp[0],-prices[i]);
dp[1]=Math.max(dp[1],dp[0]+prices[i]);
}
return dp[1];
}
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
if(root.val>p.val&&root.val>q.val){
return lowestCommonAncestor(root.left,p,q);
}else if(root.val<p.val&&root.val<q.val){
return lowestCommonAncestor(root.right,p,q);
}
return root;
}
}
剑指 Offer 68 - II. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
if(root==p || root==q) return root;
TreeNode left=lowestCommonAncestor(root.left,p,q);
TreeNode right=lowestCommonAncestor(root.right,p,q);
if(left!=null&&right==null) return left;
if(left==null&&right!=null) return right;
if(left==null&&right==null) return null;
return root;
}
}
剑指 Offer 60. n个骰子的点数
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
示例 1:
输入: 1 输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
class Solution {
public double[] dicesProbability(int n) {
double[] dp=new double[6];
Arrays.fill(dp,1.0/6.0);
for(int i=2;i<=n;i++){
double[] temp=new double[5*i+1];
for(int j=0;j<dp.length;j++){
for(int k=0;k<6;k++){
temp[j+k]+=dp[j]/(6.0);
}
}
dp=temp;
}
return dp;
}
}
剑指 Offer 62. 圆圈中最后剩下的数字
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
class Solution {
public int lastRemaining(int n, int m) {
int x=0;
for(int i=2;i<=n;i++){
x=(x+m)%i;
}
return x;
}
}
剑指 Offer 64. 求1+2+…+n
求 1+2+...+n
,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3 输出: 6
class Solution {
int res=0;
public int sumNums(int n) {
boolean x=n>1&&sumNums(n-1)>0;
res+=n;
return res;
}
}
剑指 Offer 65. 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
示例:
输入: a = 1, b = 1 输出: 2
class Solution {
public int add(int a, int b) {
if(a==0 || b==0){
return a==0?b:a;
}
int sum=a^b;
int carray=(a&b)<<1;
return add(sum,carray);
}
}
面试题45. 把数组排成最小的数
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
示例 1:
输入:[10,2]
输出: "102"
//类似冒泡排序
class Solution {
public String minNumber(int[] nums) {
String[] str=new String[nums.length];
for(int i=0;i<nums.length;i++){
str[i]=String.valueOf(nums[i]);
}
for(int i=0;i<str.length;i++){
for(int j=0;j<str.length-1;j++){
if((str[j]+str[j+1]).compareTo(str[j+1]+str[j])>0){
String temp=str[j];
str[j]=str[j+1];
str[j+1]=temp;
}
}
}
StringBuilder sb=new StringBuilder();
for(String s:str){
sb.append(s);
}
return sb.toString();
}
}
面试题59 - II. 队列的最大值
请定义一个队列并实现函数 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]
class MaxQueue {
Deque<Integer> deque1;
Deque<Integer> deque2;
public MaxQueue() {
deque1=new ArrayDeque<>();
deque2=new ArrayDeque<>();
}
public int max_value() {
if(deque2.isEmpty()) return -1;
return deque2.peekFirst();
}
public void push_back(int value) {
deque1.addLast(value);
while(!deque2.isEmpty() && value>deque2.peekLast()) deque2.pollLast();
deque2.addLast(value);
}
public int pop_front() {
if(deque1.isEmpty()) return -1;
if(deque1.peekFirst().equals(deque2.peekFirst())) deque2.pollFirst();
return deque1.pollFirst();
}
}
面试题61. 扑克牌中的顺子
从若干副扑克牌中随机抽 5
张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
示例 1:
输入: [1,2,3,4,5] 输出: True
class Solution {
public boolean isStraight(int[] nums) {
Arrays.sort(nums);
int joker=0;
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;
}
}
面试题67. 把字符串转换成整数
示例 2:
输入: " -42" 输出: -42 解释: 第一个非空白字符为 '-', 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
class Solution {
public int strToInt(String str) {
int start=0;
long res=0;
int sign=1;
while(start<str.length()&&str.charAt(start)==' ') start++;
if(start==str.length()) return 0;
if(str.charAt(start)=='+'){
start++;
}else if(str.charAt(start)=='-'){
sign=-1;
start++;
}
for(int i=start;i<str.length()&&Character.isDigit(str.charAt(i));i++){
res=res*10+sign*(str.charAt(i)-'0');
if(res>0&&res>Integer.MAX_VALUE) return Integer.MAX_VALUE;
if(res<0&&res<Integer.MIN_VALUE) return Integer.MIN_VALUE;
}
return (int)res;
}
}