目录
【案例1】
【题目描述】
【思路解析】
【代码实现】
【案例2】
【题目描述】
【思路解析】
【代码实现】
【案例3】
【题目描述】
【思路解析】
【代码实现】
【案例4】
【题目描述】 2018年美团面试题
【思路解析】
【代码实现】
【案例5】
【题目描述】
【思路解析】
【代码实现】
(8条消息) 详解前缀树和贪心算法_Studying~的博客-CSDN博客
(8条消息) 详解图论算法 图的宽度优先遍历 图的深度优先遍历 图的拓扑排序算法 kruskal算法 prim算法 Dijkstra算法_Studying~的博客-CSDN博客
先根据这个字符串类型的数组,先生成一个前缀树,然后再对这个前缀树进行深度优先遍历,在深度优先遍历时,根据层数来控制输出的空格即可。
import java.util.HashMap;
/**
* @ProjectName: study3
* @FileName: Ex1
* @author:HWJ
* @Data: 2023/7/24 15:38
*/
public class Ex1 {
public static void main(String[] args) {
String[] folderPaths = {"a\\b\\c", "b\\f\\k"};
ex(folderPaths);
}
public static void ex(String[] folderPaths){
Node node = generateFolderTree(folderPaths);
process(node, 0);
}
// 构建前缀树
public static Node generateFolderTree(String[] folderPaths){
Node head = new Node(" "); // 系统根目录,前缀树头节点
for (String folderPath : folderPaths) {
// folderPath.split("\\\\") 的split函数里面即含转义匹配又含正则匹配
// 通过转义匹配 \\\\ 被解释为 \\ , \\再通过正则匹配解释为 \,所以这\\\\最终被解释为\。
String[] paths = folderPath.split("\\\\");
Node cur = head;
for (int i = 0; i < paths.length; i++) {
// 根据前缀树规则,如果不存在这条路径就新建,存在就复用
if (!cur.nextMap.containsKey(paths[i])){
cur.nextMap.put((paths[i]), new Node(paths[i]));
}
cur = cur.nextMap.get(paths[i]);
}
}
return head;
}
// 深度优先遍历
public static void process(Node node, int level){
if(level != 0){
System.out.println(print2Space(level) + node.name);
}
for (Node cur : node.nextMap.values()) {
process(cur, (level + 1));
}
}
// 得到相应的空格
public static String print2Space(int n){
String s = "";
for (int i = 1; i < n; i++) {
s += " ";
}
return s;
}
}
class Node {
public String name;
public HashMap nextMap;
public Node(String name) {
this.name = name;
this.nextMap = new HashMap<>();
}
}
这里使用二叉树的递归套路,假设一个以Node节点开头的二叉树,如果他的左子树能转为一个双向链表,且他的右子树也能转为一个双向链表,则我们只需要将左子树的双向链表的末尾连向Node节点,右子树的双向链表的开头连向Node节点。然后左子树的双向链表的头,右子树的双向链表的尾就作为整个双向链表的头和尾。
/**
* @ProjectName: study3
* @FileName: Ex2
* @author:HWJ
* @Data: 2023/7/29 9:08
*/
public class Ex2 {
public static void main(String[] args) {
}
public static Node convert(Node head){
if (head == null){
return null;
}
return process(head).start;
}
// 搜索二叉树转为双向链表后返回的头和尾信息。
public static class Info{
public Node start;
public Node end;
public Info(Node start, Node end) {
this.start = start;
this.end = end;
}
}
// 节点类
public static class Node{
public int value;
public Node right;
public Node left;
public Node(int value) {
this.value = value;
}
}
public static Info process(Node X){
if (X == null){
return new Info(null, null);
}
Info leftHeadEnd = process(X.left);
Info rightHeadEnd = process(X.right);
if (leftHeadEnd.end != null){
leftHeadEnd.end.right = X;
X.left = leftHeadEnd.end;
}
if (rightHeadEnd.start != null){
X.right = rightHeadEnd.start;
rightHeadEnd.start.left = X;
}
return new Info(leftHeadEnd.end != null ? leftHeadEnd.end : X,
rightHeadEnd.end != null ? rightHeadEnd.end : X);
}
}
找到一颗二叉树中,最大的搜索二叉子树,返回最大搜索二叉子树的头结点。
对于这个二叉树的任意一个节点X,有两种情况。
(1) 他作为最大搜索二叉子树的头结点。则他需要满足左子树是搜索二叉树,右子树是搜索二叉树。左子树的最大值小于X节点的值,右子树的最小值大于X节点的值。
(2)他不作为最大搜索二叉子树的头结点。 需要他左子树的最大搜索二叉子树的头结点和大小的信息,还有需要他右子树的最大搜索二叉子树的头结点和大小的信息。通过比较大小,就得到了以X节点下的最大搜索二叉子树。
所以对于任意一个节点我们需要他的信息为,以他为头结点的子树是否是搜索二叉树,子树的最大值,子树的最小值,子树的最大搜索二叉子树的头节点,子树的最大搜索二叉子树的大小。
/**
* @ProjectName: study3
* @FileName: Ex3
* @author:HWJ
* @Data: 2023/7/29 9:48
*/
public class Ex3 {
public static void main(String[] args) {
}
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int value) {
this.value = value;
}
}
public static class Info {
public Node head; // 最大搜索二叉子树的头结点
public int max; // 这颗树的最大值。
public int min; // 这棵树的最小值
public boolean isBst; // 这棵树是否为搜索二叉树
public int size; // 最大搜索二叉子树的大小
public Info(Node head, int max, int min, boolean isBst, int size) {
this.head = head;
this.max = max;
this.min = min;
this.isBst = isBst;
this.size = size;
}
}
public static Info process(Node x) {
if (x == null) {
return new Info(null, Integer.MIN_VALUE, Integer.MAX_VALUE, true, 0);
}
Info leftBst = process(x.left);
Info rightBst = process(x.right);
int min = Math.min(x.value, Math.min(leftBst.min, rightBst.min));
int max = Math.max(x.value, Math.max(leftBst.max, rightBst.max));
boolean isBst = false;
int size = Math.max(leftBst.size, rightBst.size);
Node head = size == leftBst.size ? leftBst.head : rightBst.head;
if (leftBst.isBst && rightBst.isBst) {
if (leftBst.max < x.value && x.value < rightBst.min) {
size = leftBst.size + rightBst.size + 1;
isBst = true;
head = x;
}
}
return new Info(head, max, min, isBst, size);
}
}
设计两个变量,max和cur,max初始为系统最小Integer.MIN_VALUE,cur初始为0,遍历数组,cur+=arr[i],如果能使max更新则更新,如果不能则不变,如果cur<0,则使cur=0;
/**
* @ProjectName: study3
* @FileName: Ex4
* @author:HWJ
* @Data: 2023/7/29 11:00
*/
public class Ex4 {
public static void main(String[] args) {
}
public static int getMaxScore(int[] arr){
if (arr == null || arr.length == 0){
return 0;
}
int cur = 0;
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
cur += arr[i];
max = Math.max(cur, max);
cur = Math.max(cur, 0);
}
return max;
}
}
子矩阵只能为长方形或者正方形。
利用矩阵压缩技巧,对于子矩阵则它有多种情况,最优解可以出现在 第i行到第j行的最优长方形中,所以我们遍历各种矩阵,然后将矩阵压缩成一个一维数组,然后找到这个一维数组的最优解。
/**
* @ProjectName: study3
* @FileName: Ex5
* @author:HWJ
* @Data: 2023/7/29 11:12
*/
public class Ex5 {
public static void main(String[] args) {
}
public static int getMaxMatrix(int[][] map){
if (map == null || map.length == 0 || map[0].length == 0){
return 0;
}
int max = Integer.MIN_VALUE;
int cur = 0;
int[] s = null;
// 开始的行号i
for (int i = 0; i < map.length; i++) {
s = new int[map[i].length];
for (int j = i; j < map.length; j++) { // 以i行号开始,以j行号结尾
cur = 0;
for (int k = 0; k < map[0].length; k++) {
s[k] += map[j][k];
cur += s[k];
max = Math.max(cur, max);
cur = Math.max(0, cur);
}
}
}
return max;
}
}