Java算法优化笔记(持续更新)

博客涵盖了在OJ平台上处理树结构输入的问题,详细解释了如何使用深度优先搜索(DFS)解决机器人运动范围问题。此外,还介绍了二叉树的输入格式,包括先序序列化和层序序列化的输出描述。最后,讨论了剪绳子问题的贪心算法和动态规划解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OJ平台的树结构输入

import java.io.*;
import java.util.*;
public class Main{
    public static class Node {
    public int value;
    public Node left;
    public Node right;
    public Node(int val) {
        this.value = val;
    }
    }
    //map的K是节点的值,V是这个节点在nodes节点数组中的索引
    public static Node createTree(int root, int[][] childinfo, Node[] nodes, HashMap<Integer,Integer> map) {
        for(int i = 0; i < nodes.length; i++) {
            int left = childinfo[i][0];
            int right = childinfo[i][1];
            nodes[i].left = left == 0 ? null : nodes[map.get(left)];
            nodes[i].right = right == 0 ? null : nodes[map.get(right)];
        }
        return nodes[map.get(root)];
    }
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] s = br.readLine().split(" ");
        int tree1 = Integer.parseInt(s[0]);
        int root1 = Integer.parseInt(s[1]);
        int[][] childinfo1 = new int[tree1][2];
        Node[] nodes1 = new Node[tree1];
        HashMap<Integer,Integer> map1 = new HashMap<>();
        for(int i = 0; i < tree1; i++) {
            s = br.readLine().split(" ");
            Node node = new Node(Integer.parseInt(s[0]));
            nodes1[i] = node;
            childinfo1[i][0] = Integer.parseInt(s[1]);
            childinfo1[i][1] = Integer.parseInt(s[2]);
            map1.put(node.value,i);
        }
        Node first = createTree(root1,childinfo1,nodes1,map1);
        s = br.readLine().split(" ");
        int tree2 = Integer.parseInt(s[0]);
        int root2 = Integer.parseInt(s[1]);
        //System.out.println(root2);
        int[][] childinfo2 = new int[tree2][2];
        Node[] nodes2 = new Node[tree2];
        HashMap<Integer,Integer> map2 = new HashMap<>();
        for(int i = 0; i < tree2; i++) {
            s = br.readLine().split(" ");
            Node node = new Node(Integer.parseInt(s[0]));
            //System.out.println(s[0]);
            nodes2[i] = node;
            childinfo2[i][0] = Integer.parseInt(s[1]);
            childinfo2[i][1] = Integer.parseInt(s[2]);
            map2.put(node.value,i);
        }
        Node second = createTree(root2,childinfo2,nodes2,map2);
        System.out.print(contains(first,second)?"true":"false");
    }
 
    public static boolean contains(Node t1, Node t2){
        if (t2==null){
            return true;
        }
        if (t1==null){
            return false;
        }
        return check(t1,t2) || contains(t1.left,t2) || contains(t1.right,t2);
    }
    public static boolean check(Node h,Node t2){
        if (t2==null){
            return true;
        }
        if (h==null || h.value!=t2.value){
            return false;
        }
        return check(h.left,t2.left) && check(h.right,t2.right);
    }
}

机器人的运动范围

题目描述
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。
例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?

解题思路
使用深度优先搜索(Depth First Search,DFS)方法进行求解。回溯是深度优先搜索的一种特例,它在一次搜索过程中需要设置一些本次搜索过程的局部状态,并在本次搜索结束之后清除状态。而普通的深度优先搜索并不需要使用这些局部状态,虽然还是有可能设置一些全局状态。


private static final int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
private int cnt = 0;
private int rows;
private int cols;
private int threshold;
private int[][] digitSum;
 
public int movingCount(int threshold, int rows, int cols) {
    this.rows = rows;
    this.cols = cols;
    this.threshold = threshold;
    initDigitSum();
    boolean[][] marked = new boolean[rows][cols];
    dfs(marked, 0, 0);
    return cnt;
}
 
private void dfs(boolean[][] marked, int r, int c) {
    if (r < 0 || r >= rows || c < 0 || c >= cols || marked[r][c])
        return;
    marked[r][c] = true;
    if (this.digitSum[r][c] > this.threshold)
        return;
    cnt++;
    for (int[] n : next)
        dfs(marked, r + n[0], c + n[1]);
}
 
private void initDigitSum() {
    int[] digitSumOne = new int[Math.max(rows, cols)];
    for (int i = 0; i < digitSumOne.length; i++) {
        int n = i;
        while (n > 0) {
            digitSumOne[i] += n % 10;
            n /= 10;
        }
    }
    this.digitSum = new int[rows][cols];
    for (int i = 0; i < this.rows; i++)
        for (int j = 0; j < this.cols; j++)
            this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
}

