剑指offer

  • JZ1 二维数组中的查找
  • JZ2 替换空格
  • JZ3 从尾到头打印链表
  • JZ4 重建二叉树
  • JZ5 用两个栈实现队列
  • JZ6 旋转数组的最小数字
  • JZ7 斐波那契数列
  • JZ8 跳台阶
  • JZ9 变态跳台阶
  • JZ10 矩形覆盖
  • JZ11 二进制中1的个数
  • JZ12 数值的整数次方
  • JZ13 调整数组顺序使奇数位于偶数前面
  • JZ14 链表中倒数第k个节点
  • JZ15 反转链表
  • JZ16 合并两个排序的链表
  • JZ17 树的子结构
  • JZ18 二叉树的镜像
  • JZ20 包含min函数的栈
  • JZ21 栈的压入、弹出序列
  • JZ22 从上往下打印二叉树
  • JZ23 二叉搜索树的后序遍历序列
  • JZ24 二叉树中和为某一值的路径
  • JZ25 复杂链表的复制
  • JZ26 二叉搜索树与双向链表
  • JZ27 字符串的排列
  • JZ28 数组中出现次数超过一半的数字
  • JZ29 最小的K个数
  • JZ30 连续子数组的最大和
  • JZ31 整数中1出现的次数(从1到n整数中1出现的次数)
  • JZ32 把数组排成最小的数
  • JZ33 丑数
  • JZ34 第一个只出现一次的字符
  • JZ35 数组中的逆序对
  • JZ36 两个链表的第一个公共结点
  • JZ38 二叉树的深度
  • JZ39 平衡二叉树
  • JZ40 数组中只出现一次的两个数字
  • JZ41 和为S的连续正数序列
  • JZ42 和为S的两个数字
  • JZ43 左旋转字符串
  • JZ44 翻转单词序列
  • JZ47 求1+2+3+...+n
  • JZ50 数组中重复的数字
  • JZ51 构建乘积数组
  • JZ55 链表中环的入口结点
  • JZ56 删除链表中重复的结点
  • JZ57 二叉树的下一个结点
  • JZ59 按之字形顺序打印二叉树
  • JZ60 把二叉树打印成多行
  • JZ62 二叉搜索树的第k个结点

迷茫,是青春最真实的状态;但奋斗,才是青春的主基调;努力是打败焦虑的绝好方法!


JZ1 二维数组中的查找

题目:二维数组中的查找

public class Solution {//双重for循环,遍历查找public boolean Find(int target, int [][] array) {//行遍历,array.length为行数for(int i = 0;i<array.length;i++){//列遍历,array[0].length为列数for(int j=0;j<array[0].length;j++){//存在目标值if(array[i][j] == target)return true;}}//默认不存在return false;}
}

JZ2 替换空格

题目:替换空格

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param s string字符串 * @return string字符串*/public String replaceSpace (String s) {// write code here//定义一个String类型保存替换后的字符串String res= "";//遍历String里面的每一个字母for(int i=0;i<s.length();i++){//为空格,注意s.charAt(i)的类型是字符,单引号if(s.charAt(i) == ' '){//为空格,替换res += "%20";}else{//不为空格,不替换res += s.charAt(i);}}//返回得到的字符串return res;}
}

JZ3 从尾到头打印链表

题目:从尾到头打印链表

暂时有点问题代码

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {//本质为反转链表输出public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {//用于保存数值ArrayList<Integer> array = new ArrayList<>();if(listNode == null || listNode.next == null){return null;}//定义三个指针ListNode beg = null;ListNode mid = listNode;ListNode end = listNode.next;//无限循环反转while(true){//反转mid.next = beg;//end为空,意味着mid指向最后一个节点,全部反转完成if(end == null){//全部反转完成,退出循环break;}//反转之后下一个节点,三个指针同步后移beg = mid;mid = end;end = end.next;}//反转完成,反转后的链表是以mid为头节点的链表//输出的是数值,遍历存储数值while(mid != null){array.add(mid.val);mid = mid.next;}//返回存储的反转链表的值return array;}
}

JZ4 重建二叉树

题目:重建二叉树

  • 先找到根
  • 找左右子树
  • 递归
/*** Definition for binary tree* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode(int x) { val = x; }* }*/
import java.util.*;public class Solution {//dfs,递归public TreeNode reConstructBinaryTree(int [] pre,int [] in) {//空树if(pre.length == 0 || in.length == 0){return null;}//找到根节点,并且创建树的根节点,因为数组中只有值//前序序列第一个为根节点TreeNode root = new TreeNode(pre[0]);//以根结点在中序序列中划分左右子树for(int i=0;i<in.length;i++){//找到根节点//证明左子树i个元素,右子树in.length-i-1个元素if(in[i] == pre[0]){//左子树//先序第一个是根节点,从下标1开始,到包括下标i,因为i个元素root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));//右子树//除了左子树和根,剩下的都是右子树root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));}}return root;}
}

JZ5 用两个栈实现队列

题目:用两个栈实现队列

  • 队列加入元素方式和栈一样,所以可以直接push
  • 队列先进先出,栈后进先出,所以出栈的时候需要两个栈进行双重逆序转换
  • 调用出栈函数时,有两种情况:
    1. 栈2为空,出栈,首先要从栈1放入元素所有元素再出栈
    2. 栈2不为空,出栈,先直接出栈2的元素,到栈2元素出栈完,再执行1步骤
