刷题 1
// class Solution {
// public int maxArea(int[] a) {
// int max = 0;
// for(int i =0;i<a.length;i++){
// for(int j=i+1;j<a.length;j++){
// //双层遍历 保证遍历所有条件
// // int area = getArea(i,j);
// int area = (j-i)*Math.min(a[i],a[j]);
// max = Math.max(max , area) ;//擂台法
// }
// }
// return max;
// }
// }
// class Solution {
// public int maxArea(int[] a) {
// int max =0; //擂主
// for(int i=0 , j=a.length-1; i<j;) {
// int minHeight = a[i]<a[j]?a[i++]:a[j--];//两者之间的较小者
// int area = (j-i+1)*minHeight; //
//新擂主 旧擂主
// max = Math.max(area , max);//擂台比武保证所有情况都比较一遍
// }
// return max;
// }
// }
// res = height[i] < height[j] ?
// Math.max(res, (j - i) * height[i++]):
// Math.max(res, (j - i) * height[j--]);
class Solution {
public int maxArea(int[] height) {
int res =0 , j =height.length-1 ;
for(int i=0 ; i<j ;){
res = height[i]<height[j]?
Math.(res , (j-i)*height[i++]): //此时 res = Math.(res , (j-i)*height[i]) 还没加
Math.(res , (j-i)*height[j--]) //此时 res = Math.(res , (j-i)*height[j]) 还没减
}
}
}
刷题 2
class Solution {
public int climbStairs(int n) {
//想简单的问题 eg 先计算前 三个 再找规律
// 找重复子问题 泛化
//所有的问题 都可以分解成if else || for
//f3=f1+f2
int f1=1;
int f2=0;// 确保第一次循环中 第一项 / 第二项 均为 1
int f3=0;
//注意fblx 的 前两项为特殊情况
for (int i=0;i<n;i++){
f3 = f1+f2;
f2 =f1;
f1 = f3;
}
return f3;
}
}
三数之和3
// class Solution {
// public List<List<Integer>> threeSum(int[] num) {
// Arrays.sort(num);
// List<List<Integer>> res = new LinkedList<>();
// for (int i = 0; i < num.length-2; i++) {
// if (i == 0 || (i > 0 && num[i] != num[i-1])) { // 可删?
// int lo = i+1, hi = num.length-1, sum = 0 - num[i];
// while (lo < hi) {
// if (num[lo] + num[hi] == sum) {
// res.add(Arrays.asList(num[i], num[lo], num[hi]));
// while (lo < hi && num[lo] == num[lo+1]) lo++;
// while (lo < hi && num[hi] == num[hi-1]) hi--;
// lo++; hi--;
// } else if (num[lo] + num[hi] < sum) lo++;
// else hi--;
// }
// }
// }
// return res;
// }
// }
class Solution {
public List<List<Integer>> threeSum(int[] num) {
//使用双指针前 将 数组排序
Arrays.sort(num);
List<List<Integer>> res = new LinkedList<>();
//将第一个作为 target || a+b+ target =0;
for(int i=0 ;i<num.length-2;i++){ //控制循环次数
if(num[i]>0){
break; //num[i] > 0 则num[i+1]也大于0 不比计算
}
// 新的target 旧的target
if( i==0||i>0&&num[i]!=num[i-1]){ //对于重复元素:跳过,避免出现重复解(题目哟求) why
int lo =i+1,hi = num.length -1 , sum =0-num[i];
while(lo<hi){ //结束条件
if(num[lo]+num[hi]==sum){
res.add(Arrays.asList(num[i],num[lo],num[hi])) ;//数组转list 与下一步无关
// 先添加完后再去重
while (lo < hi && num[lo] == num[lo+1]) lo++;
// while (lo < hi && num[hi] == num[hi-1]) hi--;
while (lo < hi && num[hi] == num[hi-1]) hi--;
lo++; //前提条件=>num[lo]+num[hi]==sum 则直接 lo++;hi--;
hi--;
}else if(num[lo]+num[hi]<sum){
lo++;
}else{
hi--;
}
}
}
}
return res;
}
}
4
// class Solution {
// public int[] twoSum(int[] numbers, int target) {
// int[] result = new int[2];
// Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// for (int i = 0; i < numbers.length; i++) {
// if (map.containsKey(target - numbers[i])) {
// result[1] = i;
// result[0] = map.get(target - numbers[i]);//该key值是否存在于map中 并返回 value值
// return result;
// }
// map.put(numbers[i], i); // a+b = target 现将 a存入 则遍历到b时会与map中的a进行匹配
// }
// return result;
// }
// }
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] res =new int[2];
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<numbers.length;i++){
if(map.containsKey(target-numbers[i])){ //在map中找到匹配项
res[0] = map.get(target - numbers[i]); //作为key
res[1] = i; //作为value
return res;
}
//与if同级
map.put(numbers[i],i); //将遍历的数据保存到map中 , 第1+n次循环与之进行匹配
}
}
}
5
// //最近相关性 用stack
// //最外层==最外层...内层==内层1
// class Solution {
// public boolean isValid(String s) {
// Stack<Character> stack = new Stack<Character>(); //创建一个stack
// for (char c : s.toCharArray()) { //增强for遍历String
// if (c == '(')
// stack.push(')');
// else if (c == '{')
// stack.push('}');
// else if (c == '[')
// stack.push(']');
// // 如何不执行前面的 则会执行后面的
// else if (stack.isEmpty() || stack.pop() != c)//stack.pop()会在判断是执行
// return false;
// }
// return stack.isEmpty();
// }
// }
//最近相关性 用stack
//最外层==最外层...内层==内层
class Solution {
public boolean isValid(String s) {
//创建stack
Stack<Character> stack = new Stack<Character>(); //创建一个stack
for(char c :s.toCharArray()){
if(c=='{'){
stack.push('}');
}else if(c=='['){
stack.push(']');
}else if(c=='('){
stack.push(')');
}else{
if(stack.isEmpty()==true||stack.pop()!=c){
return false;
}
}
}
return stack.isEmpty();
}
}
6
//283
// class Solution {
// public void moveZeroes(int[] nums) {
// int j=0; //记录下标 ,j只有在符合条件下才能改变
// for(int i =0 ; i< nums.length ; i++){
// if(nums[i]!=0){
// nums[j] = nums[i];
// if(i!=j){//why? ,j只有在符合条件下才能改变
// nums[i]=0;
// }
// j++;
// }
// }
// }
// }
// class Solution {
// public void moveZeroes(int[] nums) {
// if(nums==null) {
// return;
// }
// //两个指针i和j
// int j = 0;
// for(int i=0;i<nums.length;i++) {
// //当前元素!=0,就把其交换到左边,等于0的交换到右边
// if(nums[i]!=0) {
// int tmp = nums[i];
// nums[i] = nums[j];
// nums[j++] = tmp;
// }
// }
// }
// }
//思路 把什么 放在 左边
//交换的条件是什么 !=0
class Solution {
public void moveZeroes(int[] nums) {
//判断边缘条件
if(nums==null) {
return;
}
int j=0;// 从下标0开始
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
int t=nums[i]; //要交换的值(相对后面的值) 赋给临时变量
nums[i]=nums[j];
nums[j++]=t;
}
}
}
}
7
// class MinStack {
// int min = Integer.MAX_VALUE;
// Stack<Integer> stack = new Stack<Integer>();
// public void push(int x) {
// if(x <= min){ //劫持 获取最小值
// stack.push(min);
// min=x;
// }
// stack.push(x);
// }
// public void pop() {//万一删了最小值
// if(stack.pop() == min) min=stack.pop(); //保存最小值在他被删除前
// }
// public int top() {
// return stack.peek(); //获取站顶元素
// }
// public int getMin() { //获取最小值
// return min;
// }
// }
// public class MinStack {
// // 数据栈
// private Stack<Integer> data;
// // 辅助栈
// private Stack<Integer> helper;
// public MinStack() {
// data = new Stack<>();
// helper = new Stack<>();
// }
// // 思路 1:数据栈和辅助栈在任何时候都同步(stack的长度也要同步)
// public void push(int x) {
// // 数据栈和辅助栈一定会增加元素
// data.add(x);
// if (helper.isEmpty() || helper.peek() >= x) {
// helper.add(x); //
// } else {
// helper.add(helper.peek()); //同步所需
// }
// }
// public void pop() {
// // 两个栈都得 pop
// if (!data.isEmpty()) {
// helper.pop();
// data.pop();
// }
// }
// public int top() {
// if(!data.isEmpty()){
// return data.peek();//
// }
// throw new RuntimeException("栈中元素为空,此操作非法");
// }
// public int getMin() {
// if(!helper.isEmpty()){
// return helper.peek();//
// }
// throw new RuntimeException("栈中元素为空,此操作非法");
// }
// }
class MinStack {
// 数据栈
private Stack<Integer> data;
// 辅助栈
private Stack<Integer> helper;
public MinStack() {
data = new Stack<>();
helper = new Stack<>();
}
public void push(int val) {
data.push(val);
if (helper.isEmpty() || helper.peek() >= val) { //确保添加的为最小值
helper.add(val);
}else{
helper.add(helper.peek()); //就是不能添加比peek大的值
}
}
public void pop() {
data.pop();
helper.pop();
}
public int top() {
return data.peek();
}
public int getMin() {
return helper.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
- 8_84
package com.leetCode;
//import java.util.ArrayDeque;
//import java.util.Deque;
//
//public class Solution {
// public int largestRectangleArea(int[] heights) {
// int len = heights.length;
// if (len == 0) {
// return 0;
// }
//
// if (len == 1) {
// return heights[0];
// }
/
// int res = 0;
/扩充数组长度
// int[] newHeights = new int[len + 2];//增加了两个长度
// newHeights[0] = 0;// 新数组的第一位是0
// System.arraycopy(heights, 0, newHeights, 1, len);
// newHeights[len + 1] = 0;// 新数组的最后一位是0
// len += 2;
// heights = newHeights;
///
Java集合提供了接口Deque来实现一个双端队列,它的功能是:
既可以添加到队尾,也可以添加到队首;
既可以从队首获取,又可以从队尾获取。
// Deque<Integer> stack = new ArrayDeque<>(len);//长度为len
// // 先放入哨兵,在循环里就不用做非空判断
// stack.addLast(0); //
//
// for (int i = 1; i < len; i++) {
// while (heights[i] < heights[stack.peekLast()]) { //右边严格小于 获取队尾元素但不删除
// int curHeight = heights[stack.pollLast()]; //(记录当前柱状体的高度,同时,柱状体边长向左拓展)
// int curWidth = i - stack.peekLast() - 1; //(边长向左拓展的时候stack.peekLast() 值减少 curwidth的值在增加)取队尾元素但不删除 队尾先进后厨
// res = Math.max(res, curHeight * curWidth);
// }
// stack.addLast(i); //将数组下标添加到队尾
// }
// return res;
// }
//
// public static void main(String[] args) {
// int[] heights = {2, 1, 5, 6, 2, 3};
int[] heights = {1, 1};
// Solution solution = new Solution();
// int res = solution.largestRectangleArea(heights);
// System.out.println(res);
// }
//}
// @lc code=start
import java.util.ArrayDeque;
import java.util.Deque;
public class Solution {
public int largestRectangleArea(int[] heights) {
int len = heights.length;
if (len == 0) {
return 0;
}
if (len == 1) {
return heights[0];
}
///
int res = 0;
///扩充数组长度
int[] newHeights = new int[len + 2];//增加了两个长度
newHeights[0] = 0;// 新数组的第一位是0
System.arraycopy(heights, 0, newHeights, 1, len);
newHeights[len + 1] = 0;// 新数组的最后一位是0
len += 2;
heights = newHeights;
/
// Java集合提供了接口Deque来实现一个双端队列,它的功能是:
// 既可以添加到队尾,也可以添加到队首;
// 既可以从队首获取,又可以从队尾获取。
Deque<Integer> stack = new ArrayDeque<>(len);//长度为len
// 先放入哨兵,在循环里就不用做非空判断
stack.addLast(0); //
for (int i = 1; i < len; i++) {
while (heights[i] < heights[stack.peekLast()]) {
// while (heights[i] < heights[i + 1]) {
int curHeight = heights[stack.pollLast()]; //记录栈顶并出站
int curWidth = (i - stack.peekLast()-1); //计算width后并将指针向左移两位
res = Math.max(res ,curHeight * curWidth);
}
stack.addLast(i);//计算width
}
return res;
}
public static void main(String[] args) {
int[] heights = {2, 1, 5, 6, 2, 3};
// int[] heights = {1, 1};
Solution solution = new Solution();
int res = solution.largestRectangleArea(heights);
System.out.println(res);
}
}
202
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
// class Solution {
// public ListNode reverseList(ListNode head) {
// //申请节点,pre和 cur,pre指向null
// ListNode pre = null;
// ListNode cur = head;
// ListNode tmp = null; //暂存指针,防止丢失
// while(cur!=null) {
// //记录当前节点的下一个节点
// tmp = cur.next; //
// //然后将当前节点指向pre
// cur.next = pre;
// //pre和cur节点都前进一位
// pre = cur;
// cur = tmp;
// }
// return pre;
// }
// }
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
ListNode tmp=null;
// ListNode next = null;
while(cur!=null){
tmp = cur.next; //先保存下个指针
cur.next = pre; //将指针反转 ==断链表
pre = cur; //pre指针右移 结束时指向非null
cur =tmp; //cur指针右移
}
return pre;
}
}
24
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
// 找终止条件:本题终止条件很明显,当递归到链表为空或者链表只剩一个元素的时候,没得交换了,自然就终止了。
// 找返回值:返回给上一层递归的值应该是已经交换完成后的子链表。
// 单次的过程:因为递归是重复做一样的事情,所以从宏观上考虑,只用考虑某一步是怎么完成的。我们假设待交换的俩节点分别为head和next,next的应该接受上一级返回的子链表(参考第2步)。就相当于是一个含三个节点的链表交换前两个节点,就很简单了,想不明白的画画图就ok。
// class Solution {
// public ListNode swapPairs(ListNode head) {
// if (head == null || head.next == null)
// return head;
// ListNode rest = head.next.next; //保存第三个节点 下一个递归的开始
交换//
// ListNode newHead = head.next;
// newHead.next = head; //
//
// head.next = swapPairs(rest);//next的应该接受上一级返回的子链表(参考第2步)
// return newHead; // (newHead.next=head,head.next=return newHead)一个递归;
// }
// }
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null||head.next==null){
return head;
}
递归参数///
ListNode tmp = head.next.next;
交换//
ListNode newHead = head.next;
newHead.next = head;
入口//
head.next = swapPairs(tmp);
///返回值/
return newHead;
}
}
94_遍历二叉树
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inorder( root , res);
return res;
}
void inorder(TreeNode root, List<Integer> res){
if(root==null){
return;
}
//递归中的root.left root.right 是相对子根节点的而言的
inorder(root.left , res);//递归第一层开始
res.add(root.val);
inorder(root.right , res);//递归最后一层开始
}
}
后续遍历根节点 590
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> postorder(Node root) {//Node类型
dfs(root);
return res;
}
void dfs(Node root){
if(root==null){
return;
}
//从前到后递归并调用
//放在递归函数上方 从外向里加(从前向后加)
// res.add(root.val);
//依次遍历一个根节点的儿子级别的节点
for(Node node : root.children){//
dfs(node);
}
//递归到最后一层在开始调用
//放在递归函数下方 从里向外加(从后向前加)
res.add(root.val);
}
}
589
// 很久没有见过这么正宗的广度优先遍历了,不多说直接开搞
// class Solution {
// // 创建一个全局集合来保存数的遍历
// List<List<Integer>> list=new ArrayList<List<Integer>> ();
// public List<List<Integer>> levelOrder(Node root) {
// // 首先创建一个队列进行广度优先遍历
// Queue<Node> queue=new LinkedList<Node>();//
// if(root==null){
// return list;
// }
// queue.offer(root);
// while(queue.isEmpty()==false){
// List<Integer> temp=new ArrayList<Integer>();//创建->在什么适当的时候调用(遍历当前层级的)
// int cout = queue.size();//
// for(int i=0;i<cout;i++){
// //什么时候将queue的元素取出 , 取出的数量为多少呢
// Node node=queue.poll();//先进先出//取出来的这个(保存他的值,将她的子节点保存到queue中)
// temp.add(node.val);//保存当前出队列节点的值
// //取出队列 node 将node.children 在放入队列中
// for(Node node1 : node.children){ //同时遍历当前层级的多个节点的N个子节点
// queue.add(node1);//这时候父节点还没完全出队列
// }
// }
// list.add(temp);
// }
// return list;
// }
// }
// 拆解逻辑:
// 找到一层的起始位置(准备要遍历的数据)
// 在 1 的基础上遍历一层
// 在 2 的基础上遍历的同时向下遍历每个节点的子节点
// 在 3 的基础上,把遍历时遇到的子节点按左——>右的顺序保存起来
// 重复 1 ,直到在 3 里面没有子节点
// 要遍历,那就需要有容器来装需要遍历的数据。
// 容器应该满足:顺序遍历、能扩容
// 这里我选用 deque 作为遍历的容器
// 把 root 作为种子遍历。
// 伪代码:
// 申请第一层遍历容器,并把 root 作为种,压入遍历容器。
// while (二叉树非底层) {
// 申请当前层值的容器
// while (当前层遍历容器) {
// 获取一个节点;
// 把当前节点的值,压入当前层值的容器内;
// 申请下层遍历节点的容器;
// while (遍历该节点的子节点) {
// 把该节点的子节点,压入下层遍历节点容器内
// }
// }
// 下层遍历节点容器,赋值给当前层遍历容器;
// 保存当前层值的容器,并累加;
// }
// 返回遍历累加后每层的值
22
// 当前左右括号都有大于 00 个可以使用的时候,才产生分支;
// 产生左分支的时候,只看当前是否还有左括号可以使用;
// 产生右分支的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支;
// 在左边和右边剩余的括号数都等于 00 的时候结算。
class Solution {
List<String> res = new ArrayList<>();
public List<String> generateParenthesis(int n) {
_generate(0, 0, n, "");
return res;
}
private void _generate(int left, int right, int n, String s) {
//先写终止条件
if (left ==n && right == n) {
res.add(s);
return ;//每一个满足条件的递归return掉
}
///逻辑层 + 递归层
//总共调用递归次数 二的六次方
//从最外层开始向里调用
if (left < n) {
_generate(left+1, right , n, s + "(");//递归到最深层时程序开始向下走
}
if(right<left){
//从最深层开始向外调用
_generate(left , right +1, n, s + ")");
}
//清除当前层 局部变量不用清除
}
}
236
// class Solution {
// public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// if (root == null) return null;
// // 如果p,q为根节点,则公共祖先为根节点
// if (root.val == p.val || root.val == q.val) return root;
// // 如果p,q在左子树,则公共祖先在左子树查找
// if (find(root.left, p) && find(root.left, q)) {
// return lowestCommonAncestor(root.left, p, q);
// }
// // 如果p,q在右子树,则公共祖先在右子树查找
// if (find(root.right, p) && find(root.right, q)) {
// return lowestCommonAncestor(root.right, p, q);
// }
// // 如果p,q分属两侧,则公共祖先为根节点
// return root;
// }
// private boolean find(TreeNode root, TreeNode c) {
// //遍历一侧树的所有节点 是否===root.val
// if (root == null) return false;
// if (root.val == c.val) {
// return true;
// }
// return find(root.left, c) || find(root.right, c);//如果左边不执行 则执行右边
// }
// }
104
class Solution {
// int leftHeight =0;
// int rightHeight =0;
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
// int leftHeight =1;
// int rightHeight =0;
//从null往根节点遍历
int leftHeight = maxDepth(root.left)+1;
int rightHeight = maxDepth(root.right)+1;
return Math.max(leftHeight , rightHeight);
}
}
111
// class Solution {
// //中间根节点为null时不参与计算
// public int minDepth(TreeNode root) {
// if(root == null) return 0;
// // 从根节点开始
// int L = minDepth(root.left);
// int R = minDepth(root.right);
// //左叶节点为空
// if(L==0) return R + 1;
// //右叶节点为空
// else if (R==0) return L + 1;
// //这是个返回值
// else return Math.min(L, R) + 1;//不区分左右
// }
// }
class Solution {
//中间根节点为null时不参与计算
public int minDepth(TreeNode root) {
if(root==null){
return 0;
}
int L = minDepth(root.left);
int R = minDepth(root.right);
if(L==0){
return R+1;
}else if(R==0){
return L+1;
}else{
return Math.min(L,R)+1;//
}
}
}
226
class Solution {
public TreeNode invertTree(TreeNode root) {
//递归函数的终止条件,节点为空时返回
if(root==null) {
return null;
}
//下面三句是将当前节点的左右子树交换
TreeNode tmp = root.right; //找最小重复子单元()
root.right = root.left;
root.left = tmp;
//递归交换当前节点的 左子树
invertTree(root.left);
//递归交换当前节点的 右子树
invertTree(root.right);
//函数返回时就表示当前这个节点,以及它的左右子树
//都已经交换完了
return root;
}
}
98
// class Solution {
// 满足中序遍历出来的是一个递增数组
//中序遍历时,判断当前节点是否大于中序遍历的前一个节点,如果大于,说明满足 BST,继续遍历,否则直接返回 false。
// long pre = Long.MIN_VALUE; //表示遍历当前节点的前一个节点
// public boolean isValidBST(TreeNode root) {
// if(root==null){
// return true;
// }
// //访问左节点
// if(isValidBST(root.left)==false){
// return false;
// }
// if(root.val<=pre){//
// return false;
// }
// pre = root.val;
// //
// if(isValidBST(root.right)==false){
// return false;
// }
// return true;
// // return isValidBST(root.right);
// }
// }
//bst
class Solution {
long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root ==null){
return true;
}
//确定顺序 从 左子树的 最左的叶节点开始->到右子树的 最右的叶节点
if(isValidBST(root.left)==false){
return false;
}
// 当前的右 上一级左
if(root.val<=pre){
return false;
}
pre = root.val;
//右子树从左向右遍历
//root.right 比较的是root.left
if(isValidBST(root.right)==false){
return false;
}
return true;
}
}
105
/**
* 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;
* }
* }
*/
///************************************************* */
// :递归、区间分治。使用左闭右闭区间更加方便
//先构造根节点,再构造**左子树**,接下来构造**右子树**
//构造**左子树**和**右子树**是一个子问题,递归处理即可
//因此我们只关心如何构造根节点,以及如何递归构造左子树和右子树。
// 数组本身 arr
// 数组的起始位置 lo
// 数组的结束位置 hi
// class Solution {
// public TreeNode buildTree(int[] preorder, int[] inorder) {
// return helper(0, 0, inorder.length - 1, preorder, inorder);
// }
// public TreeNode helper(int preStart, int inStart, int inEnd, int[] preorder, int[] inorder) {
// if (preStart > preorder.length - 1 || inStart > inEnd) {
// return null;
// }
// TreeNode root = new TreeNode(preorder[preStart]);
// int inIndex = 0; // Index of current root in inorder
// for (int i = inStart; i <= inEnd; i++) {
// if (inorder[i] == root.val) {
// inIndex = i;
// }
// }
// root.left = helper(preStart + 1, inStart, inIndex - 1, preorder, inorder);
// root.right = helper(preStart + inIndex - inStart + 1, inIndex + 1, inEnd, preorder, inorder);
// return root;
// }
// }
class Solution {
private Map<Integer, Integer> indexMap;// 定义全局变量
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length; //记录数组长度
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i], i);//将中序遍历的所有键值对存到indexMap
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
//前序遍历的节点//
public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
//前序遍历从上向下遍历 中序遍历从左往右遍历
//重复子单元 确定根节点的位置 完成根节点的创建并确定子节点的位置
if (preorder_left > preorder_right) {//下标的比较
return null;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;//
// 在中序遍历中定位根节点
int inorder_root = indexMap.get(preorder[preorder_root]);//祖节点的子节点
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目->只能通过中序遍历得知
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);//节点按前序遍历移动
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;//每一次递归返回/\形状
}
}
78
// DFS和回溯法主要区别在于状态重置,DFS在访问过一个结点之后会标记为访问过,这个标记不会被撤销。回溯法也会对结点进行标记,但是在回溯之后会撤销这个标记。例如寻找条路径,DFS在找到一条后就会结束,回溯法可以将所有的路径找到。
//从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到” 尽头 “的时候, 再倒回出发点, 从另一个可能出发, 继续搜索. 这种不断” 回溯 “寻找解的方法
// class Solution {
// public List<List<Integer>> subsets(int[] nums) {
// List<List<Integer>> res = new ArrayList<>();
// backtrack(0, nums, res, new ArrayList<Integer>());
// return res;//三步走
// }
// //
// private void backtrack(int i, int[] nums, List<List<Integer>> res, ArrayList<Integer> tmp) {
// res.add(new ArrayList<>(tmp));//从空数组开始加
// for (int j = i; j < nums.length; j++) {//结束的条件只要这一层没有进入递归就算结束
// tmp.add(nums[j]);//j = 递归返回的j+1;
// backtrack(j + 1, nums, res, tmp);//
// tmp.remove(tmp.size() - 1);//一定要在递归函数的下面->返回上一级->前一个数字
// }
// }
// }
// result = [] //结果集
// def backtrack(路径, 选择列表):
// if 满足结束条件:
// result.add(路径) //把已经做出的选择添加到结果集;
// return //一般的回溯函数返回值都是空;
// for 选择 in 选择列表: //其实每个题的不同很大程度上体现在选择列表上,要注意这个列表的更新,
// //比如可能是搜索起点和终点,比如可能是已经达到某个条件,比如可能已经选过了不能再选;
// 做选择 //把新的选择添加到路径里;路径.add(选择)
// backtrack(路径, 选择列表) //递归;
// 撤销选择 //回溯的过程;路径.remove(选择)
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
backtrack(0, nums, res, new ArrayList<Integer>());
return res;//三步走
}
//
private void backtrack(int i, int[] nums, List<List<Integer>> res, ArrayList<Integer> tmp) {
// res.add(new ArrayList(path)):开辟一个独立地址,地址中存放的内容为path链表,后续path的变化不会影响到res
// res.add(path):将res尾部指向了path地址,后续path内容的变化会导致res的变化。
//注 引用里存的引用也会随着实例对象的改变而改变。
res.add(new ArrayList<>(tmp));//累计保存
for(int j=i;j<nums.length; j++){
tmp.add(nums[j]);//x
backtrack(j+1 , nums , res , tmp);
tmp.remove(tmp.size()-1);//删除最后一个元素
}
}
}
50
// class Solution {
// public double myPow(double x, int n) {
// long N = n;
// return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
// }
// public double quickMul(double x, long N) {
// if (N == 0) {
// return 1.0;
// }
// double y = quickMul(x, N / 2);
// return N % 2 == 0 ? y * y : y * y * x;//返回的一个值
// }
// }
class Solution {
// double y;
public double myPow(double x, int n) {
long N = n;
return N >0 ? fastPow(x , N) : 1.0/fastPow(x , -N);
}
public double fastPow(double x , long N){
if(N==0){
return 1.0;
}
double y= fastPow(x,N/2); //用一个变量接受函数返回的值->实现数据的递增
return N%2==0? y*y : y*y*x;
}
}
169
// 它表示Map中的一个实体(一个key-value对)
// 接口中有getKey(),getValue方法(很重要的两个方法)
// 如果我们把众数记为 +1+1,把其他数记为 -1−1,将它们全部加起来,显然和大于 0,从结果本身我们可以看出众数比其他数多。
// class Solution {
// public int majorityElement(int[] nums) {
// int ret = nums[0];
// int count = 1;
// for(int num : nums) {
// if(num != ret) {
// count--;
// if(count == 0) {
// count = 1;
// ret = num;
// }
// }
// else
// count++;
// }
// return ret;
// }
// }
// keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> counts = countNums(nums);
最终结果
Map.Entry<Integer, Integer> majorityEntry = null;//只含一对的key || value的类型
for (Map.Entry<Integer, Integer> entry : counts.entrySet()) {
if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {
majorityEntry = entry;
}
}
return majorityEntry.getKey();
}
private Map<Integer, Integer> countNums(int[] nums) {
Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
for (int num : nums) {
if (!counts.containsKey(num)) {
counts.put(num, 1);
} else {
counts.put(num, counts.get(num) + 1);
}
}
return counts;
}
}
455
class Solution {
public int maxProfit(int[] prices) {
int result = 0;
for(int i=1;i<prices.length;i++) {
if(prices[i]>prices[i-1]){
result = result+ prices[i]-prices[i-1];
}else{
continue;
}
}
return result;
}
}
200
class Solution {
public int numIslands(char[][] grid) {
int islandNum = 0;
for(int i = 0; i < grid.length; i++){//row
for(int j = 0; j < grid[0].length; j++){//col
if(grid[i][j] == '1'){
//找到陆地时开始搜索
infect(grid, i, j);//
//找不到陆地结束搜索
islandNum++;
}
}
}
return islandNum;
}
//感染函数
public void infect(char[][] grid, int i, int j){
//终止条件 超过上下左右边界||遇到0
if(i < 0 || i >= grid.length ||
j < 0 || j >= grid[0].length || grid[i][j] != '1'){
return;//返回上一级递归
}
//深度优先搜索 递归函数传参level+1
grid[i][j] = '2';
infect(grid, i + 1, j);//相邻的两个递归函数==第一个先递归到终点在开始第二的递归
infect(grid, i - 1, j);
infect(grid, i, j + 1);
infect(grid, i, j - 1);
}
// public static void main(String[] args) {
// Solution solution = new Solution();
// char[][] s = new char[][] {{'0', '0', '1', '1', '0'},
// {'0', '1', '0', '1', '0'},
// {'1', '1', '0', '0', '1'},
// {'0', '0', '1', '1', '0'}};
// System.out.println(solution.numIslands(s));
// }
}
51
// class Solution {
// List<List<String>> ansList = new ArrayList<>();
// private final int N = 22;
// private int[][] path = new int[N][N];
// private boolean[] col = new boolean[N];
// private boolean[] dg = new boolean[N];
// private boolean[] udg = new boolean[N];
// private int n;
// public List<List<String>> solveNQueens(int n) {
// this.n = n;//转 全局变量
// if(n == 1) {
// List<String> ls = new ArrayList<>();
// ls.add("Q");
// ansList.add(ls);
// return ansList;
// }
// dfs(0);
// return ansList;
// }
// public void dfs(int u) {
// if(u == n) { //终止条件
// List<String> row = new ArrayList<>();
// ///构造棋盘
// for (int i = 0; i < n; i++) {
// StringBuilder sb = new StringBuilder();
// for (int j = 0; j < n; j++) {//
// if (path[i][j] == 0) { //
// sb.append(".");
// } else {
// sb.append("Q");
// }
// }
// row.add(sb.toString());//保存每一层所有个体的数据
// }
// //构造结束 并储存棋盘
// ansList.add(row);//保存所有层级的数据
// return;
// }
// for(int i = 0; i < n; i++) {
// //if ==false
// if(!col[i] && !dg[i+u] && !udg[i-u+n]) {//在这一点都不冲突
// //给所有的的棋盘先赋值编号
// //相对上一级
// path[u][i] = 1;
// col[i] = true;
// dg[i+u] = true;//
// udg[i-u+n] = true; //
// dfs(u+1);//进入下一行
// ///这是?????
// //回溯,撤销皇后
// //相对下一级
// col[i] = false;
// dg[i+u] = false;
// udg[i-u+n] = false;
// path[u][i] = 0;
// }
// }
// }
// }
// void backtracking(参数) {
// if (终止条件) {
// 存放结果;
// return;
// }
// for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
// 处理节点;
// backtracking(路径,选择列表); // 递归
// 回溯,撤销处理结果
// }
// }
// class Solution {
// public List<List<String>> solveNQueens(int n) {
// List<List<String>> solutions = new ArrayList<List<String>>();
// int[] queens = new int[n];
// Arrays.fill(queens, -1);//
// Set<Integer> columns = new HashSet<Integer>(); //
// Set<Integer> diagonals1 = new HashSet<Integer>();
// Set<Integer> diagonals2 = new HashSet<Integer>();
// backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
// return solutions;
// }
// public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
// if (row == n) {
// List<String> board = generateBoard(queens, n);//构造棋盘
// solutions.add(board);
// } else {
// for (int i = 0; i < n; i++) {
// if (columns.contains(i)) {//保存皇后所在列
// continue;//结束本次循环
// }
// int diagonal1 = row - i;//判断是否在对角线==看横坐标-纵坐标的值是否唯一
// if (diagonals1.contains(diagonal1)) {//是否在同一对角线
// continue;//结束本次循环
// }
// int diagonal2 = row + i;
// if (diagonals2.contains(diagonal2)) {//是否在同一对角线
// continue;//结束本次循环
// }
// queens[row] = i;//记录皇后在每一行的什么位置
// columns.add(i);//记录皇后在的列数
// diagonals1.add(diagonal1);//横坐标-纵坐标的值
// diagonals2.add(diagonal2);//横坐标+纵坐标的值
// backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
// queens[row] = -1;
// columns.remove(i);
// diagonals1.remove(diagonal1);
// diagonals2.remove(diagonal2);
// }
// }
// }
// public List<String> generateBoard(int[] queens, int n) {
// List<String> board = new ArrayList<String>();
// for (int i = 0; i < n; i++) {
// char[] row = new char[n];
// Arrays.fill(row, '.');
// row[queens[i]] = 'Q'; //保存每一层所有个体的数据
// board.add(new String(row));保存所有层级的数据
// }
// return board;
// }
// }
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> solutions = new ArrayList<List<String>>();
int[] queens = new int[n];
Arrays.fill(queens, -1);//
Set<Integer> columns = new HashSet<Integer>(); //
Set<Integer> diagonals1 = new HashSet<Integer>();
Set<Integer> diagonals2 = new HashSet<Integer>();
backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
return solutions;
}
public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
List<String> board = generateBoard(queens, n);//构造棋盘
solutions.add(board);
} else {
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {//保存皇后所在列
continue;//结束本次循环
}
int diagonal1 = row - i;//判断是否在对角线==看横坐标-纵坐标的值是否唯一
if (diagonals1.contains(diagonal1)) {//是否在同一对角线
continue;//结束本次循环
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {//是否在同一对角线
continue;//结束本次循环
}
queens[row] = i;//记录皇后在每一行的什么位置
columns.add(i);//记录皇后在的列数
diagonals1.add(diagonal1);//横坐标-纵坐标的值
diagonals2.add(diagonal2);//横坐标+纵坐标的值
backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
queens[row] = -1;
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
}
}
public List<String> generateBoard(int[] queens, int n) {
List<String> board = new ArrayList<String>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[queens[i]] = 'Q'; //保存每一层所有个体的数据
board.add(new String(row));保存所有层级的数据
}
return board;
}
}
55
// class Solution {
// public static boolean canJump(int[] nums) {
// if (nums == null) {
// return false;
// }
// //前n-1个元素能够跳到的最远距离
// int k = 0;
// for (int i = 0; i <= k; i++) {
// //第i个元素能够跳到的最远距离
// int temp = i + nums[i];
// //更新最远距离
// k = Math.max(k, temp);
// //如果最远距离已经大于或等于最后一个元素的下标,则说明能跳过去,退出. 减少循环
// if (k >= nums.length - 1) {
// return true;
// }
// }
// //最远距离k不再改变,且没有到末尾元素
// return false;
// }
// }
class Solution {
public static boolean canJump(int[] nums) {
if(nums==null)
{
return false;
}
int k=0;
for(int i=0;i<=k;i++){
int temp = i+nums[i];
k=Math.max(temp , k);
if(k>=nums.length -1){
return true;
}
}
return false;
}
}
1143
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
if(text1.length() == 0 || text2.length() == 0) return 0;
int m = text1.length();
int n = text2.length();
//
int[][] dp = new int[m+1][n+1];//
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(text1.charAt(i-1) == text2.charAt(j-1)){//如果遇到相同的则加一
// dp[i][j] = dp[i-1][j]+dp[i][j-1]+1;
//当 text1[i - 1] == text2[j - 1] 时,说明两个子字符串的最后一位相等,最长公共子序列又增加了1,所以 dp[i][j] = dp[i - 1][j - 1] + 1
dp[i][j] = dp[i-1][j-1] + 1;// dp[i][j]记录的是最终结果
}else{
dp[i][j] = Math.max(dp[i-1][j] ,dp[i][j-1]);
}
}
}
return dp[m][n];
}
}
62
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for (int i = 0; i < n; i++) dp[0][i] = 1;//数据初始化
for (int i = 0; i < m; i++) dp[i][0] = 1;///数据初始化
for (int i = 1; i < m; i++) {//
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];//这一点的走法=响铃相邻的row加上col
}
}
return dp[m - 1][n - 1];
}
}
63
// class Solution {
// public int uniquePathsWithObstacles(int[][] obstacleGrid) {
// int m = obstacleGrid.length;
// int n = obstacleGrid[0].length;
// int[][] dp = new int[m][n];
// //初始化注意遇到障碍物时就将阻碍物的位置及其后面的位置都初始化为0
// for(int i = 0;i < m;i++){
// if(obstacleGrid[i][0] == 1){
// break;
// }
// dp[i][0] = 1;
// }
// for(int j = 0;j < n;j++){
// if(obstacleGrid[0][j] == 1){
// break;
// }
// dp[0][j] = 1;
// }
// for(int i = 1;i < m;i++){
// for(int j = 1;j < n;j++){
// if(obstacleGrid[i][j] == 0){
// //约束条件
// dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
// }
// }
// }
// return dp[m - 1][n - 1];
// }
// }
// 遇到obstacleGrid的值为1的首行或首列,那么不管这一行(列)后面的值是0还是1,都不需要再往后初始化了
// 多申请一个位置就可以不用判断越界了
解法三:动态规划
定义二维 dp 数组,将解法二中「自顶向下的递归」改为「自底向上的递推」。
1、状态定义:
dp[i][j]dp[i][j] 表示从点 (i, j)(i,j) 到底边的最小路径和。
2、状态转移:
dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
自底向上的优势是①可以规避边界问题,②出口dp[0]就是最终答案。
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[] dp = new int[n + 1]; // 只需要记录每一层的最小值即可
for (int i = n - 1; i >= 0; i--) {//从底部向上
for (int j = 0; j <= i; j++) {//row增 列减
//当前i行的j 下一层的j ,j+1
左j代表逻辑上的 上一行 右j代表逻辑上的下一行
..获取.get(i).get(j)
dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);//加上第i行的第j个(遍历的当前个体)
}
}
return dp[0];
}
}
152
class Solution {
public int maxProduct(int[] nums) {
//最终最大值 当前最大值 当前最小值
int max = Integer.MIN_VALUE, imax = 1, imin = 1;
//遇到正号记作最大值 负号记作最小值
for(int i=0; i<nums.length; i++){//遇到负数时 将未与负数相乘的数保存 与
if(nums[i] < 0){ //负号将最大值转为最小值
int tmp = imax;//交换最大最小值
imax = imin;//imax>0
imin = tmp;//imin<0
}
imax = Math.max(imax*nums[i], nums[i]);/// 当num[i]<0 imax= -imin * + = +
imin = Math.min(imin*nums[i], nums[i]); // 当num[i]<0 imin= + * - = -
max = Math.max(max, imax);
}
return max;
}
}
198
// 状态 State
// 灵感,创造力,存储小规模问题的结果
// 方程 Function
// 状态之间的联系,怎么通过小的状态,来算大的状态
// 初始化 Intialization
// 最极限的小状态是什么, 起点
// 答案 Answer
// 最大的那个状态是什么,终点
// class Solution {
// public int rob(int[] nums) {
// int[][] dp = new int[nums.length][2];
// //dp init
// dp[0][0]=0;
// dp[0][1]=nums[0];
// //dp[i]表示 第i个房子get到的钱
// for(int i=1;i<nums.length;i++){
// //0表示mei投 1表示偷
// dp[i][0] = Math.max(dp[i-1][1] ,dp[i-1][0]);//保存两种状态发生后的结果
// dp[i][1] = dp[i-1][0]+nums[i];
// }
// return Math.max(dp[nums.length-1][0],dp[nums.length-1][1]);
// }
// }
class Solution {
public int rob(int[] nums) {
int[][] dp = new int[nums.length][2];
dp[0][0]=0;
dp[0][1]=nums[0];
for(int i=1;i<nums.length;i++){
//不确定状态 =>无法确下一步
dp[i][0]=Math.max(dp[i-1][1],dp[i-1][0]);
//可以确定下一步所以只要在当前把dp[i]最大化即可
dp[i][1]=dp[i-1][0]+nums[i];
}
return Math.max(dp[nums.length-1][0],dp[nums.length-1][1]);
}
}