二叉树的输入

输入描述:

第一行输入两个整数 n 和 root,n 表示二叉树的总节点个数,root 表示二叉树的根节点。
以下 n 行每行三个整数 fa,lch,rch,表示 fa 的左儿子为 lch,右儿子为 rch。(如果 lch 为 0 则表示 fa 没有左儿子,rch同理)

输出描述:
输出两行分别表示该二叉树的先序序列化和层序序列化
示例1输入
2 1
1 2 0
2 0 0

输出
1!2!#!#!#!
1!2!#!#!#!

import java.io.*;
import java.util.*;
public class Main {
    static class Node{
        Node left;
        Node right;
        int val;
        Node(int val){
            this.val = val;
        }
    }
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] strs = reader.readLine().split(" ");
        
        int n = Integer.valueOf(strs[0]);
         
        Node root = new Node(Integer.valueOf(strs[1]));
         
        HashMap<Integer,Node> map = new HashMap<>();
         
        map.put(root.val,root);
         
        Node left,right,parent;
         
        //构建二叉树
        for(int i=0;i<n;i++) {
            String[] content = reader.readLine().split(" ");
            parent = map.get(Integer.valueOf(content[0]));
             
            int leftValue = Integer.valueOf(content[1]);
            int rightValue = Integer.valueOf(content[2]);
             
            left =  leftValue == 0 ? null : new Node(leftValue);
            right =  rightValue == 0 ? null : new Node(rightValue);
             
            parent.left = left;
            parent.right = right;
             
            map.put(leftValue,left);
            map.put(rightValue,right);
        }
        /*
        代码执行到这里的时候 树结构已经建立完毕,直接调用root就相当于调用了树结构。
        如果输入示例建完树以后还有一行,如下
        2 1
        1 2 0
        2 0 0
        4
        可以通过 String[] s=reader.readLine().split(" "); 进行获取
        数据的输入问题解决以后就可以调用算法进行test了
        */
        StringBuilder sb = new StringBuilder();
        //前序遍历
        preOrder(root,sb);
        System.out.println(sb.substring(0, sb.length() - 1));
        //清空StringBuilder
        sb.delete(0,sb.length());
        //中序遍历
        inOrder(root,sb);
        System.out.println(sb.substring(0, sb.length() - 1));
        sb.delete(0,sb.length());
        //后序遍历
        postOrder(root,sb);
        System.out.println(sb.substring(0, sb.length() - 1));

    }
 
    private static StringBuilder preOrder(Node treeNode, StringBuilder sb){     //前序遍历
        if (treeNode == null){
            return null;
        }
        sb.append(treeNode.val + " ");
        preOrder(treeNode.left,sb);
        preOrder(treeNode.right,sb);
        return sb;
    }
 
    private static StringBuilder inOrder(Node treeNode, StringBuilder sb){      //中序遍历
        if (treeNode == null){
            return null;
        }
        inOrder(treeNode.left,sb);
        sb.append(treeNode.val + " ");
        inOrder(treeNode.right,sb);
        return sb;
    }
 
    private static StringBuilder postOrder(Node treeNode, StringBuilder sb){    //后序遍历
        if (treeNode == null){
            return null;
        }
        postOrder(treeNode.left,sb);
        postOrder(treeNode.right,sb);
        sb.append(treeNode.val + " ");
        return sb;
    }
 
}

  1. 剪绳子

Leetcode
题目描述
把一根绳子剪成多段,并且使得每段的长度乘积最大。
解题思路
贪心算法或动态规划算法
尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。
证明:当 n >= 5 时,3(n - 3) - n = 2n - 9 > 0,且 2(n - 2) - n = n - 4 > 0。因此在 n >= 5 的情况下,将绳子剪成一段为 2 或者 3,得到的乘积会更大。又因为 3(n - 3) - 2(n - 2) = n - 5 >= 0,所以剪成一段长度为 3 比长度为 2 得到的乘积更大。

//贪心算法
public int integerBreak(int n) {
    if (n < 2)
        return 0;
    if (n == 2)
        return 1;
    if (n == 3)
        return 2;
    int timesOf3 = n / 3;
    if (n - timesOf3 * 3 == 1)
        timesOf3--;
    int timesOf2 = (n - timesOf3 * 3) / 2;
    return (int) (Math.pow(3, timesOf3)) * (int) (Math.pow(2, timesOf2));
}
//动态规划
public int integerBreak(int n) {
    int[] dp = new int[n + 1];
    dp[1] = 1;
    for (int i = 2; i <= n; i++)
        for (int j = 1; j < i; j++)
            dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j)));
    return dp[n];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值