文章目录
- 引言
 - 复习
 - 有塔游戏——关联传递性实现
 - 复习实现
 - 参考实现
 
- 新作
 - 随机链表的复制
 - 个人实现
 - 参考实现
 
- 排序链表
 - 个人实现
 - 参考实现
 
- 二叉树章节
 - 二叉树的中序遍历
 - 个人实现
 
- 二叉树的最大深度
 - 个人实现
 - 参考实现
 
- 反转二叉树
 - 个人实现
 - 参考实现
 
- 总结
 
引言
- 旅游完回来了,今天继续开始准备秋招了,目前除了继续刷算法,还应该抽出时间继续完善一下项目。除此之外,关于很多java的底层的东西,都是看过了,就忘记了,以后每天得抓紧时间开始背书,不然只会更加难过!
 - 加油!
 
复习
有塔游戏——关联传递性实现
题目内容
-  
给你一个阈值x,然后给你m个点的坐标,然后计算每一个点彼此之间的距离,如果距离小于等于x,那么这两个点就是相关联的。关联具有传递性,A和B关联,B和C关联,那么A和C关联。
 -  
输出所有关联的集合,每一行表示对应序号的点是关联的,使用空格尽心分割,同一行按照从小到大进行排序,不同行之间按照首字母的从小到大进行排序。
 -  
第一次做学习
 -  
输入样例
 
2.0
5
1.0 2.0
3.0 2.0 
1.0 3.0
5.0 9.5
8.9 1.0
 
- 输出样例
 
0 1 2
3
4
 
复习实现
- 这里使用深度遍历dfs去实现,深度是遍历对应的元素数量,单次遍历的内容是当前元素在邻接矩阵中所在的行。
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
class Main{
    static List<Integer> list = new ArrayList<>();
    static boolean[] visited;
    static boolean[][] link;
    static void dfs(int idx,int count){
        for(int i = 0;i < count;i ++){
            if(link[idx][i] && !visited[i]){
                visited[i] = true;
                list.add(i);
                dfs(i,count);
            }
        }
    }
    public static void main(String args[]){
        Scanner in = new Scanner(System.in);
        float edge = (float)Math.pow(in.nextFloat(),2);
        int count = in.nextInt();
        float[][] nodes = new float[count][2];
        for(int i = 0;i < count;i ++){
            nodes[i][0] = in.nextFloat();
            nodes[i][1] = in.nextFloat();
        }
        // cal the neighbour matrix
        link = new boolean[count][count];
        for(int i = 0;i < count;i ++){
            for(int j = i + 1;j < count;j ++){
                float dist = (float)(Math.pow(nodes[i][0] - nodes[j][0],2) +
                        Math.pow(nodes[i][1] - nodes[j][1],2));
                link[i][j] = edge >= dist;
                link[j][i] = link[i][j];
            }
        }
        // gene the visited matrix and dfs the result
        visited = new boolean[count];
        for(int i = 0;i < count;i ++){
            if(!visited[i]){
                visited[i] = true;
                list.add(i);
                dfs(i,count);
                Collections.sort(list);
                System.out.println(list);
                list.clear();
            }
        }
    }
}
 

参考实现
- 这里看了一下GPT,使用了并查集实现,感觉代码比我写的要简洁,而且时间复杂度更低,没研究过并查集,一会去研究一下!
 
import java.util.*;
public class PointsClusters {
    // 并查集中的查找操作
    private static int find(int[] parent, int i) {
        if (parent[i] != i) {
            parent[i] = find(parent, parent[parent[i]]);
        }
        return parent[i];
    }
    // 并查集中的合并操作
    private static void union(int[] parent, int[] rank, int x, int y) {
        int rootX = find(parent, x);
        int rootY = find(parent, y);
        if (rootX != rootY) {
            if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            } else if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            } else {
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }
    // 计算点之间的关联集合
    public static List<List<Integer>> getConnectedComponents(double[][] points, double threshold) {
        int n = points.length;
        int[] parent = new int[n];
        int[] rank = new int[n];
        
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            rank[i] = 0;
        }
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                double dist = Math.sqrt(Math.pow(points[i][0] - points[j][0], 2) + Math.pow(points[i][1] - points[j][1], 2));
                if (dist <= threshold) {
                    union(parent, rank, i, j);
                }
            }
        }
        Map<Integer, List<Integer>> components = new HashMap<>();
        for (int i = 0; i < n; i++) {
            int root = find(parent, i);
            components.computeIfAbsent(root, k -> new ArrayList<>()).add(i);
        }
        List<List<Integer>> result = new ArrayList<>();
        for (List<Integer> component : components.values()) {
            Collections.sort(component);
            result.add(component);
        }
        result.sort(Comparator.comparingInt(a -> a.get(0)));
        return result;
    }
    // 格式化输出
    public static String formatOutput(List<List<Integer>> components) {
        StringBuilder sb = new StringBuilder();
        for (List<Integer> component : components) {
            for (int i = 0; i < component.size(); i++) {
                if (i > 0) sb.append(" ");
                sb.append(component.get(i));
            }
            sb.append("\n");
        }
        return sb.toString();
    }
    public static void main(String[] args) {
        double x = 5.0;
        double[][] points = {{0, 0}, {1, 2}, {3, 4}, {8, 8}, {7, 7}};
        List<List<Integer>> components = getConnectedComponents(points, x);
        String output = formatOutput(components);
        System.out.print(output);
    }
}
 
