一个栈A用来模拟添加元素到队列尾,另一个B存A中元素倒叙,用来取出队列头
class CQueue {
LinkedList<Integer> A, B;
public CQueue() {
A = new LinkedList<>();//A用来加入数据
B = new LinkedList<>();//B用于存放A中倒叙元素,取出队列头
}
public void appendTail(int value) {
A.addLast(value);
}
public int deleteHead() {
if(!B.isEmpty()) return B.removeLast();
if(A.isEmpty()) return -1;
else {
while(!A.isEmpty()){
B.addLast(A.removeLast());
}
return B.removeLast();
}
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
class MinStack {
/** initialize your data structure here. */
Stack<Integer> A, B;
public MinStack() {
A = new Stack<>();
B = new Stack<>();
}
public void push(int x) {
A.add(x);
if(B.isEmpty() || x <= B.peek()){//用<=防止重复的最小值只记录了一次
B.add(x);
}
}
public void pop() {
if(A.pop().equals(B.peek())){
B.pop();
}
}
public int top() {
return A.peek();
}
public int min() {
return B.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.min();
*/
59 - II. 队列的最大值 M
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
class MaxQueue {
Queue<Integer> queue;//队列
Deque<Integer> deque;//双端队列
public MaxQueue() {
queue = new LinkedList<>();
deque = new LinkedList<>();
}
public int max_value() {
if(!deque.isEmpty()){
return deque.peekFirst();
}
return -1;
}
public void push_back(int value) {
queue.offer(value);
while(!deque.isEmpty() && deque.peekLast() < value){
deque.pollLast();
}
deque.offerLast(value);
}
public int pop_front() {
if(queue.isEmpty()) return -1;
if(queue.peek().equals(deque.peekFirst())){
deque.pollFirst();
}
return queue.poll();
}
}
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue obj = new MaxQueue();
* int param_1 = obj.max_value();
* obj.push_back(value);
* int param_3 = obj.pop_front();
*/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
Stack<Integer> stack = new Stack<>();
while(head != null){
stack.push(head.val);
head = head.next;
}
int res[] = new int[stack.size()];
for(int i = 0; i < res.length; i++){
res[i] = stack.pop();
}
return res;
}
}
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
class MedianFinder {
Queue<Integer> A,B;
/** initialize your data structure here. */
public MedianFinder() {
A = new PriorityQueue<>(); // 小顶堆,保存较大的一半,堆底->顶,小->大,优先级队列会自动排序
B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半,堆底->顶,大->小
}
public void addNum(int num) {//AB中数目不等,向B添加,方式:先加入A,再将A顶加入B
if(A.size() != B.size()){
A.add(num);
B.add(A.poll());
}else{
B.add(num);
A.add(B.poll());
}
}
//A中放的元素多,B中少,若AB中数目不等则中位数为A堆顶元素,相等则为AB堆顶和的1/2
public double findMedian() {
return A.size() == B.size() ? (A.peek()+B.peek())/2.0 : A.peek();
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
滑动窗口的重要性质是:窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)。如果左右边界向左移动的话,这叫做“回溯”,算法的时间复杂度就可能不止 O(n)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
class Solution {
public int[][] findContinuousSequence(int target) {
int i = 1, j = 2, sum = 3;
List<int[]> res = new ArrayList<>();
while(i < j){
if(sum == target){
int[] arr = new int[j-i+1];
for(int k = i; k <= j; k++){//i从1开始,既是数组下标,又是数组内容
arr[k-i] = k;
}
res.add(arr);
}
if(sum > target){//左边界右移,先减再移动
sum -= i;
i++;
}else{//右边界右移,先移再加
j++;
sum += j;
}
}
return res.toArray(new int[0][]);
}
}
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
class Solution {
public int lengthOfLongestSubstring(String s) {
int l = 0;//滑动窗口的左边界
int max = 0;//记录最大长度
for(int i = 0; i < s.length(); i++){//外循环i遍历字符串
for(int j = l; j < i; j++){//内循环j遍历滑动窗口
if(s.charAt(i) == s.charAt(j)){
l = j + 1;
break;
}
}
max = Math.max(max, i-l+1);
}
return max;
}
}
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
下一个丑数是通过上一个丑数x2或x3或x5得到,所以用动态规划
class Solution {
public int nthUglyNumber(int n) {
int a = 0, b = 0, c = 0;
int[] arr = new int[n];//需要的结果是第n-1个,所以要全部记录下来
arr[0] = 1;
for(int i = 1; i < n; i++){
int n1 = arr[a]*2, n2 = arr[b]*3, n3 = arr[c]*5;//下一个丑数必定是上一个丑数乘2、3、5后
arr[i] = Math.min(Math.min(n1,n2),n3);//其中最小的一个
if(arr[i] == n1) a++;//若下一个是通过上一个乘2得到,则a索引向后走一步
if(arr[i] == n2) b++;//若下一个数=乘2或乘3,则a、b都要向后走一步
if(arr[i] == n3) c++;
}
return arr[n-1];
}
}
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出: 3
约瑟夫环问题,关键公式:dp[i]=(dp[i−1]+m)%i
class Solution {
public int lastRemaining(int n, int m) {
int ans = 0;//一个数字的话,结果必然是下标0
for(int i = 2; i <= n; i++){//从两个数字开始
ans = (ans + m) % i;
}
return ans;
}
}
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”
规律:0个数和1个数时,都是1种方法,
当有2个数字以上:1.若最后两数字组合能翻译,则dp[i] = dp[i-1] +dp[i-2]
2.否则dp[i] = dp[i-1],就是说只加一个数字和不加方法数量一样
class Solution {
public int translateNum(int num) {
String s = String.valueOf(num);//转换成字符串才好切片
int b = 1, a = 1;//b是0个数的总方法,a是1个数的总方法,都是1种
for(int i = 2; i <= s.length(); i++){//从2个数开始
String tmp = s.substring(i-2,i);//subString取值范围,左闭右开
int c = tmp.compareTo("10")>=0 && tmp.compareTo("25")<=0 ? a+b : a;
//compareTo方法,将tmp和“10”、“25”的ASCII码值相减
b = a;
a = c;
}
return a;
}
}
f[i][j] 代表 A 的前 i个和 B 的前 j个能否匹配
对于前面两个情况,可以合并成一种情况 f[i][j] = f[i-1][j-1]f[i][j]=f[i−1][j−1]
对于第三种情况,对于 c∗ 分为看和不看两种情况
不看:直接砍掉正则串的后面两个, f[i][j] = f[i][j-2]f[i][j]=f[i][j−2]
看:正则串不动,主串前移一个,f[i][j] = f[i-1][j]f[i][j]=f[i−1][j]
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] res = new boolean[m+1][n+1];
for(int i = 0; i <= m; i++){
for(int j = 0; j <= n; j++){
if(j == 0){
res[i][j] = (i == 0);//若p为空,则根据s是否为空决定
}
else{
if(p.charAt(j-1) != '*'){//若p的最后一个不是*,只能比较该位是否匹配,或该位是.
if(i > 0 && (s.charAt(i-1) == p.charAt(j-1) || p.charAt(j -1) == '.')){
res[i][j] = res[i-1][j-1];
}
}else{//若p的最后一个是*
if(j >= 2){
res[i][j] = res[i][j-2];//判断能否直接把p的最后两位舍去
}
if(i >= 1 && j >= 2 && (s.charAt(i-1) == p.charAt(j-2) || p.charAt(j-2) == '.')){
res[i][j] |= res[i-1][j];//若不能舍去且s最后一位能匹配上,则s向前移动一位继续匹配
}
}
}
}
}
return res[m][n];
}
}
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];
for(int i = 1; i < nums.length; i++){
nums[i] += Math.max(nums[i-1], 0);
res = Math.max(nums[i], res);
}
return res;
}
}
class Solution {
public int maxValue(int[][] grid) {
int m = grid.length, n = grid[0].length;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(i == 0 && j == 0) continue;
if(i == 0 && j != 0){//在第一行,后面只可能从左边加
grid[i][j] += grid[i][j-1];
}
if(i != 0 && j == 0){//在第一行,后面只可能从上边加
grid[i][j] += grid[i-1][j];
}
if(i != 0 && j != 0){
grid[i][j] += Math.max(grid[i-1][j], grid[i][j-1]);
}
}
}
return grid[m-1][n-1];
}
}
class Solution {
public int maxProfit(int[] prices) {
if(prices.length == 0) return 0;
int res = 0, minVal = prices[0];
for(int i = 1; i < prices.length; i++){
minVal = Math.min(minVal, prices[i-1]);
res = Math.max(res, prices[i] - minVal);
}
return res;
}
}
class Solution {
public int cuttingRope(int n) {
if(n < 4){//小于4的绳子,最大乘积都是n-1
return n - 1;
}
int res = 1;
while(n > 4){
res *= 3;
n -= 3;
}
return res * n;
}
}
class Solution {
public int cuttingRope(int n) {
if(n < 4) return n-1;
long res = 1;
while(n > 4){
res = res * 3 % 1000000007;
n-=3;
}
return (int)(res * n % 1000000007);
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int[] preorder;
HashMap<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for(int i = 0; i < inorder.length; i++){
map.put(inorder[i], i);
}
return recur(0,0,inorder.length-1);
}
TreeNode recur(int pre_root, int in_left, int in_right){
if(in_left > in_right) return null;
TreeNode root = new TreeNode(preorder[pre_root]);
int i = map.get(preorder[pre_root]);//获取前序遍历中根节点在中序遍历的下标
//1.左子树的根节点是前序遍历根节点+1
//2.左边界是中序遍历的左边界
//3.右边界是中序遍历根节点左边一位
root.left = recur(pre_root+1, in_left, i-1);
//1.右子树根节点是前序遍历根节点+左子树长度+1
//2.左边界是中序遍历根节点右边一位
//3.右边界是中序遍历右边界
root.right = recur(pre_root+i-in_left+1, i+1, in_right);
return root;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
68 - I. 二叉搜索树的最近公共祖先 E
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while(root != null){
if(root.val > p.val && root.val > q.val){//root的值大于p,q,说明p,q都在左子树
root = root.left;//到左子树遍历
}else if(root.val < p.val && root.val < q.val){
root = root.right;
}else{
break;
}
}
return root;
}
}
68 - II. 二叉树的最近公共祖先 E
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
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);
if(left == null) return right;//向左递归,若左边没有,必定都在右边
if(right == null) return left;
return root;//不同时在左或右,说明分布在不同子树,根节点为最近祖先
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int res, count;
public int kthLargest(TreeNode root, int k) {
this.count = k;
dfs(root);
return res;
}
public void dfs(TreeNode root){
if(root == null || count == 0) return;//若root为空或k=0即已经找到,则返回
dfs(root.right);
if(--count == 0){
res = root.val;
return;
}
dfs(root.left);
}
}
55 - II. 平衡二叉树 E
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
此树的深度 等于 左子树的深度 与 右子树的深度 中的 最大值 +1
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(dfs(root) == -1){
return false;
}
return true;
}
public int dfs(TreeNode root){
if(root == null) return 0;
int left = dfs(root.left);
if(left == -1) return -1;//出现-1,直接返回
int right = dfs(root.right);
if(right == -1) return -1;
if(Math.abs(left - right) < 2){//左右子树高度差<1就是平衡二叉树
return Math.max(left,right) + 1;
}
return -1;
}
}
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
一定是用深度优先搜索(DFS)+ 剪枝关键是各种条件的判断
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for(int i = 0; i <board.length; i++){
for(int j = 0; j < board[0].length; j++){
if (bfs(board, words, i, j, 0)) return true;
}
}
return false;
}
public boolean bfs(char[][] board, char[] words, int i, int j, int k){
if(i >= board.length || j >= board[0].length || i < 0 || j < 0 || board[i][j] != words[k]) return false;
if(k == words.length - 1) return true;
board[i][j] = '\0';//将访问过的标记,防止重复访问
boolean res = (bfs(board, words, i-1, j, k+1) || bfs(board, words, i+1, j, k+1) ||
bfs(board, words, i, j-1, k+1) || bfs(board, words, i, j+1, k+1));
board[i][j] = words[k];//将标记过的还原,因为若某次失败回溯后可能还要访问他
return res;
}
}
深度优先搜索: 可以理解为暴力法模拟机器人在矩阵中的所有路径。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
剪枝: 在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为 可行性剪枝
递归参数: 当前元素在矩阵中的行列索引 i 和 j ,两者的数位和 si, sj 。
终止条件: 当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 00 ,代表不计入可达解。
递推工作:
标记当前单元格 :将索引 (i, j) 存入 Set visited 中,代表此单元格已被访问过。
搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
class Solution {
public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
return dfs(visited, m, n, k, 0, 0);
}
private int dfs(boolean[][] visited, int m, int n, int k, int i , int j){
if(i >= m || j >= n//i或j越过数组边界
|| sum(i)+sum(j) > k//i+j > k
|| visited[i][j]){//该位数字已经被访问过
return 0;//直接返回
}
visited[i][j] = true;//否则将该数字标记为已经访问
return 1 + dfs(visited, m, n, k, i+1, j) + dfs(visited, m, n, k, i, j+1);//返回向下向左遍历的可行解数量和
}
private int sum(int n){
int sum = 0;
while(n > 0){
sum += n % 10;//求个位
n /= 10;//十位
}
return sum;
}
}
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null) return "[]";
StringBuilder res = new StringBuilder("[");
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(node != null){
res.append(node.val + ",");
queue.add(node.left);
queue.add(node.right);
}else{
res.append("null,");
}
}
res.deleteCharAt(res.length()-1);
res.append("]");
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null;
String[] res = data.substring(1,data.length()-1).split(",");
TreeNode root = new TreeNode(Integer.parseInt(res[0]));
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int i = 1;
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(!res[i].equals("null")){
node.left = new TreeNode(Integer.parseInt(res[i]));
queue.add(node.left);
}
i++;
if(!res[i].equals("null")){
node.right = new TreeNode(Integer.parseInt(res[i]));
queue.add(node.right);
}
i++;
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
这题和上一题思路一样
题目要求的二叉树的 从上至下 打印(即按层打印),又称为二叉树的 广度优先搜索(BFS)。
BFS 通常借助 队列 的先入先出特性来实现。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int[] levelOrder(TreeNode root) {
if(root == null) return new int[0];
ArrayList<Integer> arr = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
arr.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
int[] res = new int[arr.size()];
for(int i = 0; i < arr.size(); i++){
res[i] = arr.get(i);
}
return res;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()){
List<Integer> arr = new ArrayList<>();
for(int i = queue.size(); i > 0; i--){
TreeNode node = queue.poll();
arr.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(arr);
}
return res;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
boolean flag = true;
while(!queue.isEmpty()){
LinkedList<Integer> arr = new LinkedList<>();
for(int i = queue.size(); i > 0; i--){
TreeNode node = queue.poll();
if(flag) arr.addLast(node.val);
else{
arr.addFirst(node.val);
}
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
flag = !flag;
res.add(arr);
}
return res;
}
}
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
while(n != 0){
res += n&1;
n >>>= 1;
}
return res;
}
}
class Solution {
public double myPow(double x, int n) {
if(x ==0) return 0;
long b = n;
double res = 1.0;
if(b < 0){
x = 1/x;
b = -b;
}
while(b > 0){
if((b&1) == 1) res *= x;//判断末位是否为1,是1才乘x,是0直接跳过该位
x *= x;
b >>= 1;//不断右移,将前面的数移到末位
}
return res;
}
}
class Solution {
public int[] singleNumbers(int[] nums) {
int x = 0, y = 0, n = 0, m = 1;
for(int num : nums){//循环按位异或遍历,不同为1,相同为0
n ^= num;//最后剩下x^y
}
while((n&m) == 0){//x,y不同,则至少有一位不同,该位异或后位为1 & 1 = 1
m <<= 1;
}
for(int num : nums){//按照不同的这一位将nums分为两组
if((num&m) == 0) x ^= num;
else y ^= num;
}
int[] res = {x,y};
return res;
}
}
class Solution {
public int singleNumber(int[] nums) {
int[] count = new int[32];//int类型32位
for(int num : nums){
for(int j = 0; j < 32; j++){//每个数有32位
count[j] += num & 1;//统计每个位上1的个数
num >>>= 1;
}
}
int res = 0;
for(int i = 0; i < 32; i++){
res <<= 1;
res |= count[31 - i] % 3;//从高位开始还原这个数
}
return res;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null) return false;
boolean r1 = recure(A,B);//B与A相等
boolean r2 = isSubStructure(A.left,B);//B是A左子树的一部分
boolean r3 = isSubStructure(A.right,B);//B是A右子树的一部分
return r1 || r2 || r3;
}
public boolean recure(TreeNode A, TreeNode B){
if(B == null) return true;//这里B为空说明B已经全匹配上了
if(A == null || A.val != B.val) return false;//说明A已经越过叶子节点,B还没匹配完,或者A,B已经有值不一样
//若一某个点为根节点匹配上了,则他们的左右子节点
//也要完全匹配
return recure(A.left,B.left) && recure(A.right,B.right);
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);
return root;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
return (root == null) ? true : recur(root.left,root.right);
}
public boolean recur(TreeNode l,TreeNode r){
if(l == null && r == null) return true;
if(l == null || r == null || l.val != r.val) return false;
return recur(l.left,r.right) && recur(l.right,r.left);
}
}
二叉搜索树定义: 左子树中所有节点的值 << 根节点的值;右子树中所有节点的值 >> 根节点的值;其左、右子树也分别为二叉搜索树。
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
Node per, head;
public Node treeToDoublyList(Node root) {
if(root == null) return null;
dfs(root);//二叉搜索树的中序遍历即为排序
per.right = head;
head.left = per;//首尾相连
return head;
}
public void dfs(Node cur){
if(cur == null) return;
dfs(cur.left);
if(per == null) head = cur;//per为空,说明当前节点是头节点
else per.right = cur;//建立双向链表
cur.left = per;//无论per是否为空,当前节点都指向per
per = cur;//将当前cur置为per
dfs(cur.right);
}
}
最后一个数一定是根节点的值,要找到以一个大于改值的数,将数组划分为左右子树
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length - 1);
}
public boolean recur(int[] postorder, int l, int r){
if(l >= r) return true;
int point = l;//定义一个指针遍历数组
while(postorder[point] < postorder[r]) point++;//后序遍历,最后一个必定是root节点
int m = point;//现在要找到第一个大于root的节点,划分左右子树,现在m左边全都小于root,即为大左子树
while(postorder[point] > postorder[r]) point++;//m右边需要全部大于root,若达成该条件则point=r
return point == r && recur(postorder, l, m-1) && recur(postorder, m, r-1);
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head, per = null;
while(cur != null){
ListNode tmp = cur.next; // 暂存后继节点 cur.next
cur.next = pre; // 修改 next 引用指向
pre = cur; // pre 暂存 cur
cur = tmp; // cur 访问下一节点
}
return per;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
LinkedList<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
recur(root, target);
return res;
}
public void recur(TreeNode root, int tar){
if(root == null) return;
path.add(root.val);
tar -= root.val;
if(tar == 0 && root.left == null && root.right == null){
res.add(new LinkedList(path));
}
recur(root.left, tar);
recur(root.right, tar);
path.removeLast();
}
}
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int num : nums){
if(set.contains(num)){
return num;
}else{
set.add(num);
}
}
return -1;
}
}
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i = matrix.length - 1, j = 0;//从左下角开始判断
while(i >= 0 && j < matrix[0].length){
if(target < matrix[i][j]){
i--;
}else{
if(target > matrix[i][j]){
j++;
}else{
return true;
}
}
}
return false;
}
}
class Solution {
public int minArray(int[] numbers) {
int i = 0 , j = numbers.length - 1;
while(i < j){
int m = (i+j)/2;
if(numbers[m] > numbers[j]){//如果中间数大于末尾,说明m在左排序数组,旋转点在(m,j)之间
i = m + 1;
}else{
if(numbers[m] < numbers[j]){//中间数小于末尾,说明m在右排序数组,旋转点在(i,m)之间
j = m;
}else{
j--;
}
}
}
return numbers[i];
}
}
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int l = 0, r = matrix[0].length-1, t = 0, b = matrix.length-1;
int x = 0;
int[] res = new int[(r+1)*(b+1)];
while(true){
for(int i = l; i <= r; i++){
res[x++] = matrix[t][i];
}
if(++t > b) break;
for(int i = t; i <= b; i++){
res[x++] = matrix[i][r];
}
if(--r < l) break;
for(int i = r; i >= l; i--){
res[x++] = matrix[b][i];
}
if(--b < t) break;
for(int i = b; i >= t; i--){
res[x++] = matrix[i][l];
}
if(++l > r) break;
}
return res;
}
}
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
}
class Solution {
int[] nums, tmp;
int res = 0;
public int reversePairs(int[] nums) {
this.nums = nums;
tmp = new int[nums.length];
mergeSort(0, nums.length - 1);
return res;
}
private void mergeSort(int left, int right){
if(left >= right) return;
int mid = (left + right) / 2;
mergeSort(left, mid);
mergeSort(mid + 1, right);
merge(left, mid, right);
}
private void merge(int left, int mid, int right){
int i = left;
int j = mid + 1;
int t = 0;
while(i <= mid && j <= right){//在数组合并前统计
if(nums[i] > nums[j]){//若左边数组最小的数都大于右边的数,则左边数组所有数都大于右边
tmp[t++] = nums[j++];
res += mid - i + 1;//统计逆序数对
}else{
tmp[t++] = nums[i++];
}
}
while(i <= mid){//说明右边统计完了
tmp[t++] = nums[i++];
}
while(j <= right){//左边的统计完了
tmp[t++] = nums[j++];
}
t = 0;
while(left <= right){
nums[left++] = tmp[t++];
}
}
}
53 - I. 在排序数组中查找数字 I E
统计一个数字在排序数组中出现的次数。
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
二分法找到重复数字的左右边界
class Solution {
public int search(int[] nums, int target) {
int i = 0, j = nums.length - 1;
while(i <= j){
int mid = (i+j)/2;
if(target >= nums[mid]) i = mid + 1;
else j = mid - 1;
}
int right = i;//上面二分查找完毕后,target的有边界就是i
if(j >= 0 && nums[j] != target) return 0;//说明数组中没有target
i = 0;
j = nums.length - 1;
while(i <= j){
int mid = (i+j)/2;
if(target > nums[mid]) i = mid + 1;
else j = mid - 1;
}
int left = j;
return right - left - 1;
}
}
53 - II. 0~n-1中缺失的数字 E
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
输入: [0,1,3]
输出: 2
排序数组中的搜索问题,首先想到 二分法 解决。
class Solution {
public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;
while(i <= j){
int mid = (i+j)/2;
if(mid == nums[mid]){//如果当前下标等于当前元素,说明mid左边没问题,向右找
i = mid + 1;
}else
j = mid - 1;//如果当前下标不等于当前元素,说明缺失的元素在左边
}
return i;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
if(l1.val <= l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
使用双指针则可以不用统计链表长度。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former = head, latter = head;//因为不知道链表长度,所以设置双指针
for(int i = 0; i < k ;i++){//循环结束后,快慢指针相差k
former = former.next;
}
while(former != null){//快慢指针相差k,当快指针到达链表结尾时,慢指针距离结尾k,即为倒数第k个节点
former = former.next;
latter = latter.next;
}
return latter;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head.val == val) return head.next;
ListNode pre = head, cur = head.next;
while(cur != null && cur.val != val){
pre = cur;
cur = cur.next;
}
if(cur != null){
pre.next = cur.next;
}
return head;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA, B = headB;
while(A != B){
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}
return A;
}
}
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
Map<Node, Node> map = new HashMap<>();
Node cur = head;
while(cur != null){
map.put(cur, new Node(cur.val));//先在map的val里复制每一个节点的值
cur = cur.next;
}
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);
}
}
class Solution {
public String replaceSpace(String s) {
StringBuilder str = new StringBuilder();
for(Character c : s.toCharArray()){
if(c == ' ') str.append("%20");
else str.append(c);
}
return str.toString();
}
}
class Solution {
List<String> res = new ArrayList<>();
char[] c;
public String[] permutation(String s) {
c = s.toCharArray();
dfs(0);
String[] str = new String[res.size()];
return res.toArray(str);
}
private void dfs(int x){
if(x == c.length - 1){//如果到了倒数第一个,前面的都固定了,只剩下一种排法
res.add(String.valueOf(c));
}
HashSet<Character> set = new HashSet<>();
for(int i = x; i < c.length; i++){
if(set.contains(c[i])){//出现重复的,直接跳过
continue;
}
set.add(c[i]);
swap(x, i);//将c[i]固定在第x位
dfs(x+1);//固定剩下的位
swap(x, i);//固定完后恢复
}
}
private void swap(int a, int b){
char tmp = c[a];
c[a] = c[b];
c[b] = tmp;
}
}
输入:s = “abaccdeff”
输出:‘b’
肯定是用键值对统计每个字母出现的次数
class Solution {
public char firstUniqChar(String s) {
HashMap<Character, Boolean> map = new HashMap<>();//用键值对存储
char[] ch = s.toCharArray();
for(Character c :ch){
map.put(c, map.containsKey(c));//存入<字符,是否出现过>
}
for(Character c : ch){
if(!map.get(c)){//如果某个键的值为否,说明该字母只出现了一次
return c;
}
}
return ' ';
}
}
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
quickSort(arr, 0, arr.length - 1);
return Arrays.copyOf(arr, k);
}
private void quickSort(int[] arr, int left, int right){
if(left >= right) return;
int i = left, j = right;
while(i < j){
while(i < j && arr[j] >= arr[left]) j--;
while(i < j && arr[i] <= arr[left]) i++;
swap(arr, i, j);
}
swap(arr, left, i);
quickSort(arr, left, i-1);
quickSort(arr, i+1, right);
}
private void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
class Solution {
public String minNumber(int[] nums) {
String[] arr = new String[nums.length];
for(int i = 0; i < nums.length; i++){
arr[i] = String.valueOf(nums[i]);
}
quickSort(arr, 0, nums.length-1);
StringBuilder res = new StringBuilder();
for(String s : arr){
res.append(s);
}
return res.toString();
}
public void quickSort(String[] arr, int l, int r){
if(l >= r) return;
int i = l, j = r;
String tmp = arr[i];
while(i < j){
while((arr[j]+arr[l]).compareTo(arr[l]+arr[j]) >= 0 && i < j) j--;
while((arr[i]+arr[l]).compareTo(arr[l]+arr[l]) <= 0 && i < j) i++;
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
arr[i] = arr[l];
arr[l] = tmp;
quickSort(arr,l,i-1);
quickSort(arr,i+1,r);
}
}
示例 1:
输入: s = “abcdefg”, k = 2
输出: “cdefgab”
法一:
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n,s.length()) + s.substring(0,n);
}
}
法二:
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder sb = new StringBuilder();
for(int i = n; i < n+s.length(); i++){
sb.append(s.charAt(i%s.length()));//取模,非常巧妙。i
//i>length时,可以取到前半段
}
return sb.toString();
}
}
class Solution {
public boolean isStraight(int[] nums) {
Set<Integer> set = new HashSet<>();
int max = 0, min = 14;//最大最小值是反过来设置的,这样他们才能更新
for(int num : nums){
if(num == 0) continue;//遇到0,跳过
min = Math.min(num,min);
max = Math.max(num,max);
if(set.contains(num)) return false;//如果包含重复的数字,直接退出
set.add(num);
}
return max - min < 5;//最大 - 最小 < 5,且无重复数字,必然可行
}
}
class Solution {
public int[] printNumbers(int n) {
int end = (int)Math.pow(10,n) - 1;//最大n位数为,10的n次方-1
int[] res = new int[end];
for(int i = 0; i < end; i++){
res[i] = i + 1;
}
return res;
}
}
本题若考虑大数则难度加大,之后再学
class Solution {
public String replaceSpace(String s) {
StringBuilder str = new StringBuilder();
for(Character c : s.toCharArray()){
if(c == ' ') str.append("%20");
else str.append(c);
}
return str.toString();
}
}
class Solution {
public int[] exchange(int[] nums) {
int i = 0, j = nums.length - 1, tmp;
while(i < j){
if(i<j && nums[i]%2==1) i++;
if(i<j && nums[j]%2==0) j--;
tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
return nums;
}
}
1.确定 n 所在 数字 的 位数 ,记为 digit ;
2.确定 n 所在的 数字 ,记为 num;
3.确定 n 是 num 中的哪一数位,并返回结果。
class Solution {
public int findNthDigit(int n) {
int digit = 1;//几位数1,2,3...
long start = 1;//几位数的最小一个1,10,100...可能超出int范围,所以用long
long count = 9;//几个数位9,180,2700...
while(n > count){//找到n是几位数
n -= count;//减掉几位数的开头数字,n = 该位数段从0开始数的第几位
digit += 1;
start *= 10;
count = 9 * digit * start;
}
long num = start + (n-1) / digit;//得到n所在的数字
String s = Long.toString(num);//转换乘Sting数组,便于找到n的位置
char c = s.charAt((n-1) % digit);//确定n在num的第几位
return c - '0';//将char转换为int
}
}
class Solution {
public int[] constructArr(int[] a) {
int[] res = new int[a.length];
for(int i = 0, p = 1; i < a.length; i++){//先遍历i左边的数,获得乘积
res[i] = p;
p *= a[i];
}
for(int i = a.length - 1, p = 1; i >=0; i--){//再遍历右边,相乘
res[i] *= p;
p *= a[i];
}
return res;
}
}
示例 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]){//判断栈顶元素是否与poped相等
stack.pop();//相等则弹出
i++;//继续比较poped中下一个,
}//不等则继续入栈
}
return stack.isEmpty();//若stack为空,表示弹出顺序与poped一致
}
}
class Solution {
int res = 0;
public int sumNums(int n) {
boolean x = n > 1 && sumNums(n -1) > 0;//n = 1 是终止递归的标志
res += n;
return res;
}
}
class Solution {
public boolean isNumber(String s) {
int n = s.length();
int index = 0;
boolean hasNum = false, hasDot = false, hasSign = false, hasE = false;
while(index < n && s.charAt(index) == ' '){//处理开头的空格
index++;
}
while(index < n){
while(index < n && s.charAt(index) >= '0' && s.charAt(index) <= '9'){//是数字跳过
index++;
hasNum = true;
}
if(index == n){//遍历到末尾则跳出循环
break;
}
char c = s.charAt(index);
if(c == 'e' || c == 'E'){//若e前面已经有e或e前没有数字则不能表示数值
if(hasE || !hasNum){
return false;
}
hasE = true;
hasNum = false; hasDot = false; hasSign = false;//e前面判断完成后,标志位置false, //继续判断e后面是不是数字
}else if(c == '.'){
if(hasDot || hasE){//若小数点前面已经有小数点或小数点前没有数字则不能表示数值
return false;
}
hasDot = true;
}else if(c == '+' || c == '-'){//若符号前面已经有符号或符号前没有数字或符号前面有数字则不能表示数值
if(hasSign || hasDot || hasNum){
return false;
}
hasSign = true;
}else if(c == ' '){
break;
}else {
return false;
}
index++;
}
while(index < n && s.charAt(index) == ' '){//处理末尾的空格
index++;
}
return hasNum && index == n;
}
}