重建二叉树
public TreeNode reConstructBinaryTree(int[] pre, int[] vin) {
if (pre == null || vin == null || pre.length != vin.length) {
return null;
}
return build(pre, 0, pre.length - 1, vin, 0, vin.length - 1);
}
private TreeNode build(int[] pre, int pre_start, int pre_end, int[] vin, int vin_start, int vin_end) {
if (pre_start > pre_end || vin_start > vin_end) {
return null;
}
TreeNode root = new TreeNode(pre[pre_start]);
for (int j = vin_start; j <= vin_end; j++) {
if (pre[pre_start] == vin[j]) {
root.left = build(pre, pre_start + 1, pre_start+ j - vin_start, vin, vin_start, j - 1);
root.right = build(pre,pre_start+ j - vin_start + 1, pre_end, vin, j + 1, vin_end);
break;
}
}
return root;
}
输出斐波那契数列的第 n 项。
斐波那契数列
【剪枝将重复的叶子树用Map集合过滤】
Map<Integer, Integer> map = new HashMap<>();//剪枝
public int Fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
}
int pre =0;//找n-1
if (map.containsKey(n-1)){
pre = map.get(n-1);
}else {
pre = Fibonacci(n-1);
map.put(n-1,pre);
}
int ppre =0;//找n-2
if (map.containsKey(n-2)){
ppre =map.get(n-2);
}else {
ppre = Fibonacci(n-2);
map.put(n-2,ppre);
}
return ppre+pre;
}
跳台阶
题目:青蛙跳台阶
DP动态规划
public static int jumpFloor(int target) {
if (target == 0){
return 0;
}
if (target <= 2){
return target;
}
int[] dp = new int[target + 1];
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= target; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[target];
}
二进制中1的个数
public int NumberOf1(int n) {
int count = 0;
while (n != 0) {
int x = n - 1;
n = n & x;
count++;
}
return count;
}
合并两个排序的链表
方法一:创建虚拟头节点对两个链表拼接
方法二:递归实现方法一
合并两个排序的 链表
ListNode dummyHead =new ListNode(0);
ListNode tail =dummyHead;
public ListNode Merge(ListNode list1, ListNode list2) {
build(list1,list2);
return dummyHead.next;
}
private void build(ListNode list1, ListNode list2) {
if (list1 == null && list2 == null){
return;
}
if (list1 == null) {
tail.next =list2;
return;
}
if (list2 == null) {
tail.next = list1;
return;
}
if (list1.val < list2.val){
tail.next = list1;
list1 = list1.next;
}else {
tail.next = list2;
list2 =list2.next;
}
tail = tail.next;
build(list1,list2);
}
树的子结构
[空树不为子结构的情况]牛客原题
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
if (root1 == null || root2 == null) {
return false;
}
Boolean isSame = false;
if (root1.val == root2.val) {
isSame = isSameChild(root1, root2);//判断子树是否相同
}
if (!isSame) {//当前头节点不是,在左子树找
isSame = HasSubtree(root1.left, root2);
}
if (!isSame) {//左子树没有在右子树找
isSame = HasSubtree(root1.right, root2);
}
return isSame;
}
private Boolean isSameChild(TreeNode root1, TreeNode root2) {
if (root2 == null) {//树2遍历结束都没找到不一样的返回真
return true;
}
if (root1 == null) {//树2都没走完,树1走完了返回假
return false;
}
if (root1.val != root2.val) {//判断根节点
return false;
}
return isSameChild(root1.left, root2.left) && isSameChild(root1.right, root2.right);//判断左右子树
}
栈的压入、弹出序列
牛客原题
思路:采用栈结构保存输入顺序,当栈顶某一个数等于输出数组中的数,开始弹出,直到输入数组走完,若栈中还有数返回假,否则返回真
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();
int j = 0;
for(int i =0;i < pushA.length; i++){
stack.push(pushA[i]);
while(!stack.empty() && stack.peek() == popA[j]){
stack.pop();
j++;
}
}
return stack.empty();
}
二叉搜索树的后序遍历序列
牛客原题
【思路】后序遍历–左右根,数组末尾必为二叉树的根节点,在数组中,左区间必小于根节点,右区间必大于根节点
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence.length == 0){
return false;
}
return isBST(sequence,0,sequence.length-1);
}
public boolean isBST(int[] arr,int start,int end){
if(start >= end){//终止条件,当遍历结束都没返回false,则表示此树为二叉树
return true;
}
int i =0;
while(i <= end-1 && arr[i] <= arr[end]){
i++;//找到第一个大于end的索引
}
for(int j = i;j < end;j++){
if(arr[j] < arr[end]){
return false;//在右区间若有小于end索引的值返回false
}
}
return isBST(arr,start,i -1) && isBST(arr,i,end -1);//在左区间和右区间判断
}
二叉树中和为某一值的路径(二)
牛客原题
【思路】
List<List<Integer>> ret = new ArrayList();
List<Integer> arr = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
find(root, targetSum);
return ret;
}
private void find(TreeNode root, int x) {
if (root == null) {
return;
}
arr.add(root.val);
x -= root.val;
if (root.left == null && root.right == null && x==0){//回溯剪枝
ret.add(new ArrayList<Integer>(arr));
}
find(root.left,x);
find(root.right,x);
arr.remove(arr.size()-1);//回退
}
字符串的排列
public ArrayList<String> Permutation(String str) {
ArrayList<String> ret = new ArrayList<>();
if (str != null && str.length() > 0) {
PermutationHelper(str.toCharArray(), 0, ret);//从索引0开始全排列
Collections.sort(ret);//按照字典序输出
}
return ret;
}
private void PermutationHelper(char[] str, int start, ArrayList<String> ret) {
if (start == str.length - 1) {//若已经排列到最后一个
if (!isExist(ret, str)) {//若动态数组中没有这个str
ret.add(new String(str));
}
return;
}
for (int i = start; i < str.length; i++) {
Swap(str, start, i);//交换i与start,以i为起点,i+1之后进行全排列
PermutationHelper(str, start + 1, ret);
Swap(str, start, i);//换回来
}
}
private boolean isExist(ArrayList<String> ret, char[] str) {
return ret.contains(String.valueOf(str));
}
private void Swap(char[] str, int start, int i) {
char temp = str[start];
str[start] = str[i];
str[i] = temp;
}
数组中只出现一次的数字
思路:
public class OnceTime {
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public static void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
if (array == null || num1 == null || num2 == null) {
return;
}
int temp = array[0];
//第一步,将所有数据进行异或
for (int i = 1; i < array.length; i++) {
temp ^= array[i];
}
//第二步,根据题面,最终结果一定不为0,找到该数据第一个为1的比特位,从高向底
int num = 1;
int size = Integer.SIZE;
int i =0;
while (size >= 0) {
size -= 1;
if ((num << i & temp) != 0) {
num <<= i;
break;
}
i++;
}
num1[0] = 0;
num2[0] = 0;
//第三步,分组
for (int x : array) {
if ((x & num) == 0) {
num1[0] ^= x;
} else {
num2[0] ^= x;
}
}
}
}
动态规划
斐波那契
public int Fibonacci(int n) {
if (n == 0) {
return 0;
}
if (n == 1 || n == 2) {
return 1;
}
int dp1 = 1;
int dp2 = 1;
int dpn = 0;
for (int i = 3; i <= n; i++) {
dpn = dp2 + dp1;//f(n) = f(n-1)+ f(n-2);
dp1 = dp2;
dp2 = dpn;
}
return dpn;
}
拆分词句
牛客原题
定义状态:f(n) = 前n个字符能否根据词典中的词被成功分词
转移方程:f(n) = f(j) && substr[j+1,i) ,其中j<i,只要能找到一个F(j)为true,并且从j+1到i之间的字符能在词典 中找到,则F(i)为true
初始值:f(0) = true;
public boolean wordBreak(String s, Set<String> dict) {
if (dict == null || s.length() == 0) {
return false;
}
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i < s.length(); i++) {
for (int j = i - 1; j > 0; j--) {
if (dp[j] && dict.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
CC31 三角形牛客原题
定义状态 f(i)(j) = 从 f(i)(j)到f(0)(0)的最短路径
转移方程: f(i)(j) = Math.min(f(i+1)(j),f(i+1)(j+1))+array(i)(j);
初始状态: f(row -1)(j) = array(row -1 )(j);
返回结果 : f(0)(0);
public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
if (triangle.isEmpty()){
return 0;
}
int row =triangle.size();
ArrayList<ArrayList<Integer>> ret = new ArrayList<>(triangle);
for (int i = row -2; i >= 0; i--) {//从到数第二行开始
for (int j = 0; j <= i; j++) {
int num = Math.min(triangle.get(i+1).get(j),triangle.get(i+1).get(j+1))+triangle.get(i).get(j);
ret.get(i).set(j,num);
}
}
return ret.get(0).get(0);
}
CC88 不同路径的数目(一)牛客原题
定义状态:dp[i][j] 从dp[0]0[]到dp[i][j]的所有不同路径
状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1] 因为只有向右或者向下才能走到
初始值 第一行、第一竖行都是一种走法
dp[0][i] = dp[j][0] = 1;i<=m,j<=n;
返回dp[m-1][n-1];
public int uniquePaths(int m, int n) {
if (m == 1 || n == 1) {
return 1;
}
int[][] dp = new int[m][n];
for (int i = 0; i < m; i++) {
dp[i][0] = 1;//初始值
}
for (int i = 0; i < n; i++) {
dp[0][i] = 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];
}
}
return dp[m - 1][n - 1];
}
CC86 带权值的最小路径和牛客原题
定义状态:f(i)(j)表示从f(0)(0)走到(i,j)路径之和
转移方程:f(i)(j) += Math.min(f(i-1)(j),f(i)(j-1));
初始化值:f(i)(0) += f(i-1)(0); i>=1
f(0)(i) += f(0)(i-1); i >=1,初始化第一列和第一行的值
返回 f(m-1)(n-1);
public int minPathSum(int[][] grid) {
if (grid.length == 0) {
return 0;
}
int row = grid.length;
int col = grid[0].length;
for (int i = 1; i < row; i++) {
grid[i][0] += grid[i][0];//初始化
}
for (int i = 1; i < col; i++) {
grid[0][i] += grid[0][i];//初始化
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
}
}
return grid[row - 1][col - 1];
}