并查集这个,就是合并成最小生成树呀,然后返回对应的最小生成树,这样做确实效率会更高一点,但是这个算法感觉要花比较多的时间重新做!
 大概看了一下并查集,这里使用路径压缩,确实效果会更好,暂时先跳过,等到了图论的时候,在进行补充!
新作
随机链表的复制
- 题目链接
 

 
 注意
- 输入的单纯是一个head头节点,返回也仅仅包含对应复制链表的head头节点
 - 防止新的链表的指针指向旧的链表指针。
 
个人实现
- 这题如果没有随机指针,会简单很多,就单纯复制对应的链表以及对应的next指针就行。现在需要复制随机链表,如果每一个节点都需要进行遍历一次,这个时间复杂度太高了,这里如何保存对应的,指向它的指针,然后进行反向遍历!

 
 /*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;
    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        // define the dummy node to sotre the res
        Node dummy = new Node(-1);
        Node current = dummy;
        // define the map to store the pos and pointer of the node 
        Node[] idxToPointer = new Node[1001];
        Map<Node,Integer> map = new HashMap<>();
        Node temp = head;
        for(int i = 0;temp != null;temp = temp.next){
            map.put(temp,i);
            current.next = new Node(temp.val);
            idxToPointer[i] = current.next;
            current = current.next;
            i ++;
        }
        // traverse the origin list to copy random pointer
        temp = head;
        for(int i = 0;temp != null;temp = temp.next,i ++){
            int randomIdx = map.getOrDefault(temp.random,-1);
            if(randomIdx != -1){
                idxToPointer[i].random = idxToPointer[randomIdx];
                //System.out.println(idxToPointer[i].random.val);
            }
        
        }
        return dummy.next;
       
        
    }
}
 

 这里忘记加上对应的i的迭代条件,调了半天没有调整出来,还是gpt给我调整出来,这可不行,如果是手撕的话,我这里写错了,就尴尬了!
参考实现
- 这里使用回溯实现的,将原来的链表和新创建的链表进行绑定,通过回溯遍历对应的链表,如果不存在对应链表就进行创建。
 - 通过一个map来维系 
  
- 如果没有找到,说明当前的节点没有遍历过,然后创建对应的指针。
 - 如果找到了,说明当前节点 遍历过,直接返回对应连接就行了。
 
 
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;
    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    Map<Node,Node> map = new HashMap<>();
    public Node copyRandomList(Node head) {
        if(head == null) return null;
        
        // recursive the list
        // 下述是没有遍历过对应的节点,然后创建对应的节点
       if(!map.containsKey(head)){
            Node newHead = new Node(head.val);
            map.put(head,newHead);
            newHead.next = copyRandomList(head.next);
            newHead.random = copyRandomList(head.random);
        }
        // 遍历过对应的节点,这里是直接返回
        return map.get(head);
       
        
    }
}
 

排序链表

 
 注意
- 时间复杂度要在nlogn,说明可以使用快排,然后在直接相连对对应的节点就行了。
 
个人实现
- java中collectionss.sort方法实现是通过timesort,时间复杂度就是O(nlogn),然后我其他两个操作的时间复杂度都是O(n),所以就是最优的时间复杂度!
 - 直接找到每一个节点,然后进行排序就行了。
 
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null)    return head;
        List<ListNode> list = new ArrayList<>();
        ListNode temp = head;
        while(temp != null) {
            list.add(temp);
            temp = temp.next;
        }
        Collections.sort(list,(ListNode a,ListNode b)->Integer.compare(a.val,b.val));
        for(int i = 0;i < list.size() - 1;i ++){
            list.get(i).next = list.get(i + 1);
        }
        list.get(list.size() - 1).next = null;;
        return list.get(0);
    }
}
 

不好意思,我没看到还是需要常数级别的时间复杂度,这里没注意到,有问题!
补充
- 指定sort排序方式的lambda表达式
 - 这段内容是没写出来,是我问了GPT才知道的,不行呀,如果下次笔试或者手撕这样就尴尬了!
 
 Collections.sort(list,(ListNode a,ListNode b)->Integer.compare(a.val,b.val));
 
参考实现
- 这里使用了归并排序的自底向上版本,能够在O(1)的空间复杂度内,实现O(nlogn)的时间复杂度排序,代码有点复杂,要我写肯定写不出来,放弃了!
 - 但是大概的框架是基本上都知道的!
 
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
       int n = 0;
       for(ListNode temp = head;temp != null;temp = temp.next)  n++;
       // traverse the list
       ListNode dummy = new ListNode(-1);
       dummy.next = head;
       for(int i = 1;i < n;i *= 2){
            // 这里是每一次都是二分,所以每一次都往后两倍
            ListNode cur = dummy;
            for(int j = 0;j + i <= n;j += i*2){
                // 这里每一次都是排序0 到2*i的序列
                ListNode p = cur.next,q = p;
                // 向后遍历i次,到达第i个位置,也就是下一个序列开始的部分
                for(int k = 0;k < i;k ++) q = q.next;
                int x = 0;
                int y = 0;
                while(x < i && y < i && p!= null && q != null){
                    if(p.val <= q.val)  {
                        cur.next = p;
                        cur = cur.next;
                        p = p.next;
                        x ++;
                    }else{
                        cur.next = q;
                        cur = cur.next;
                        q = q.next;
                        y ++;
                    }
                }
                while(x < i && p != null)   {
                    cur.next = p;
                    cur = cur.next;
                    p = p.next;
                    x ++;
                }
                while(y < i && q != null){
                    cur.next = q;
                    cur = cur.next;
                    q = q.next;
                    y ++;
                }
                cur.next = q;
            } 
       }
       return dummy.next;
    }
}
 

二叉树章节
- 二叉树在拼多多主管面的时候,被狠狠的说了,说我没怎么做过二叉树的题目,然后就挂了,拼多多主管面只能说自己被别人问的一览无余,底裤都不剩了,确实能力不行!
 - 不过无所谓了,继续加油!
 
二叉树的中序遍历
- 题目连接
 


注意
- 节点的数量可能为0,所以需要处理为空的情况
 
个人实现
- 前序遍历:左中右,然后进行输出
 
递归实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    List<Integer> list;
    public void dfs(TreeNode temp){
        if(temp.left != null) dfs(temp.left);
        list.add(temp.val);
        if(temp.right != null) dfs(temp.right);
    }
    public List<Integer> inorderTraversal(TreeNode root) {
        list = new ArrayList<>();
        if(root == null)    return list;
        dfs(root);
        return list;
    }
}
 

 迭代实现
- 迭代实现,就是使用stack模拟函数调用的堆栈进行实现
 
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stk = new Stack<>();
        TreeNode current = root;
        while (current != null || !stk.isEmpty()) {
            // 走到最左边的节点
            while (current != null) {
                stk.push(current);
                current = current.left;
            }
            // 当前节点为空,弹出栈顶节点并访问它
            current = stk.pop();
            list.add(current.val);
            // 准备访问右子树
            current = current.right;
        }
        
        return list;
    }
}
 
这里没有理出来,这种迭代,实现起来,怪怪的,没整好
问题
-  
java中使用什么来模拟栈
 -  
push(x)将元素压入栈顶
 -  
pop()弹出栈顶元素
 -  
peek()查看栈顶元素,但是不移除
 -  
isEmpty()查看是否为空
 -  
java中队列的操作
 -  
add()元素插入队列,队列满就返回异常
 -  
offer()元素插入队列,队列满就返回false
 -  
remove()移除队头元素,如果队列为空,就抛出异常
 -  
poll移除并返回队列元素头部,如果队列为空,就返回null
 -  
peek返回队列元素,但是不删除
 
二叉树的最大深度
- 题目连接


注意 - 节点的数量可能为空,特殊处理
 - 二叉树
 
个人实现
- 树形结构,完全就是回溯的复刻,所以可以直接使用dfs进行实现 
  
- 高度就是迭代限制
 - 单次迭代的范围就是两个节点
 
 - 按照某种顺序遍历一次就行了
 
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int maxDept = 0;
    
    void dfs(TreeNode root,int idx){
        if(root == null)    {
            maxDept = Math.max(idx , maxDept);
            return;
        }
        dfs(root.left,idx + 1);
        dfs(root.right,idx + 1);
    }
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        dfs(root,0);
        return maxDept;
    }
}
 
参考实现
dfs
- 这个问题可以拆解,拆解成左右两个子树的最大值,然后加一,就是当前的子树的最高的高度了
 
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
  
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        else{
            int lDept = maxDepth(root.left);
            int rDept = maxDepth(root.right);
            return Math.max(lDept,rDept) + 1;
        }
    }
}
 

真是简洁!!发现这种问题拆解,始终做的不是很流畅,或者说优点奇怪!做不出来,多练习一下!
反转二叉树
- 题目连接
 

 
注意
- 队列可能为空,需要特殊处理
 
个人实现
- 这里就是交换两个子节点,中间可能需要要给临时的保存一下
 - 这也是一个问题嵌套的情况,如果要交换当前节点的左右子节点,可以交换左右子树的两个节点
 
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return null;
        else{
            TreeNode temp = invertTree(root.left);
            root.left = invertTree(root.right);
            root.right = temp;
            return root;
        }
        
    }
}
 

太舒服了,真的是爽题,一下子就出来了!
参考实现
不看了,方法基本上和我一样的,就不写了
总结
-  
放假回来第一天,题目成功刷完,直到现在几点了吗?十二点四十,我是真的努力呀!我都佩服自己了,今天又是六道题!
 -  
明天面试加油,能过就过!
 



