import java.util.Stack;public class Solution {Stack<Integer> stack1 = new Stack<Integer>();Stack<Integer> stack2 = new Stack<Integer>();public void push(int node) {//入栈队列的方式和栈一致//直接push即可stack1.push(node);}public int pop() {//队列出栈采用两个栈//第一个栈入栈,逆序//出栈放入第二个栈,再出栈,即为双重逆序,即正序,即成队列//栈2为空,要出栈,首先要从栈1放入元素所有元素再出栈//因为栈1最底下的元素才是第一个入栈的,所以要全部移到栈2if(stack2.empty()){//栈2空,需要从栈1取数据while(!stack1.empty()){//栈1栈顶入栈栈2//pop方法取栈顶并出栈,peek方法仅取栈顶stack2.push(stack1.pop());}}//栈2不空,直接从栈2出栈即可,所以返回栈2栈顶return stack2.pop();}
}

JZ6 旋转数组的最小数字

题目:旋转数组的最小数字

  • 暴力求解,循环遍历
import java.util.ArrayList;
public class Solution {//暴力求解,遍历数组public int minNumberInRotateArray(int [] array) {//第一个数值开始int min = array[0];//循环遍历for(int i = 1;i<array.length;i++){//比较最小值if(array[i] <= min){min = array[i];}}//返回最小值return min;}
}
  • 二分查找
import java.util.ArrayList;public class Solution {// 二分查找public int minNumberInRotateArray(int[] array) {// 数组大小为0if (array.length == 0)return 0;// 定义数组左边界int left = 0;// 定义数组右边界int right = array.length - 1;// 定义基准int target;// 定义中间值int mid;// 循环查找,条件为左界小于右界while (left < right) {// 去最右边作为基准,随着边界改变而改变target = array[right];// 中值随边界变化而变mid = (left + right) / 2;// 最右边的值大于中间的// 本来递增,旋转后得到的非递减// 所以最小值一定不在后面,但是mid那个可能是最小的if (array[mid] < target) {right = mid;} else if (array[mid] > target) {// 中值大于最后的值// 旋转得到表示中值前面的都大于最后的// 所以中值后的值中才存在最小的left = mid + 1;} else {// 最后一个值和中值相等// 无法判断,去掉最后一个值,继续// 因为中值和最后一个相等,去掉最后无碍right = right - 1;}}// 最后左右边界相等,即为最小值,返回哪一个边界都可// 返回左边界198ms,返回右边界188ms// 不懂为什么,但是返回右边界复杂度小return array[right];}
}

JZ7 斐波那契数列

题目:斐波那契数列

public class Solution {//斐波那契数列,典型递归调用public int Fibonacci(int n) {//第0项和第1项,为0和1if(n==0 || n==1)return n;else//第n项,等于第n-1项和第n-2项的和//递归调用,自己调用自己return Fibonacci(n-1) + Fibonacci(n-2);}
}

JZ8 跳台阶

题目:跳台阶

同斐波那契数列,递归调用即可。

public class Solution {public int jumpFloor(int target) {//边界条件if(target>=0 && target <= 2){return target;}//跳到第n级台阶的跳法等于跳到第n-1级台阶的跳法加上跳到第n-2级台阶的跳法//到了n-1级,就只有一种跳法了//n-2级,也就剩一种跳法了//n-2后,要是一级一级跳,就又是在n-1级里面包含了,可以不考虑return jumpFloor(target-1) + jumpFloor(target-2);}
}

JZ9 变态跳台阶

题目:跳台阶扩展问题

public class Solution {public int jumpFloorII(int target) {//边界条件if(target == 0 || target == 1){return 1;}//f(n) = f(n-1) + ......+f(0)//f(n-1) = f(n-2) + ......+f(0)//相减,所以f(n) = 2*f(n-1)//所以是2的n-1次幂return 2*jumpFloorII(target - 1);}
}

JZ10 矩形覆盖

题目:矩形覆盖

  • 递推(11ms)
public class Solution {//递推法public int rectCover(int target) {//边界条件if(target == 0 || target == 1 || target == 2){return target;}int res = 0;int first = 1;int second = 2;//循环向后求值for(int i=3;i<=target;i++){res = first + second;first = second;second = res;}return res;}
}
  • 递归(389ms)
public class Solution {//递归法public int rectCover(int target) {//边界条件if(target == 0)return 0;if(target == 1)return 1;if(target == 2)return 2;//递归return rectCover(target-2) + rectCover(target-1);}
}
  • 记忆递归(备忘录法)(10ms)
  1. 核心代码模式
import java.util.*;public class Solution {//声明数组,也可以创建数组,但不能初始化在类文件中int[] bp = new int[200];//构造函数中创建并且初始化//利于类的继承Solution(){for(int i=0;i<bp.length;i++){bp[i] = 0;}}//记忆递归,备忘录法public int rectCover(int target) {//边界条件if(target == 1 || target == 2){//保存数据bp[target] = target;}//当前值没有计算过,考虑bp[0]的值,那个0为边界,要去掉if(bp[target] == 0 && target != 0){//递归进行计算//计算完毕进行保存bp[target] = rectCover(target-2) + rectCover(target-1);return bp[target];}else{//已经计算过该值,直接返回return bp[target];}}
}
  1. ACM模式
package com.hnucm;import java.util.*;public class Solution {// 声明数组static int[] bp;//记忆递归,备忘录法public static int rectCover(int target) {//边界条件if(target == 1 || target == 2){//保存数据bp[target] = target;}//当前值没有计算过,考虑bp[0]的值,那个0为边界,要去掉if(bp[target] == 0 && target != 0){//递归进行计算//计算完毕进行保存bp[target] = rectCover(target-2) + rectCover(target-1);return bp[target];}else{//已经计算过该值,直接返回return bp[target];}}public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n;bp = new int[200];for (int i = 0; i < bp.length; i++) {bp[i] = 0;}while (sc.hasNext()) {n = sc.nextInt();System.out.println(rectCover(n));}}
}
  • 动态规划(11ms)

动态规划有点像递归填表,和记忆递归和递推差不多,更像递推填表,参照上面的即可。


JZ11 二进制中1的个数

题目:二进制中1的个数

public class Solution {/*** 求负数的补码的方法。 注意: 负数的补码是在其原码的基础上,符号位不变,其余位取反,然后加1* @param a* @author lhever 2017年4月4日 下午8:42:47* @since v1.0*///负数public static int back(int a){int s = 0;for (int i = 0; i < 32; i++){// 0x80000000 是一个首位为1,其余位数为0的整数int t = (a & 0x80000000 >>> i) >>> (31 - i);if(t == 1){s++;}}return s;}//正数int m = 0;public int normal(int a){if(a%2 == 1)m++;if(a/2 == 0)return m;elsereturn normal(a/2);}public int NumberOf1(int n) {if(n>=0){return normal(n);}else{return back(n);}}
}

JZ12 数值的整数次方

题目:数值的整数次方

  • 递推(36ms)
public class Solution {//递推public double Power(double base, int exponent) {double s = 1.0;if(base == 0){return 0;}else if(exponent == 0){return 1;}else if(exponent > 0){for(int i=0;i<exponent;i++){s *= base;}}else{for(int i=0;i<Math.abs(exponent);i++){s *= base;}s = 1.0/s;}return s;}
}

JZ13 调整数组顺序使奇数位于偶数前面

题目:调整数组顺序使奇数位于偶数前面

  • 先进先出,采用队列
  • 遍历数组,加入队列
  • 遍历数组,改变数组值
  • 队列(时间:400ms,O(n),空间:30MB,O(n))
import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param array int整型一维数组 * @return int整型一维数组*/public int[] reOrderArray (int[] array) {// write code here//先进先出,采用队列Queue<Integer> q1 = new LinkedList();Queue<Integer> q2 = new LinkedList();//遍历每一个数组元素for(int i=0;i<array.length;i++){//偶数if(array[i] % 2 == 0){//加入队列2q2.offer(array[i]);}//奇数else{//加入队列1q1.offer(array[i]);}}//遍历每一个元素,更换值for(int i=0;i<array.length;i++){//队列1没有出完if(!q1.isEmpty()){//存储并出队array[i] = q1.poll();}//队列1结束,队列2开始else{array[i] = q2.poll();}}return array;}
}
  • 直接交换法(时间:O(n),空间:O(n))
import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param array int整型一维数组 * @return int整型一维数组*/public int[] reOrderArray (int[] array) {// write code here//定义位置int i = 0;//创建新数组存储新的int[] res = new int[array.length];//遍历每一个,先加入奇数//array数组中的每一个元素,定义为jfor(int j : array){//奇数if(j%2 != 0){res[i] = j;i++;}}//加入偶数for(int j : array){//偶数if(j%2 == 0){res[i] = j;i++;}}return res;}
}

JZ14 链表中倒数第k个节点

题目:链表中倒数第k个节点

import java.util.*;/** public class ListNode {*   int val;*   ListNode next = null;*   public ListNode(int val) {*     this.val = val;*   }* }*/public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param pHead ListNode类 * @param k int整型 * @return ListNode类*///求链表长度public int length(ListNode pHead){//定义长度int length = 0;//遍历链表while(pHead != null){//长度加一length++;//向后遍历pHead = pHead.next;}//返回长度return length;}//返回倒数第k个节点,就是返回以倒数第k个节点为头节点的剩下的链表public ListNode FindKthToTail (ListNode pHead, int k) {// write code here//获得链表长度int length = length(pHead);//如果没有第k个节点,链表长度小于k,即k不存在if(length < k)return null;//遍历去掉0 -> (length-k-1)的节点for(int i=0;i<length-k;i++){//将指针移到第length-k个节点//后面还剩下k个,所以这就是倒数第k个节点pHead = pHead.next;}//返回这个节点return pHead;}
}

JZ15 反转链表

题目:反转链表
解答详情请看:反转链表


JZ16 合并两个排序的链表

题目:合并两个排序的链表

有问题

/*
public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}*/
public class Solution {//递归、循环,两种解决方式public ListNode Merge(ListNode list1,ListNode list2) {ListNode head = null;ListNode cur = head;while(list1 != null && list2 != null){if(list1.val <= list2.val){cur.next = list1;list1 = list1.next;}else{cur.next = list2;list2 = list2.next;}cur = cur.next;}if(cur.next == list1){cur.next = list1;}elsecur.next = list2;return head.next;}
}

没问题

/*
public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}*/
public class Solution {// 递归方式解决public ListNode Merge(ListNode list1, ListNode list2) {// 定义头节点ListNode head = null;// 有一个链表为空的情况,边界条件,结束条件// 返回另一个链表if (list1 == null)return list2;else if (list2 == null)return list1;//数值小的,加入链表if (list1.val <= list2.val) {// 加入head = list1;// 递归剩下的链表,继续加入小的head.next = Merge(list1.next, list2);} else {head = list2;head.next = Merge(list1, list2.next);}//返回所有的加入之后的return head;}
}

JZ17 树的子结构

题目:树的子结构

  • 首先遍历大树
  • 找到结点值相同的结点
  • 进行判断,以这两个点为根的两个树是否为子结构
/**
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {//遍历整个树,大树public boolean HasSubtree(TreeNode root1,TreeNode root2) {//空树if(root1 == null || root2 == null)return false;//如果当前结点值和子树根节点相同,判断以该结点为根的树是否是子结构if(root1.val == root2.val){//判断是否是子结构if(judge(root1,root2)){return true;}}//当前结点值不等//递归遍历左右孩子,有一个是,那就是,所以采用||return HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2);}//判断是否是子结构public boolean judge(TreeNode node1,TreeNode node2){//子树已经循环完毕,证明全部匹配,是子结构if(node2 == null){return true;}//整个树已经循环完毕,未成功匹配完全,不是子结构if(node1 == null){return false;}//当前结点值相等,那么只需要左右孩子为子结构,那么就为子结构if(node1.val == node2.val){//要两个同时为子结构,当前的才是子结构,所以是&&return judge(node1.left,node2.left) && judge(node1.right,node2.right);}return false;}
}

JZ18 二叉树的镜像

题目:二叉树的镜像

方法一:BFS(广度优先搜索)
方法二:DFS(深度优先搜索)
方法三:中序遍历
方法四:递归

import java.util.*;/** public class TreeNode {*   int val = 0;*   TreeNode left = null;*   TreeNode right = null;*   public TreeNode(int val) {*     this.val = val;*   }* }*/public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param pRoot TreeNode类* @return TreeNode类*/// 镜像变换public TreeNode Mirror(TreeNode pRoot) {// write code here// 递归遍历if (pRoot == null) {return null;}// 中序遍历// 将当前结点的右子树当作根节点,将它的子树镜像变换Mirror(pRoot.right);// 将当前结点的左右子树整个交换TreeNode node = pRoot.right;pRoot.right = pRoot.left;pRoot.left = node;// 左子树变成了右子树,将右子树做根节点,将它的子树再变换Mirror(pRoot.right);// 返回根节点return pRoot;}
}

JZ20 包含min函数的栈

题目:包含min函数的栈

以空间换时间,很常见的方法。

import java.util.Stack;public class Solution {Stack<Integer> stack1 = new Stack<Integer>();Stack<Integer> stack2 = new Stack<Integer>();public void push(int node) {stack1.push(node);if(stack2.isEmpty() || stack2.peek() >= node){stack2.push(node);}else{stack2.push(stack2.peek());}}public void pop() {stack1.pop();stack2.pop();}public int top() {return stack1.peek();}public int min() {return stack2.peek();}
}

JZ21 栈的压入、弹出序列

题目:栈的压入、弹出序列

辅助栈进行判断,后面循环,必加入数据,还需要判断栈不为空,还未理解这一点。

import java.util.*;public class Solution {public boolean IsPopOrder(int [] pushA,int [] popA) {if(pushA.length == 0 || popA.length == 0 || popA.length != pushA.length){return false;}//辅助栈Stack<Integer> stack = new Stack<Integer>();//定义指向出栈数组的指针int j = 0;//入栈for(int i=0;i<pushA.length;i++){//入栈stack.push(pushA[i]);//判断当前栈顶元素是否等于出栈数组元素while(!stack.isEmpty() && stack.peek() == popA[j]){//栈顶元素等于出栈元素//栈顶出栈,指针后移stack.pop();j++;}}//判断是否出栈顺序正确//辅助栈空,正确,不为空,错误return stack.isEmpty();}
}

JZ22 从上往下打印二叉树

题目:从上往下打印二叉树

  • 层次遍历
  • 广度优先搜索
/**
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
import java.util.*;public class Solution {//先序遍历,深度优先递归,广度优先队列//深度优先dfs不对,要广度优先搜索bfspublic ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {//建立链表存储结点值ArrayList<Integer> array = new ArrayList<Integer>();//新建队列进行广度优先搜索,层次遍历Queue<TreeNode> queue = new LinkedList<TreeNode>();//空树,返回空表if(root == null){return array;}//不空,使用队列进行层次遍历//将根结点加入队列,一层一层结点加入queue.offer(root);//队列不为空,即树没有遍历完全,无限循环while(!queue.isEmpty()){//取出队列头,将头节点的值加入链表TreeNode node = queue.poll();array.add(node.val);//当前结点的左孩子,右孩子都加入,即当前结点的下一层结点全部加入if(node.left != null){queue.offer(node.left);}if(node.right != null){queue.offer(node.right);}}return array;}
}

JZ23 二叉搜索树的后序遍历序列

题目:二叉搜索树的后序遍历序列

public class Solution {//划分出左右子树public boolean divideToLeft(int[] s,int left,int right){//左边界大于右边界,证明全部化分,正确,是二叉搜索树//递归结束条件if(left >= right){return true;}//找到后序遍历的根int root = s[right];//定义左右子树边界int i;//从左边边界遍历到根前一个结点for(i = left;i<right;i++){//值大于根的值,证明是根的右子树,跳出循环if(s[i] > root){break;}}//边界前是左子树,后是右子树//遍历右子树,确保后面不存在左子树的值,确保为二叉搜索树for(int j=i;j<right;j++){if(s[j] < root){return false;}}//返回左右子树return divideToLeft(s,left,i-1) && divideToLeft(s,i,right-1);}public boolean VerifySquenceOfBST(int [] sequence) {//空树if(sequence.length == 0){return false;}//返回整个数组的树return divideToLeft(sequence,0,sequence.length-1);}
}

JZ24 二叉树中和为某一值的路径

题目:二叉树中和为某一值的路径

import java.util.ArrayList;
import java.util.Stack;
/**
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {ArrayList<ArrayList<Integer>> res = new ArrayList<>();ArrayList<Integer> path = new ArrayList<>();public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {if (root == null) {return res;}Path(root, target);return res;}public void Path(TreeNode root, int target) {//因为FindPath中和 下面程序中都进行了判null操作,root绝对不可能为 nullpath.add(root.val);//已经到达叶子节点,并且正好加出了targetif (root.val == target && root.left == null && root.right == null) {//将该路径加入res结果集中res.add(new ArrayList(path));}//如果左子树非空,递归左子树if (root.left != null) {Path(root.left, target - root.val);}//如果右子树非空,递归右子树if (root.right != null) {Path(root.right, target - root.val);}//这个路径到达叶子结点,去掉这条路径的最后一个结点//返回父结点,找另一条路径path.remove(path.size() - 1);return;}
}

JZ25 复杂链表的复制

题目:复杂链表的复制

/*
public class RandomListNode {int label;RandomListNode next = null;RandomListNode random = null;RandomListNode(int label) {this.label = label;}
}
*/
/*
*解题思路:
*1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
*2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
*3、拆分链表,将链表拆分为原链表和复制后的链表
*/
public class Solution {public RandomListNode Clone(RandomListNode pHead) {//如果链表为空,无需复制,返回空if(pHead == null) {return null;}//定义从头结点开始指向当前节点的指针RandomListNode currentNode = pHead;//1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;//遍历整个链表,进行复制while(currentNode != null){  //定义一个新的克隆节点,值为当前节点的值RandomListNode cloneNode = new RandomListNode(currentNode.label);//定义一个原链表的下一个节点RandomListNode nextNode = currentNode.next;//将克隆节点插入到当前节点和下一个节点中间currentNode.next = cloneNode;cloneNode.next = nextNode;//跳过加入的克隆节点,将当前指针指向下一个节点,向后遍历currentNode = nextNode;}//重头开始遍历,以pHead为头节点的链表已经改变currentNode = pHead;//2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;while(currentNode != null) {//当前节点的下一个节点即为克隆节点//克隆节点的随机节点为空则为空,否则为当前节点的随机节点的下一个节点currentNode.next.random = currentNode.random==null?null:currentNode.random.next;//当前节点指针要跳过下一个克隆节点,直接跳两个节点,继续指向随机节点currentNode = currentNode.next.next;}//3、拆分链表,将链表拆分为原链表和复制后的链表//重新重头开始currentNode = pHead;//定义拷贝后的头节点从原链表的头节点的下一个节点开始,即第一个克隆节点开始RandomListNode pCloneHead = pHead.next;//遍历整个链表while(currentNode != null) {//取出克隆节点RandomListNode cloneNode = currentNode.next;//将克隆节点取出,将当前节点的指针,指向克隆节点的下一个节点,即下一个原节点currentNode.next = cloneNode.next;//将克隆节点的下一个节点指向克隆节点的下下个节点,即下一个克隆节点,为空,则置为空cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;//因为当前节点的下一个节点指向下一个原节点了,所以直接下一个节点即可currentNode = currentNode.next;}//就分成了两个链表//返回克隆链表的头节点return pCloneHead;}
}

JZ26 二叉搜索树与双向链表

题目:二叉搜索树与双向链表

思路:将整棵树遍历,中序遍历二叉搜索树,直接得到有序的结点。然后将这些结点的关系重新链接,形成双向链表,后只需要返回第一个结点,即是头结点。

/**
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
import java.util.*;public class Solution {//总方法public TreeNode Convert(TreeNode pRootOfTree) {if(pRootOfTree == null){return null;}ArrayList<TreeNode> list = new ArrayList<>();//遍历Order(pRootOfTree,list);//返回头结点return ConVert(list);}//中序遍历,保存节点public void Order(TreeNode root,ArrayList<TreeNode> list){//递归遍历左子树if(root.left != null){Order(root.left,list);}//加入当前节点list.add(root);//遍历右子树if(root.right != null){Order(root.right,list);}}//将链表中排好序的节点,重新链接关系//返回头结点public TreeNode ConVert(ArrayList<TreeNode> list){//如果只有一个数,无需链接if(list.size() == 1){return list.get(0);}else{//第一个只向后链接list.get(0).right = list.get(1);//最后一个只向前链接list.get(list.size()-1).left = list.get(list.size()-2);//中间的结点双向链接for(int i=1;i<list.size()-1;i++){list.get(i).left = list.get(i-1);list.get(i).right = list.get(i+1);}}//返回头结点return list.get(0);}
}

JZ27 字符串的排列

题目:字符串的排列

就是全排列问题。
思路:先固定一个位置的数字,然后对剩下的数字继续全排列,直到只剩下一个位置固定,证明一次全排列结束,输出这个序列。(递归)

  • 不考虑字典序输出重复的数值,只考虑全排列
import java.util.ArrayList;
public class Solution {//保存每一个排列序列ArrayList<String> list = new ArrayList<>();//全排列public void Permutation(char c[],int start,int end){//只有最后一个没固定,只有一种情况//一次全排列结束if(start == end){list.add(new String(c));}else{for(int i = start;i<=end;i++){//确定首位的数swap(c,start,i);//后面的所有数,全排列Permutation(c,start+1,end);//避免重复排列swap(c,start,i);}}}//交换public void swap(char c[],int i,int j){char temp;if(i != j){temp = c[i];c[i] = c[j];c[j] = temp;}}public ArrayList<String> Permutation(String str) {char c[] = str.toCharArray();Permutation(c,0,str.length()-1);return list;}
}
  • 不考虑字典序,考虑全排列重复值
import java.util.ArrayList;
public class Solution {//保存每一个排列序列ArrayList<String> list = new ArrayList<>();//全排列public void Permutation(char c[],int start,int end){//只有最后一个没固定,只有一种情况//一次全排列结束if(start == end){list.add(new String(c));}else{for(int i = start;i<=end;i++){//后面存在一个相同值//进行下一个循环if(!isSwap(c,i,end)){continue;}//无重复值//执行下述代码swap(c,start,i);//后面的所有数,全排列Permutation(c,start+1,end);//避免重复排列swap(c,start,i);}}}//交换public void swap(char c[],int i,int j){char temp;if(i != j){temp = c[i];c[i] = c[j];c[j] = temp;}}//去重复public boolean isSwap(char c[],int i,int end){for(int j=i+1;j<=end;j++){//i后面的值存在和i重复的//跳过此次循环if(c[j] == c[i])return false;}return true;}public ArrayList<String> Permutation(String str) {char c[] = str.toCharArray();Permutation(c,0,str.length()-1);return list;}
}
  • 全部考虑
import java.util.*;
public class Solution {//保存每一个排列序列ArrayList<String> list = new ArrayList<>();//全排列public void Permutation(char c[],int start,int end){//只有最后一个没固定,只有一种情况//一次全排列结束if(start == end){list.add(new String(c));}else{for(int i = start;i<=end;i++){//跳出此次循环if(!isSwap(c,i,end)){continue;}swap(c,start,i);//后面的所有数,全排列Permutation(c,start+1,end);//避免重复排列swap(c,start,i);}}}//交换public void swap(char c[],int i,int j){char temp;if(i != j){temp = c[i];c[i] = c[j];c[j] = temp;}}//去重复public boolean isSwap(char c[],int i,int end){for(int j=i+1;j<=end;j++){//i后面的值存在和i重复的//跳过此次循环if(c[j] == c[i])return false;}return true;}public ArrayList<String> Permutation(String str) {if(str == ""){return null;}char c[] = str.toCharArray();Permutation(c,0,str.length()-1);//内置函数,升序排列,字典序Collections.sort(list);return list;}
}

字典序法

思路:先给定一个排列,然后找下一个排列顺序。下一个排列顺序要刚好比这个排列大,但是它们中间不能有排列,直到找到排列的左邻没有小于右邻的值为止,即是递减排列了,证明全排结束,全部找到了。

步骤:

  1. 从右至左,找到左邻小于右邻的第一个数,记录位置i和值c[i]
  2. 从右至左,找到第一个比c[i]大的数,记录位置j和值c[j]
  3. 交换这两个数字
  4. 然后将i后面的值进行从小到大升序排列。
  5. 因为交换的是第一个比c[i]大的值和c[i],所以交换后,i后面的值还是从大到小排序的。所以逆序就可。

JZ28 数组中出现次数超过一半的数字

题目:数组中出现次数超过一半的数字

  • 哈希法

思路:新建一个哈希数组,用于在位置为值的地方存放这个值出现的次数,遍历哈希数组,找出大小比原数组的一半大的数,这个数存放的位置,就是众数的值。

public class Solution {public int MoreThanHalfNum_Solution(int [] array) {//只有一个元素,直接返回if(array.length == 1){return array[0];}//采用hash数组,存放出现次数int hash[] = new int[10001];//初始化数组for(int i=0;i<hash.length;i++){hash[i] = 0;}//遍历原数组,将值的个数存在在a中//存放的位置为值for(int i=0;i<array.length;i++){hash[array[i]]++;}//找出a中值大于原数组一半的数,证明是众数//返回a中的位置,即为众数的值for(int i=0;i<hash.length;i++){if(hash[i] > (array.length/2)){return i;}}//都不符合上述情况,返回0return 0;}
}
  • 候选法

思路:将两个数对比,如果这两个数不相同,则一起去掉。所以有两种情况:一种是去掉的是一个众数和一个非众数;另一种是两个非众数。无论是哪一种,如果众数存在,最后剩下的,肯定是众数。


JZ29 最小的K个数

题目:最小的K个数

主要思路:先对数组进行排序,然后返回前k个数。

  • 内置函数排序(不推荐)

因为肯定是要考我们排序算法,直接使用内置函数了,就没有意义了。

import java.util.*;public class Solution {public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {ArrayList<Integer> list = new ArrayList<>();ArrayList<Integer> array = new ArrayList<>();for(int i=0;i<input.length;i++){array.add(input[i]);}Collections.sort(array);for(int i=0;i<k;i++){list.add(array.get(i));}return list;}
}
  • 快速排序(推荐)

思路:二分法进行分区,递归快排。

单向扫描快排法

思路:从左到右扫描,小于基准,不动,看下一个;大于基准,将当前值和最后一个交换,最后一个值固定,右指针前移;继续对比当前值和基准,循环进行。

import java.util.*;public class Solution {public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {ArrayList<Integer> list = new ArrayList<>();//对数组排序quickSort(input,0,input.length-1);//取前k个加入listfor(int i=0;i<k;i++){list.add(input[i]);}return list;}// 单向扫描分区public static int partition(int s[], int start, int end) {//确定基准int base = s[start];//左指针int i = start + 1;//右指针int j = end;//从左到右扫描while (i <= j) {//这个数小于基准,不变,看下一个if(s[i]<=base) {i++;}else {//大于基准,放到后面swap(s,i,j);//不在考虑放在后面的数字,固定j--;}}//无论如何,j最后指向的都是小于base的最后一个数swap(s, start, j);return j;}//快排public static void quickSort(int[] s,int start,int end) {//相等代表只有一个,排序无意义if(start < end) {int mid = partition(s,start,end);//递归排序分区好的左右两边quickSort(s, start, mid-1);quickSort(s, mid+1, end);}}//交换public static void swap(int[] s,int i,int j) {int temp = 0;temp = s[i];s[i] = s[j];s[j] = temp;}
}

双向扫描快排法

思路:定义第一个数为基准;从左到右找第一个比基准大的数字,从右到左找第一个比基准小的数字;如果左指针小于右指针,交换他们,循环往复;直到左指针大于等于右指针结束;交换基准和右指针位置,分区完成。然后递归对每一个子块快排即可。

import java.util.*;public class Solution {public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {ArrayList<Integer> list = new ArrayList<>();//对数组排序quickSort(input,0,input.length-1);//取前k个加入listfor(int i=0;i<k;i++){list.add(input[i]);}return list;}//双向扫描分区public static int partition(int[] s,int start,int end) {//确定基准int base = s[start];//i从前开始int i = start + 1;//j从后开始int j = end;//只要i在j前while(i<=j) {//找到第一个比基准大的iwhile(i<=j && s[i]<=base) {i++;}//找到第一个比基准小的jwhile(i<=j && s[j]>=base) {j--;}if(i<j) {swap(s,i,j);}}swap(s,start,j);return j;}//快排public static void quickSort(int[] s,int start,int end) {//相等代表只有一个,排序无意义if(start < end) {int mid = partition(s,start,end);//递归排序分区好的左右两边quickSort(s, start, mid-1);quickSort(s, mid+1, end);}}//交换public static void swap(int[] s,int i,int j) {int temp = 0;temp = s[i];s[i] = s[j];s[j] = temp;}
}

还有三分快排法,感兴趣可以继续探索。

另外两种方法博客链接:快速排序的三种分区方法(整理)


JZ30 连续子数组的最大和

题目:连续子数组的最大和

  • 动态规划法(填表法)

思路:定义一个表dp,根据原来数组的位置去填表,dp[i]表示以第i个位置为结尾的子数组的最大和。所以dp[i]等于array[i]和array[i]+dp[i-1]之间的最大值。每一个dp填入后,最大和就是这张表中的最大值。

public class Solution {//动态规划法,填表法public int FindGreatestSumOfSubArray(int[] array) {//dp[i]表示以i结尾的子数组的最大和int dp[] = new int[array.length];//第一个值只有一个值,所以就是第一个值//可以保证无论数是有正负,还是全负全正,都通用dp[0] = array[0];//初始化最大值为第一个int max = dp[0];//一重循环,时间复杂度O(n)for(int i=1;i<array.length;i++){//下一个值为当前的值加上上一个dp和当前值之间的最大值//如果上一个dp是负数,那么当前值最大,即子数组开头换了位置一样dp[i] = Math.max(array[i],array[i]+dp[i-1]);//最大值,就是当前最大值和dp之比,动态变化max = Math.max(max,dp[i]);}//返回子数组最大和return max;}
}
  • 备忘录法(查表法)

思路:基本使用动态规划法的题目,备忘录法同样可以解决。思路几乎一样,就是一个查表,一个填表。

  • 变量判断法

思路:设置一个变量tmp = 0。如果tmp+array[i] < 0, 说明以i结尾的不作贡献,重新赋值tmp = 0,否则更新tmp = tmp + array[i]。最后判断tmp是否等于0, 如果等于0, 说明数组都是负数,选取一个最大值为答案


JZ31 整数中1出现的次数(从1到n整数中1出现的次数)

题目:整数中1出现的次数(从1到n整数中1出现的次数)

  • 暴力解法,空间复杂度较高

思路:计算出一个数中1的个数,然后分别求每一个数中1的个数,求和即可。

public class Solution {//返回1-n这n个数中1的个数public int NumberOf1Between1AndN_Solution(int n) {int s = 0;for(int i=1;i<=n;i++){s += NumofOne(i);}return s;}//返回这个数中1的个数public int NumofOne(int n){int num = 0;String s = n + "";char c[] = s.toCharArray();for(int i=0;i<s.length();i++){if(c[i] == '1'){num++;}}return num;}
}

JZ32 把数组排成最小的数

题目:把数组排成最小的数

  • 全排列

思路:把数组里的整数元素,看成一个字符串整体。进行字符串全排列,然后进行升序排序,第一个即是最小值。

import java.util.*;public class Solution {//保存每一个排列序列ArrayList<String> list = new ArrayList<>();//得到组成的最小值public String PrintMinNumber(int [] numbers) {String str = "";//输入为空,输出为空if(numbers.length == 0){return str;}//将整数数组变为字符串数组String s[] = new String[numbers.length];for(int i=0;i<numbers.length;i++){s[i] = numbers[i] + "";}//进行全排列Permutation(s,0,s.length-1);//字典序Collections.sort(list);//最小值str = list.get(0);//返回最小数return str;}//全排列public void Permutation(String s[],int start,int end){//只有最后一个没固定,只有一种情况//一次全排列结束if(start == end){//把结果加入list中保存String str = "";for(int i=0;i<s.length;i++){str += s[i];}list.add(str);}else{for(int i = start;i<=end;i++){//有重复,跳出此次循环if(isEquals(s,i,end)){continue;}//固定开头的值swap(s,start,i);//后面的所有数,全排列Permutation(s,start+1,end);//避免重复排列swap(s,start,i);}}}//是否有重复//去重复public boolean isEquals(String s[],int i,int end){for(int j=i+1;j<=end;j++){if(s[i].equals(s[j])){return true;}}return false;}//交换public void swap(String s[],int i,int j){String temp;temp = s[i];s[i] = s[j];s[j] = temp;}
}

JZ33 丑数

题目:丑数

  • 穷举法(枚举法)

思路:第一个丑数是1,下一个丑数肯定是前一个丑数乘以2/3/5的最小值。即第n个丑数肯定可以由第i个丑数乘以2/3/5得到。所以使用这个规律进行枚举。因为得到最小的之后,还有两个剩下的值需要继续比,所以需要三个指针,指向不同的第i个丑数。

import java.util.*;public class Solution {//控制一个数组列举丑数,然后返回这个丑数public int GetUglyNumber_Solution(int index) {//第0个丑数,不合理,返回0if(index == 0){return 0;}//创建数组保存丑数//为了不浪费空间,刚好到所求的第n个丑数int res[] = new int[index];//第一个是1res[0] = 1;//定义三个表示丑数*2*3*5int p2 = 0;int p3 = 0;int p5 = 0;//循环放入丑数for(int i=1;i<index;i++){//下一个丑数是三个里面最小的res[i] = Math.min(res[p2]*2,Math.min(res[p3]*3,res[p5]*5));//三个指针向后移动if(res[i] == res[p2]*2){p2++;}if(res[i] == res[p3]*3){p3++;}if(res[i] == res[p5]*5){p5++;}}//返回最后一个丑数,即是所求return res[index-1];}
}

JZ34 第一个只出现一次的字符

题目:第一个只出现一次的字符

  • 数组法

思路:在这个值的位置,放置它出现的次数。但是有点问题,使用map得到值增加有点问题。所以改成了ASC码和数组保存其出现的次数。

import java.util.*;public class Solution {public int FirstNotRepeatingChar(String str) {//asc码的长度多1int map[] = new int[128];//遍历得到每个字符出现次数for(int i=0;i<str.length();i++){map[str.charAt(i)]++;}//遍历,找到第一个出现次数只有一次的字符for(int i=0;i<str.length();i++){if(map[str.charAt(i)]==1){//返回位置return i;}}//没有只出现一次的字符return -1;}
}
  • 哈希法

思路:由于在这个位置保存其出现的次数有问题,而题目只需要我们判断第一个出现一次的字符的位置,所以可以把出现一次的看做无重复,把多次出现的,记作重复,采用boolean进行判断。

import java.util.*;public class Solution {public int FirstNotRepeatingChar(String str) {//创建哈希表HashMap<Character,Boolean> map = new HashMap<>();//遍历判断其是否重复出现for(int i=0;i<str.length();i++){//重复出现if(map.get(str.charAt(i)) != null){//设置true,表示重复出现map.put(str.charAt(i),true);}else{//第一次出现map.put(str.charAt(i),false);}}//重新遍历原字符串//找出第一个不重复出现的字符的位置for(int i=0;i<str.length();i++){//map中key为这个字符的,不重复if(map.get(str.charAt(i)) == false){//返回其在原数组中的位置return i;}}//都不符合,返回-1return -1;}
}

JZ35 数组中的逆序对

题目:数组中的逆序对

  • 暴力解法

思路:以当前数字固定,找出它后面比它小的数字的个数,这些数字就可以和它组成这么多逆序对。然后遍历数组,使固定的数字不同。得到的所有数之和即为逆序对总数。因为有双重循环,所以对于10^5来说,一定超时了。

public class Solution {//所有的逆序对public int InversePairs(int [] array) {int res = 0;for(int i=0;i<array.length-1;i++){res += NumsOfSmall(array,i);}return res;}//在后面中小于这个数的数字个数public int NumsOfSmall(int[] array,int i){int s = 0;for(int j=i+1;j<array.length;j++){if(array[i] > array[j]){s++;s %= 1000000007;}}return s;}
}
  • 归并排序法

思路:先分解数组,直到不能再分,然后进行对比两个数组,排序,将小的先放入,大的后放入,大的还要计算逆序数。只要前一个数组的当前值大于后一个数组的值,那么在前一个数组中,位于这个数后面的所有数,都可以和后一个数组的这个数组成逆序对。最后返回逆序对总数。

public class Solution {//记录逆序对总数private int res;//归并排序private void MergeSort(int[] array, int start, int end){if(start>=end)return;//分界点int mid = (start+end)/2;//分成两个数组MergeSort(array, start, mid);MergeSort(array, mid+1, end);//进行合并,排序,并记录逆序数MergeOne(array, start, mid, end);}//合并,排序,记录逆序数private void MergeOne(int[] array, int start, int mid, int end){//保存排序后的数组int[] temp = new int[end-start+1];//两个指针,指向两个数组的第一个数值int k=0,i=start,j=mid+1;while(i<=mid && j<= end){//如果前面的元素小于后面的不能构成逆序对if(array[i] <= array[j])temp[k++] = array[i++];else{//如果前面的元素大于后面的//那么在前面元素之后的元素都能和后面的元素构成逆序对temp[k++] = array[j++];//计算逆序对数量res = (res + (mid-i+1))%1000000007;}}//合并第一个数组while(i<= mid)temp[k++] = array[i++];//合并第二个数组while(j<=end)temp[k++] = array[j++];//改变数组位置,进行排序for(int l=0; l<temp.length; l++){array[start+l] = temp[l];}}//调用方法,计算数量public int InversePairs(int [] array) {MergeSort(array, 0, array.length-1);return res;}
}

JZ36 两个链表的第一个公共结点

题目:两个链表的第一个公共结点

/*
public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}*/
public class Solution {//双重遍历,寻找第一个相同的节点public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {//保存第二个链表ListNode temp = pHead2;//两个链表都不为空if (pHead2 != null && pHead1 != null) {//pHead1不为空,进入循环while (pHead1 != null) {//pHead2不为空,进入循环,双重循环while (pHead2 != null) {//两个相等,返回任意一个都行if (pHead2 == pHead1) {return pHead2;}//不等,第一个链表不变,第二个向右继续pHead2 = pHead2.next;}//一轮过后,第二个链表指针指向了末尾//需要重新指向开头pHead2 = temp;//第一个链表向右,开始下一轮pHead1 = pHead1.next;}}//没有,返回空return null;}
}

JZ38 二叉树的深度

题目:二叉树的深度

方法一:分治法,先求左子树,后求右子树(递归DFS)

分治法简介:求一个规模为n的问题,先求左边规模大约为n/2的问题,再求右边规模大约为n/2的问题,然后合并左边,右边的解,从而求得最终解。具体可参考归并排序。
步骤:

  • 求 pro(left, rigth) -> int
  • 先求pro(left, (left+right)/2) -> left
  • 再求pro((left+right)/2 + 1, right) -> right
  • merge(left, right) -> result

求二叉树的最大深度,我们不必管函数具体是怎么实现的。
所以最终结果为 max( 头结点左子树的最大深度, 头结点右子树的最大深度)+1
步骤:

  • 求TreeDepth(TreeNode pRoot)->int
  • 先求 TreeDepth(pRoot.left) ->left
  • 再求TreeDepth(pRoot.right) ->right
  • return Math.max(left, right) + 1
/**
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {//求树的深度,根结点到叶结点的最长路径public int TreeDepth(TreeNode root) {//树不存在,返回0if(root == null){return 0;}//找出左子树的深度int left = TreeDepth(root.left);//找出右子树的深度int right = TreeDepth(root.right);//返回左右子树大的深度加一,就是最长的路径,即整个树的深度return Math.max(left,right) + 1;}
}

方法二:层次遍历(队列BFS)

非递归:求最大深度,可用队列。因为要满足先进先出的特性。

  • 初始化:一个队列queue<TreeNode*> q, 将root节点入队列q
  • 如果队列不空,做如下操作:
  • 弹出队列头,保存为node,将node的左右非空孩子加入队列
  • 做2,3步骤,知道队列为空
/**
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
import java.util.*;public class Solution {//层次遍历,广度优先搜索BFSpublic int TreeDepth(TreeNode root) {//空树,返回0if(root == null){return 0;}Queue<TreeNode> queue = new LinkedList();TreeNode nlast = null;TreeNode last = root;int depth = 0;queue.offer(root);while(!queue.isEmpty()){TreeNode cur = queue.poll(); if(cur.left != null){queue.offer(cur.left);nlast = cur.left;}if(cur.right != null){queue.offer(cur.right);nlast = cur.right;}if(cur == last){depth++;last = nlast;}}return depth;}
}

JZ39 平衡二叉树

题目:平衡二叉树

在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右
两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

方法一:双重递归(自顶向下遍历)

public class Solution {//求出树的高度(深度),递归public int TreeDepth(TreeNode root){if(root == null){return 0;}//递归int left = TreeDepth(root.left);int right = TreeDepth(root.right);//返回左子树和右子树较大的深度加一即是树的深度return Math.max(left,right) + 1;}//判断是否是平衡二叉树public boolean IsBalanced_Solution(TreeNode root) {//空树,是平衡二叉树if(root == null){return true;}//不空,默认左右都存在//递归if(Math.abs(TreeDepth(root.left)-TreeDepth(root.right)) <= 1 &&IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right)){//左右子树的高度差绝对值不超过1,左右子树都是平衡二叉树,那么树就是平衡二叉树return true;}//返回除了上述是平衡二叉树的情况之外的其余所有的不是平衡二叉树的情况return false;}
}

方法二:回溯加剪枝(自底向上遍历)

剪枝就是将不满足的情况减去

public class Solution {//以高度进行剪枝public int TreeDepth(TreeNode root){if(root == null)//空树,高度为0return 0;//左子树高度int left = TreeDepth(root.left);if(left == -1)//如果发现左子树不平衡,剪枝return -1;//右子树高度int right = TreeDepth(root.right);if(right == -1)//如果发现右子树不平衡,剪枝return -1;//如果左右子树高度差超过1,剪枝if(Math.abs(left - right) > 1)return -1;else{//满足条件,返回树高度return Math.max(left,right) + 1;}}//判断是否是平衡二叉树public boolean IsBalanced_Solution(TreeNode root) {//满足条件,返回的就是树高度,就不为-1,返回true,是平衡二叉树//不满足条件,高度返回的是-1,所以返回false,不是平衡二叉树return TreeDepth(root) != -1;}
}

JZ40 数组中只出现一次的两个数字

题目:数组中只出现一次的两个数字

  • 哈希法

思路:采用键值对存储,在这个值的位置,存储它出现的次数。然后遍历hashmap,将出现次数等于1的键保存到数组中,返回数组即可。

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param array int整型一维数组 * @return int整型一维数组*/public int[] FindNumsAppearOnce(int[] array) {//键值对存储HashMap<Integer,Integer> map = new HashMap<>();//遍历数组,记录出现次数for(int i=0;i<array.length;i++){//表示存在值,已经出现一次了if(map.get(array[i]) != null){map.put(array[i],2);}else{map.put(array[i],1);}}//只有两个数字出现一次int[] res = new int[2];//从数组0开始写入int count = 0;//遍历hashmap找到这两个数字//keySet()方法返回key的集合for(int i:map.keySet()){//出现一次if(map.get(i)==1){//放入值,值就等于在hashmap中的位置res[count] = i;//到下一个位置count++;}}//返回数组return res;}
}

补充:map.keySet()方法是为了得到key的集合。

  • ArrayList方法

思路:遍历数组,将所有第一次到来的数据,直接加入list;第二次到来,删除list中和这个数据值相等的数据。最后返回list,里面就只有出现一次的数据。由题意可知,长度必是2个。

注意:用这个方法,有一个坑。就是移除数据的时候,因为数据值就是int类型的,所以它会移除索引为这个值的数据,而不是移除这个值。分析源码得到,将其强转为Object类型,即可删除这个值,而不是删除索引为这个值的数据。

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param array int整型一维数组 * @return int整型一维数组*/public int[] FindNumsAppearOnce(int[] array) {//创建ArrayList保存数据ArrayList<Integer> list = new ArrayList<>();//遍历原数组for (int i = 0; i < array.length; i++) {//里面存在这个数据if (list.contains(array[i])) {//删除这个数据list.remove((Object)array[i]);} else {//第一次出现,加入list.add(array[i]);}}//升序排序Collections.sort(list);//用数组保存数据,由题意知长度为2int[] s = new int[2];//将list中的数据转移到数组中s[0] = list.get(0);s[1] = list.get(1);//返回数组return s;}
}

JZ41 和为S的连续正数序列

题目:和为S的连续正数序列

  • 暴力解法(数学法)O(n^3)

思路:确定左右边界,然后加起来区间和,相等则加入这个序列,大于则停止,小于不做判断,因为开始就是必定小于的。

import java.util.ArrayList;
public class Solution {//保存所有序列ArrayList<ArrayList<Integer>> res = new ArrayList<>();//数学暴力解法public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {//定义左边界for(int i=1;i<=sum/2;i++){//定义右边界for(int j=i+1;j<=sum;j++){//定义区间和int temp = 0;//求出区间和for(int k=i;k<=j;k++){temp += k;}//相等,加入这一个序列if(temp == sum){//每次里面的内容不同,所以使用的时候再创建//保存这个序列ArrayList<Integer> list = new ArrayList<>();//形成这个序列for(int k=i;k<=j;k++){list.add(k);}//序列加入res.add(list);//一定要大于,小于就停止的话,会出现问题//因为小于是一个几乎必然的条件}else if(temp > sum){break;}}}//返回所有序列return res;}
}
  • 前缀和法 O(n^2)

思路:求前j个数的和。又有些不一样,左边界可动,不固定。左边界向后遍历,右边界不知道在哪,相加,若是相等,当前的j即是右边界,从左边界i到右边界j的序列即连续子序列。

import java.util.ArrayList;
public class Solution {//保存所有序列ArrayList<ArrayList<Integer>> res = new ArrayList<>();//前缀和法public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {//定义区间和int temp = 0;//定义左边界for(int i=1;i<=sum/2;i++){//右边界未定//从左边界开始,往后相加//直到相等,就找到了右边界for(int j=i;j<sum;j++){//求出区间和temp += j;//相等,找到了右边界,即当前的j//加入这一个序列if(temp == sum){//每次里面的内容不同,所以使用的时候再创建//保存这个序列ArrayList<Integer> list = new ArrayList<>();//形成这个序列for(int k=i;k<=j;k++){list.add(k);}//序列加入res.add(list);//一定要大于,小于就停止的话,会出现问题//因为小于是一个几乎必然的条件}else if(temp > sum){//剪枝temp = 0;break;}}}//返回所有序列return res;}
}
  • 滑动窗口法 O(n)

思路:定义一个窗口,左边界和右边界,注意窗口是左闭右开的,并且从头开始,只能向右移动,不可以向左移动。值小了,就要扩大窗口,右边界扩大;值大了,就要缩小窗口,左边界后移。直到左边界到中值停止或者找到值相等的右边界和左边界。加入序列,返回。

import java.util.ArrayList;
public class Solution {//保存所有序列ArrayList<ArrayList<Integer>> res = new ArrayList<>();//滑动窗口法public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {//定义窗口左边界int left = 1;//定义窗口右边界int right = 1;//定义窗口内值的和int temp = 0;//左边界大于一半时停止while(left <= sum/2){//和小于所需的值if(temp < sum){//加入右边界temp += right;//扩大窗口right++;//和大于所需的值}else if(temp > sum){//左边界后移,去掉左边界值temp -= left;//缩小窗口left++;//和等于所需值//即找到序列加入}else{//保存这个序列ArrayList<Integer> list = new ArrayList<>();//形成这个序列//注意滑动窗口的左闭右开for(int k=left;k<right;k++){list.add(k);}//序列加入res.add(list);temp -= left;left++;}}//返回所有序列return res;}
}

JZ42 和为S的两个数字

题目:和为S的两个数字

  • 双指针法

思路:定义两个指针,左指针指向开头,右指针指向末尾。因为递增,所以和小于所求值时,数字变大,左指针右移;大于的时候,数字变小,右指针左移;等于所求值时,要比较乘积,乘积大于前两个数字的乘积,数字不变,指针移动;乘积小于前两个数字的乘积,更新两个数字和最小乘积。

import java.util.ArrayList;
public class Solution {//保存这两个数字ArrayList<Integer> res = new ArrayList<>();//双指针法public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {//数组不存在,直接返回if(array.length == 0){return res;}//定义一个标志为最大值,表示两个数的乘积int flag = java.lang.Integer.MAX_VALUE;//定义左右指针int i=0,j=array.length-1;//结束条件为左指针大于右指针while(i<j){//这两个数和为所求值if(array[i] + array[j] == sum){//乘积小于flag,则更新这两个数字if(array[i]*array[j] < flag){//最小乘积更新flag = array[i] * array[j];//更新两个数字res.clear();res.add(array[i]);res.add(array[j]);}//左指针右移i++;//右指针左移j--;//小于的时候,数字变大}else if(array[i] + array[j] < sum){//递增,所以左指针右移i++;}else{//大于,右指针左移j--;}}//返回乘积最小,和为所求值的两个数return res;}
}

JZ43 左旋转字符串

题目:左旋转字符串

  • 暴力解法

思路:将后面的字符先放入数组,前面旋转到后面的,后面放入数组,将数组变为字符串返回即可。

public class Solution {//暴力法public String LeftRotateString(String str,int n) {//边界if(str.length() == 0 || n == 0 || n == str.length()){return str;}//存放旋转后的字符char c[] = new char[str.length()];//存放位置int j = 0;//旋转n位,所以从第n个开始,变成第一位for(int i=n;i<str.length();i++){c[j] = str.charAt(i);j++;}//然后把前面的加入后面,旋转成功for(int i=0;i<n;i++){c[j] = str.charAt(i);j++;}//组合成字符串并返回String s = new String(c);return s;}
}

JZ44 翻转单词序列

题目:翻转单词序列

  • 双指针法

思路:定义两个指针,一个从开头开始,一个从末尾开始,向中间移动。当左指针大于等于右指针时结束,不然就翻转位于这两个位置的字符串。最后拼接字符串,需要注意前面的都要加空格,最后一个不需要加空格,处理一下就好了。

public class Solution {//双指针法public String ReverseSentence(String str) {//定义左指针int i = 0;//按空格划分字符串String[] str1 = str.split(" ");//定义右指针int j = str1.length-1;//左指针大于等于右指针为结束条件while(i<j){//翻转swap(str1,i,j);//改变指针i++;j--;}//用来存储翻转后的字符串String s = new String("");//注意前几个拼接需要空格for(int k=0;k<str1.length-1;k++){s = s + str1[k] + " ";}//最后一个拼接不需要空格s = s + str1[str1.length-1];//返回翻转的字符串return s;}//交换public void swap(String s[],int i,int j){String temp;temp = s[i];s[i] = s[j];s[j] = temp;}
}

JZ47 求1+2+3+…+n

题目:求1+2+3+…+n

public class Solution {public int Sum_Solution(int n) {//递归求和if(n==1){//边界结束条件return 1;}//返回递归和,本身加上前n-1项的和return n + Sum_Solution(n-1);}
}

JZ50 数组中重复的数字

题目:数组中重复的数字

  • 哈希法

思路:在key为这个值的位置,放置其是否是重复数字的flag,以此来判断其是否是重复数字。

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param numbers int整型一维数组 * @return int整型*///哈希法,在每个位置放入其是否是重复的数字public int duplicate (int[] numbers) {// write code here//创建哈希表HashMap<Integer,Boolean> map = new HashMap<>();//遍历判断其是否重复for(int i=0;i<numbers.length;i++){//第二次出现if(map.get(numbers[i]) != null){//设置true,表示是重复的map.put(numbers[i],true);}else{//第一次出现,不是重复的map.put(numbers[i],false);}}//遍历map中的每一个keyfor(int i:map.keySet()){//重复的,返回key即可if(map.get(i) == true){return i;}}//都不符合,返回-1return -1;}
}

JZ51 构建乘积数组

题目:构建乘积数组

  • 暴力解法
import java.util.ArrayList;
public class Solution {public int[] multiply(int[] A) {int res[] = new int[A.length];for(int i=0;i<A.length;i++){res[i] = 1;}for(int i=0;i<A.length;i++){for(int j=0;j<A.length;j++){if(j==i){continue;}res[i] *= A[j]; }}return res;}
}

JZ55 链表中环的入口结点

题目:链表中环的入口结点

方法一:哈希存储法

  1. 遍历单链表的每个结点
  2. 如果当前结点地址没有出现在set中,则存入set中
  3. 否则,出现在set中,则当前结点就是环的入口结点
  4. 整个单链表遍历完,若没出现在set中,则不存在环
/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
import java.util.*;//哈希存储法
public class Solution {//运用HashSet存储节点,HashSet实现了Set接口Set<ListNode> s = new HashSet<>();public ListNode EntryNodeOfLoop(ListNode pHead) {//遍历整个单链表while(pHead != null){//已经在HashSet中,证明形成了环,pHead就是入口节点if(s.contains(pHead)){return pHead;}else{//不在HashSet中,节点加入,遍历下一个s.add(pHead);pHead = pHead.next;}}//无环,返回空return null;}
}

方法二:快慢指针法(双指针法)

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
import java.util.*;//快慢指针法
public class Solution {public ListNode EntryNodeOfLoop(ListNode pHead) {//定义快指针ListNode fast = pHead;//定义慢指针ListNode slow = pHead;//存在环,必相遇,快的追上慢的while(fast != null && fast.next != null){//快指针走两步fast = fast.next.next;//慢指针走一步slow = slow.next;//第一次相遇,停止if(fast == slow)break;}//没有环,返回空if(fast == null || fast.next == null)return null;//快指针指向开头fast = pHead;//未相遇while(fast != slow){//快慢指针都走一步fast = fast.next;slow = slow.next;}//相遇于入口结点,返回入口结点return fast;}
}

JZ56 删除链表中重复的结点

题目:删除链表中重复的结点

方法一:遍历链表,Set存储,暴力求解法

遍历链表,存储结点值,然后再遍历一次单链表,删除重复值即可。

找重复值的具体步骤:

  1. 初始化:set< int > st
  2. 遍历单链表相邻两个元素,如果相等,就加入到set当中
  3. 直到单链表遍历完< /int>

删除重复值的具体步骤:

  1. 初始化:pre指针指向重复值的前一个节点,cur指向当前遍历的节点值
  2. 遍历单链表当前元素,然后再set中检查,如果是重复值,就删除,pre->next = cur->next
  3. 否则,不是重复值,pre = pre->next, cur = cur->next
  4. 知道单链表遍历完
/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
import java.util.*;public class Solution {//Set存储,暴力求解public ListNode deleteDuplication(ListNode pHead) {//链表为空if(pHead == null || pHead.next == null){return pHead;}//定义Set存储相同结点值HashSet<Integer> st = new HashSet<>();//定义指向当前结点的指针和指向前一个结点的指针ListNode pre = pHead;ListNode cur = pHead.next;//遍历链表,保存重复值while(cur != null){if(pre.val == cur.val){//值相同,保存st.add(pre.val);}//向右遍历pre = pre.next;cur = cur.next;}ListNode head = new ListNode(0);pre = head;cur = pHead;while(cur != null){if(!st.contains(cur.val)){pre.next = cur;pre = pre.next;}cur = cur.next;}return head.next;}
}

改了好几遍,才AC九个例子,最后那个我总觉得对的,没错逻辑,就是出不来,有会的人请评论区留言,感谢。

方法二:边遍历边删除,直接删除法

根据方法一,可以优化,在遍历单链表的时候,检查当前节点与下一点是否为相同值,如果相同,继续查找祥同值的最大长度,然后指针改变指向。

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
import java.util.*;public class Solution {public ListNode deleteDuplication(ListNode pHead) {//链表为空if(pHead == null){return null;}//存储重复的结点值HashSet<Integer> set = new HashSet<>();//定义前一个指针ListNode pre = pHead;//定义当前指针ListNode cur = pHead.next;//遍历存储重复值while(cur != null){if(pre.val == cur.val){set.add(pre.val);}pre = pre.next;cur = cur.next;}//直接删除法//定义新链表头结点ListNode head = new ListNode(0);//将新的链表加上原来的,处理原来的链表head.next = pHead;//从新链表的头节点开始pre = head;//当前结点指针指向原来链表头节点开始cur = pHead;//遍历原来链表while(cur != null){//包含这个结点值,证明这个结点是个重复结点if(set.contains(cur.val)){//指向下一个结点cur = cur.next;//屏蔽当前结点,直接跳到下一个结点,就去掉了pre.next = cur;}else{//不包含,证明这个结点无重复,留下pre = pre.next;cur = cur.next;}}//返回新的链表return head.next;}
}

方法三:递归

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
public class Solution {public ListNode deleteDuplication(ListNode pHead) {//链表为空或者只有一个结点,返回即可,边界条件if(pHead == null || pHead.next == null){return pHead;}//定义下一个结点ListNode next = pHead.next;//当前结点的值和下一个结点的值相等if(pHead.val == next.val){//下一个结点存在,并且一直相等while(next != null && pHead.val == next.val){//向后移一直到不等的下一个结点next = next.next;}//从下一个结点开始,前面相等的结点全部去掉return deleteDuplication(next);}else{//两个结点不等//当前结点不变,即存在,然后下一个结点继续pHead.next = deleteDuplication(pHead.next);//返回去掉重复结点的链表头结点return pHead;}}
}

JZ57 二叉树的下一个结点

题目:二叉树的下一个结点

直接查找下一个结点,分为三种情况。

  • 右子树存在
  • 右子树不存在,当前结点是父结点的左子树
  • 右子树不存在,当前结点是父结点的右子树

这题还有些不懂,但是暂时还想不通

/*
public class TreeLinkNode {int val;TreeLinkNode left = null;TreeLinkNode right = null;TreeLinkNode next = null;TreeLinkNode(int val) {this.val = val;}
}
*/
public class Solution {// 直接找下一个结点public TreeLinkNode GetNext(TreeLinkNode pNode) {// 当前结点右子树存在,中序下来就是右子树的左子树结点if (pNode.right != null) {// 保存右子树TreeLinkNode pRight = pNode.right;// 遍历整个右子树,找到其最下面的左子树while (pRight.left != null) {pRight = pRight.left;}return pRight;}// 没有右子树且该节点是父节点的左子树if (pNode.next != null && pNode.next.left == pNode) {return pNode.next;}// 没有右子树且该节点是父节点的右子树if(pNode.next != null){//没有右子树的根节点,没有父节点TreeLinkNode pNext = pNode.next;while(pNext.next != null && pNext.next.right == pNext){pNext = pNext.next;}return pNext.next;}return null;}
}

JZ59 按之字形顺序打印二叉树

题目:按之字形顺序打印二叉树

import java.util.*;/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {//存储返回值ArrayList<ArrayList<Integer>> res = new ArrayList<>();public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {//标志位,判断从左到右还是从右到左boolean flag = true;//队列,遍历存储二叉树Queue<TreeNode> queue = new LinkedList<>();//队列中加入根结点queue.offer(pRoot);//队列不为空while(!queue.isEmpty()){//每一行的结点数量int size = queue.size();//存储每一行的结点值ArrayList<Integer> list = new ArrayList<>();//循环每一个结点for(int i=0;i<size;i++){//取出结点TreeNode node = queue.poll();//队列空了,跳出当前循环if(node == null){continue;}//true代表从左到右//false代表从右到左if(flag){list.add(node.val);}else{list.add(0,node.val);}//队列中加入左右子树queue.offer(node.left);queue.offer(node.right);}//这一行不为空,加入if(list.size() != 0){res.add(list);}//下一行,方向改变,需要改变标志位flag = !flag;}//返回结果return res;}
}

JZ60 把二叉树打印成多行

题目:把二叉树打印成多行

  • 一定要考虑边界值,空树的情况。
  • 将每一层结点加入队列
  • 根据每一层结点数量,出队列,加入到list中
  • 将该结点的左右结点加入队列,即加入下一层结点
  • 将该层结点的值加入array中
  • 循环进行下一层,到最后一层为止
/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
import java.util.*;public class Solution {ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {//创建返回值ArrayList<ArrayList<Integer>> array = new ArrayList<ArrayList<Integer>>();//边界值if(pRoot == null){return array;}//创建队列,bfs,广度优先搜索,层次遍历Queue<TreeNode> queue = new LinkedList();//加入根节点queue.offer(pRoot);//队列不为空,遍历while(!queue.isEmpty()){//得到队列中结点个数int size = queue.size();//创建对象保存每一层的结点ArrayList<Integer> list = new ArrayList<>();//队列中有结点,出队列,每一层while(size-- != 0){//得到队列头元素,相当于指向队头的指针TreeNode node = queue.peek();//头元素出队列queue.poll();//将头元素加入那一层的arraylist中list.add(node.val);//下一层,左右子树结点加入if(node.left != null){queue.offer(node.left);}if(node.right != null){queue.offer(node.right);}}//将得到的每一层结点值加入array.add(list);}//返回所有层的结点return array;}}

JZ62 二叉搜索树的第k个结点

题目:二叉搜索树的第k个结点

二叉搜索树,左子树的值小于根节点小于右子树。

/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
import java.util.*;public class Solution {//非递归,中序遍历TreeNode KthNode(TreeNode pRoot, int k) {//如果是空树,或者k值不符合常理if(pRoot == null || k <= 0){return null;}Stack<TreeNode> stack = new Stack<>(); //建立栈//从当前结点,即头节点开始查找TreeNode cur = pRoot;//while 部分为中序遍历//栈内有结点或者当前结点存在while(!stack.isEmpty() || cur != null){if(cur != null){//当前节点不为null//入栈stack.push(cur); //寻找左儿子cur = cur.left;}else{//二叉搜索树的性质//当前节点null则弹出栈内元素,相当于按顺序输出最小值。cur = stack.pop();if(--k == 0){ //计数器功能return cur;}//寻找右儿子cur = cur.right;}}return null; }
}

剑指offer刷题总记——Java相关推荐

  1. 剑指offer刷题记录 python3 Java

    剑指offer刷题记录 python3 Java 剑指 Offer 09. 用两个栈实现队列 剑指 Offer 10- I. 斐波那契数列 剑指 Offer 03. 数组中重复的数字 [★]剑指 Of ...

  2. 力扣(LeetCode)剑指offer刷题笔记(java),已完结!!!

    文章目录 3.数组中重复的数字 4.二维数组中的查找 5.替换空格 6.从尾到头打印链表 7.重建二叉树 9.两个栈来实现一个队列 10-1.斐波那契数列 10-2.跳台阶 11.旋转数组的最小数字 ...

  3. 【LeetCode 剑指offer刷题】树题6:28 对称二叉树(101. Symmetric Tree)

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) 101. Symmetric Tree /**  * Definition for a binary tree no ...

  4. 【LeetCode 剑指offer刷题】数组题2:57 有序数组中和为s的两个数(167 Two Sum II - Input array is sorted)...

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) 57 有序数组中和为s的两个数 题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是 ...

  5. 【LeetCode 剑指offer刷题】字符串题6:67 把字符串转成整数

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) 67 把字符串转成整数 题目描述 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数. 数值为0或者字符 ...

  6. 【LeetCode 剑指offer刷题】树题16:Kth Smallest Element in a BST

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) Kth Smallest Element in a BST Given a binary search tree, ...

  7. 【LeetCode 剑指offer刷题】回溯法与暴力枚举法题6:Number of Islands

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) Number of Islands Given a 2d grid map of '1's (land) and ' ...

  8. 【LeetCode 剑指offer刷题】查找与排序题14:Wiggle Sort(系列)

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) Wiggle Sort II Given an unsorted array nums, reorder it su ...

  9. 【LeetCode 剑指offer刷题】查找与排序题12:Top K Frequent Elements

    [LeetCode & 剑指offer 刷题笔记]目录(持续更新中...) Top K Frequent Elements Given a non-empty array of integer ...

最新文章

  1. Python学习笔记2 基本数据类型
  2. UML与软件建模 第三次作业
  3. appendChild append insertBefore prepend
  4. ACE-Streams架构简介及应用
  5. 【云计算】K8S DaemonSet 每个node上都运行一个pod
  6. 深入理解PHP+Mysql分布式事务与解决方案
  7. 获取指定进程所对应的可执行(EXE)文件全路径(代码)
  8. Apache Tika源码研究(三)
  9. MATLAB/Simulimk 光伏发电+boost+储能+双向dcdc+并网逆变器控制(低压用户型电能路由器仿真模型)
  10. STS安装lombok插件
  11. c++ windows console 快速编辑模式 关闭
  12. 刘强东不是一个人,互联网寒冬真的来了
  13. Windows上的安全模式
  14. 计算机语言可读性排名,计算机语言可读性强,容易记忆
  15. python管理系统学习
  16. 2.7.0 gitk 打不开 Error in startup script: unknown color name lime
  17. OFDM 符号的概念
  18. UE4 UE4使用小技巧——使用上帝视角运行游戏
  19. Spring MVC框架——Web开发框架
  20. 读书笔记---季凯帆的《解读基金,我的投资观与实践》

热门文章

  1. CVPR2021 MotionRNN: A Flexible Model for Video Prediction with Spacetime-Varying Motions
  2. RPG Maker MV 遇敌对战
  3. 苹果系统手机调用java线程出错_Java线程面试题
  4. k8s创建service
  5. Apache DolphinScheduler 海豚调度器自定义时间参数
  6. ubuntu串口调试工具kermit和minicom
  7. 计算机微软云同步怎样安装软件,在windows10/8/7系统安装和设置OneDrive 同步文件...
  8. HWDB1.1数据集 | 手写汉字数据集 |.gnt 转换 .png格式图片| 【❤️有效转换❤️】
  9. 服务器c盘里会有大量.log文件,电脑C盘爆满怎么办?这3个文件夹放心删,瞬间多出10个G...
  10. 利用DirectShow开发C#版的音频文件播放器(补充完善)