剑指Offer-68道面试题(61-68题)-Java实现

61. 序列化二叉树 – 复杂问题简单化

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
class Solution {
    int index = -1;
    
    String Serialize(TreeNode root) {
        StringBuffer sb = new StringBuffer();
        if (root == null) {
            sb.append("#,");
            return sb.toString();
        }
        sb.append(root.val + ",");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
    }
    
    TreeNode Deserialize(String str) {
        index++;
        int len = str.length();
        if (index >= len) {
            return null;
        }
        String[] strr = str.split(",");
        TreeNode node = null;
        if (!strr[index].equals("#")) {
            node = new TreeNode(Integer.valueOf(strr[index]));
            node.left = Deserialize(str);
            node.right = Deserialize(str);
        }
        return node;
    }
}
62. 二叉搜索树的第K个结点 – 知识迁移能力

题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    int index = 0;
    
    TreeNode KthNode(TreeNode pRoot, int k) {
        if (pRoot != null && k != 0) {
            TreeNode node = KthNode(pRoot.left, k);
            if (node != null)
                return node;
            index++;
            if (index == k)
                return pRoot;
            node = KthNode(pRoot.right, k);
            if (node != null)
                return node;
        }
        return null;
    }
}
import java.util.Stack;
public class Solution {
    TreeNode KthNode(TreeNode root, int k) {
        if (root == null || k == 0)
            return null;
        int count = 0;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            count++;
            if (count == k)
                return root;
            root = root.right;
        }
        return null;
    }
}
63 数据流中的中位数 – 时间效率

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

数据结构		插入的时间复杂度		获取中位数的时间复杂度
没排序的数组
排序的数组
排序的链表			思考
二叉搜索树
AVL树
最大堆和最小堆
import java.util.Comparator;
import java.util.PriorityQueue;
class maxComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer num1, Integer num2) {
        return num1 >= num2 ? -1 : 1;
    }
}
public class Solution {
    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15,
            new maxComparator());// 大顶堆
    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();// 小顶堆
    
    public void Insert(Integer num) {
        int len = minHeap.size() + maxHeap.size();
        // 将奇数位为添加到小顶堆,小顶堆保存大的数,那么小顶堆的堆顶大于等于大顶堆的堆顶
        if (len % 2 == 0) {
            maxHeap.offer(num);
            int max = maxHeap.poll();
            // 相对于,洗出最大数放入小顶堆
            minHeap.offer(max);
        } else {
            // 将偶数位为添加到大顶堆,大顶堆保存小的数,那么大顶堆的堆顶小于等于小顶堆的堆顶
            minHeap.offer(num);
            int min = minHeap.poll();
            maxHeap.offer(min);
        }
    }
    
    public Double GetMedian() {
        int len = maxHeap.size() + minHeap.size();
        double result = 0.0;
        if (len == 0) {
            return result;
        }
        if (len % 2 == 1) {
            result = minHeap.peek();
        }
        if (len % 2 == 0) {
            result = (minHeap.peek() + maxHeap.peek()) / 2.0;
        }
        return result;
    }
}
64. 滑动窗口的最大值 – 知识迁移能力

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> maxInWindows(int[] num, int size) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (size == 0) {
            return list;
        }
        int max = num[0];
        int index = -1;
        for (int i = 0; i <= num.length - size; i++) {
            if (index >= i && max > num[i + size - 1]) {
                list.add(max);
            } else {
                max = num[i];
                for (int j = i; j < i + size; j++) {
                    if (num[j] > max) {
                        max = num[j];
                        index = j;
                    }
                }
                list.add(max);
            }
        }
        return list;
    }
}
65. 矩阵中的路径 – 算法和数据操作

