算法题笔记

重建二叉树

重建二叉树
在这里插入图片描述

在这里插入图片描述

    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];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值