题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
a b c e
s f c s
a d e e
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
        boolean result = false;
        boolean[][] vis = new boolean[rows + 2][cols + 2];
        int strLen = str.length;
        for (int i = 0; i < matrix.length; i++) {
            if (matrix[i] == str[0]) {
                result = hasPathCore(matrix, rows, cols, str, strLen, vis, i
                        / cols, i % cols, 0);
                if (result) {
                    break;
                }
            }
        }
        return result;
    }
    
    public boolean hasPathCore(char[] matrix, int rows, int cols, char[] str,
            int strLen, boolean[][] vis, int x, int y, int strindex) {
        boolean result = false;
        if (strindex == strLen) {
            result = true;
            return result;
        }
        if (x >= rows || x < 0 || y >= cols || y < 0 || vis[x][y]
                || (matrix[x * cols + y] != str[strindex])) {
            return result;
        } else {
            vis[x][y] = true;
            strindex++;
            result = hasPathCore(matrix, rows, cols, str, strLen, vis, x + 1,
                    y, strindex)
                    || hasPathCore(matrix, rows, cols, str, strLen, vis, x - 1,
                            y, strindex)
                    || hasPathCore(matrix, rows, cols, str, strLen, vis, x,
                            y + 1, strindex)
                    || hasPathCore(matrix, rows, cols, str, strLen, vis, x,
                            y - 1, strindex);
            if (!result) {
                strindex--;
                vis[x][y] = false;
            }
        }
        return result;
    }
}
66. 机器人的运动范围 – 算法和数据操作

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

public class Solution {
    public static int movingCount(int threshold, int rows, int cols) {
        if (threshold < 0 || rows <= 0 || cols <= 0) {
            return 0;
        }
        int count = 0;
        boolean[][] vis = new boolean[rows + 5][cols + 5];
        count = movingCountCore(threshold, rows, cols, 0, 0, vis);
        return count;
    }
    
    public static int movingCountCore(int threshold, int rows, int cols,
            int row, int col, boolean[][] vis) {
        int count = 0;
        if (row >= rows || row < 0 || col >= cols || col < 0 || vis[row][col]) {
            return count;
        }
        int sum = 0;
        int rowtemp = row;
        int coltemp = col;
        while (rowtemp > 0) {
            sum = sum + rowtemp % 10;
            rowtemp /= 10;
        }
        while (coltemp > 0) {
            sum = sum + coltemp % 10;
            coltemp /= 10;
        }
        if (sum > threshold) {
            return count;
        } else {
            vis[row][col] = true;
            sum++;
            count = 1
                    + movingCountCore(threshold, rows, cols, row + 1, col, vis)
                    + movingCountCore(threshold, rows, cols, row, col + 1, vis);
            // + movingCountCore(threshold, rows, cols, row, col - 1, vis)
            // + movingCountCore(threshold, rows, cols, row - 1, col, vis);
        }
        return count;
    }
}
67. 剪绳子 – 算法和数据操作

题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入描述:
输入一个数n,意义见题面。(2 <= n <= 60)

public class Solution {
    public int cutRope(int target) {
        if (target == 2) {
            return 1;
        }
        if (target == 3) {
            return 2;
        }
        if (target % 3 == 0) {
            return (int) Math.pow(3, target / 3);
        }
        if (target % 3 == 1) {
            return (int) Math.pow(3, target / 3 - 1) * 4;
        }
        if (target % 3 == 2) {
            return (int) Math.pow(3, target / 3) * 2;
        }
        return 0;
    }
}
68. 实现Singleton模式 – 设计模式

题目描述
设计一个类,我们只能生成该类的一个实例。

class Singleton {
    // 饿汉式
    // private static final Singleton INSTANCE = new Singleton();
    // private Singleton(){}
    // public static Singleton getInstance(){return INSTANCE;}
    
    // 懒汉式
    private static volatile Singleton instance = null;
    private Singleton() {
    }
    /**
     * 2.这样同步:public static synchronized Singleton getInstance(){}
     * 这样同步的代价就是效率会低。 因为整体代码里面实际上只有一部分需要进行同步,就是instance对象的实例化处理部分
     */
    public static Singleton getInstance() {
        /**
         * 1.单例设计的最大特点是在整体的运行过程之中只允许产生一个实例化对象
         * 问题造成的关键在于代码本身出现了不同步的情况,需要进行同步处理,synchronized关键字
         */
        // if(instance == null){
        // instance = new Singleton();
        // }
        /**
         * 3.这样同步:if(instance == null){ synchronized(Singleton.class){ instance
         * = new Singleton();}} 还是不行,因为会有多个线程通过第一个判断
         */
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                    /**
                     * 4.如果对象被实例化的时候, 应该立刻与主内存中的数据对象进行同步,而不应该存副本。
                     * 所以最好在加个volatile关键字
                     */
                }
            }
        }
        return instance;
    }
}

其他题目

→→点击跳转

猜你喜欢

转载自blog.csdn.net/weixin_43845524/article/details/105752933