Leedcode算法题

算法题

题目来源于LeedCode热题100,取其中个人觉得较难的部分。

双指针

接雨水

正向遍历+反向遍历+取最小值

java 复制代码
public int trap(int[] height) {
        int left=0;
        int right=height.length-1;
        int water=0;
        int leftMax = 0, rightMax = 0;
       
        while(left<right){
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            
            if(height[left]<height[right]){
                water += leftMax - height[left];
                left++;
            }
            else{
                water += rightMax - height[right];
                right--;
            }       
        }
        return water;
    }

数组

最大子数组和

迭代计算以当前元素结尾的子数组的最大和(动态规划可秒)

java 复制代码
public int maxSubArray(int[] nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            pre = Math.max(pre + x, x);//以当前元素结尾的子数组的最大和
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }

缺失的第一个正数

1.所有负数变为数组长度+1,排除在外

2.将元素<数组长度的对应位置变为负数

3.返回第一个大于0的数组下标

java 复制代码
public int firstMissingPositive(int[] nums) {
        int n = nums.length;
        for (int i = 0; i < n; ++i) {//负数变正数
            if (nums[i] <= 0) {
                nums[i] = n + 1;
            }
        }
        for (int i = 0; i < n; ++i) {
            int num = Math.abs(nums[i]);
            if (num <= n) {
                nums[num - 1] = -Math.abs(nums[num - 1]);//对应位置变负数
            }
        }
        for (int i = 0; i < n; ++i) {
            if (nums[i] > 0) {
                return i + 1;
            }
        }
        return n + 1;
    }

矩阵

螺旋遍历矩阵

用矩阵存储走过的区域,二维数组存储方向

java 复制代码
public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> order = new ArrayList<Integer>();
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return order;
        }
        int rows = matrix.length, columns = matrix[0].length;
        boolean[][] visited=new boolean[rows][columns];
        int total = rows * columns;
        int row = 0, column = 0;
        int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        int directionIndex = 0;
        for (int i = 0; i < total; i++) {
            order.add(matrix[row][column]);
            visited[row][column] = true;
            int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
            if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
                directionIndex = (directionIndex + 1) % 4;
            }
            row += directions[directionIndex][0];
            column += directions[directionIndex][1];
        }
        return order;
    }

链表

K个一组翻转链表

迭代的进行翻转,每次翻转K个

java 复制代码
public ListNode reverseKGroup(ListNode head, int k) {
        ListNode hair = new ListNode(0);
        hair.next = head;
        ListNode pre = hair;
        while (head != null) {
            ListNode tail = pre;
            // 查看剩余部分长度是否大于等于 k
            for (int i = 0; i < k; ++i) {
                tail = tail.next;
                if (tail == null) {
                    return hair.next;
                }
            }
            ListNode nex = tail.next;
            ListNode[] reverse = myReverse(head, tail);
            head = reverse[0];
            tail = reverse[1];
            // 把子链表重新接回原链表
            pre.next = head;
            tail.next = nex;
            pre = tail;
            head = tail.next;
        }
        return hair.next;
    }
    public ListNode[] myReverse(ListNode head, ListNode tail) {
        ListNode prev = tail.next;
        ListNode p = head;
        while (prev != tail) {
            ListNode nex = p.next;
            p.next = prev;
            prev = p;
            p = nex;
        }
        return new ListNode[]{tail, head};
    }

深拷贝

递归拷贝next节点和random节点,并用哈希表保证不拷贝重复节点

java 复制代码
Map<Node, Node> cachedNode = new HashMap<Node, Node>();
    public Node copyRandomList(Node head) {
        if (head==null){
            return null;
        }
        if (!cachedNode.containsKey(head)) {
            Node headNew = new Node(head.val);
            cachedNode.put(head, headNew);
            headNew.next = copyRandomList(head.next);
            headNew.random = copyRandomList(head.random);
        }
        return cachedNode.get(head);
    }

归并排序链表

1.找到链表的中点,以中点为分界,将链表拆分成两个子链表(快慢指针)

2.对两个子链表分别排序

3.将两个排序后的子链表合并,得到完整的排序后的链表

4.进行递归,递归的终止条件是链表的节点个数小于或等于 1

java 复制代码
public ListNode sortList(ListNode head) {
        return sortList(head, null);
    }
    public ListNode sortList(ListNode head, ListNode tail){
        if (head == null) {
            return head;
        }
        if (head.next == tail) {
            head.next = null;
            return head;
        }
        ListNode slow=head,fast=head;
        while(fast!=tail){
            slow=slow.next;
            fast=fast.next;
            if(fast!=tail){
                fast=fast.next;
            }
        }//慢指针在中心
        ListNode mid=slow;
        ListNode list1 = sortList(head, mid);
        ListNode list2 = sortList(mid, tail);
        ListNode sorted = merge(list1, list2);
        return sorted;
    } 
    //合并两个有序链表见21
    public ListNode merge(ListNode list1, ListNode list2){
        ListNode sum;
        ListNode head;
        if(list1==null){
            return list2;
        }
        if(list2==null){
            return list1;
        }
        if(list1.val>list2.val){
            sum=list2;
            head=list2;
            list2=list2.next;
        }
        else{
            sum=list1;
            head=list1;
            list1=list1.next;
        }
        while(list1!=null&&list2!=null){
            if(list1.val>list2.val){
                sum.next=list2;
                list2=list2.next;
                sum=sum.next;
            }
            else{
                sum.next=list1;
                list1=list1.next;
                sum=sum.next;
            }
        }
        if(list1==null&&list2!=null){
            sum.next=list2;
        }
        if(list2==null&&list1!=null){
            sum.next=list1;
        }
        return head;
    }

归并、快速排序数组

LRU缓存

双向链表存取节点数据,哈希表快速查找指定元素

java 复制代码
class DLinkedNode {
        int key;//方便哈希表寻找
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;//虚拟节点,不存储数据

    public LRUCache(int capacity) {//初始化存储容量
        this.size = 0;
        this.capacity = capacity;
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        // 如果 key 存在,先通过哈希表定位,再移到头部
        moveToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果 key 不存在,创建一个新的节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                // 删除哈希表中对应的项
                cache.remove(tail.key);
                --size;
            }
        }
        else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            node.value = value;
            moveToHead(node);
        }
    }
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }

二叉树

层序遍历

利用队列先进先出取根节点存子节点

java 复制代码
public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ret = new ArrayList<List<Integer>>();
        if (root == null) {
            return ret;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            List<Integer> level = new ArrayList<Integer>();
            int currentLevelSize = queue.size();
            for (int i = 1; i <= currentLevelSize; ++i) {
                TreeNode node = queue.poll();
                level.add(node.val);
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            ret.add(level);
        }
        return ret;
    }

前序+中序遍历构造二叉树

拆分为左子树和右子树再进行递归

java 复制代码
public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder==null||inorder==null||preorder.length == 0||inorder.length == 0){
            return null;
        }
        if(preorder.length==1){
            return new TreeNode(preorder[0]);
        }
        TreeNode root=new TreeNode(preorder[0]);
        int index = -1;
        for(int i=0;i<preorder.length;i++){
            if(inorder[i]==preorder[0]){
                index=i;
                break;
            }
        }
        
        int[] leftIno = new int[index]; // 创建新数组
        int[] rightIno=new int[preorder.length-index-1];  
        int[] leftPre=new int[index];
        int[] rightPre=new int[preorder.length-index-1];          
        System.arraycopy(inorder, 0, leftIno, 0, index);
        System.arraycopy(inorder, index+1, rightIno, 0, preorder.length-index-1);
        System.arraycopy(preorder, 1, leftPre, 0, index);
        System.arraycopy(preorder, index+1, rightPre, 0, preorder.length-index-1);
                
        root.left=buildTree(leftPre,leftIno);
        root.right=buildTree(rightPre,rightIno);
        
        return root;
    }

图论

课程表

这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

用二维数组存储邻接表,再按深度优先遍历节点,每种节点有3种状态。

java 复制代码
class Solution {
    List<List<Integer>> edges;
    int[] visited;
    boolean valid = true;

    public boolean canFinish(int numCourses, int[][] prerequisites) {
    	//初始化
        edges = new ArrayList<List<Integer>>();
        for (int i = 0; i < numCourses; ++i) {
            edges.add(new ArrayList<Integer>());
        }
        visited = new int[numCourses];
        for (int[] info : prerequisites) {
        	//edges.get(0) 返回一个列表,表示从节点 0 出发的所有邻接节点
            //存储所有边信息
            //将目标节点 info[0] 添加到源节点 info[1] 的邻接表中
            edges.get(info[1]).add(info[0]);
        }
        for (int i = 0; i < numCourses && valid; ++i) {
            if (visited[i] == 0) {
                dfs(i);
            }
        }
        return valid;
    }
	//visited[u]=0未开始;=1进行中;=2已结束。如果找到进行中的节点,说明有环
    public void dfs(int u) {
        visited[u] = 1;
        for (int v: edges.get(u)) {
            if (visited[v] == 0) {
                dfs(v);
                if (!valid) {
                    return;
                }
            } else if (visited[v] == 1) {
                valid = false;
                return;
            }
        }
        visited[u] = 2;
    }
}

前缀数Trie(字典树)

树形结构,每个父节点有26个子节点,用于快速查找某个字符串是否存在

java 复制代码
class Trie {
    private Trie[] children;
    private boolean isEnd;

    public Trie() {
        children = new Trie[26];
        isEnd = false;
    }
    //插入单词
    public void insert(String word) {
        Trie node = this;
        for (int i = 0; i < word.length(); i++) {
            char ch = word.charAt(i);
            int index = ch - 'a';
            if (node.children[index] == null) {
                node.children[index] = new Trie();
            }
            node = node.children[index];
        }
        node.isEnd = true;
    }
    //搜索单词
    public boolean search(String word) {
        Trie node = searchPrefix(word);
        return node != null && node.isEnd;
    }
    //搜索单词前缀
    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;
    }

    private Trie searchPrefix(String prefix) {
        Trie node = this;
        for (int i = 0; i < prefix.length(); i++) {
            char ch = prefix.charAt(i);
            int index = ch - 'a';
            if (node.children[index] == null) {
                return null;
            }
            node = node.children[index];
        }
        return node;
    }
}

单调栈使用场景:在一维数组中找第一个满足某种条件(更大/更小)的数

单调栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈

使用双栈二维数组分别存储元素的值和该元素及里面其它元素的最小值

柱状图中最大的矩形(单调栈应用)

目标是找到左右两侧最近的高度小于 h 的柱子,维护一个存储下标的栈,栈中存放的下标对应的值单调递增,若遍历到的数小于栈顶的值,则将小于的值全部移除。

核心是上浮和下沉

前K个高频元素

堆与Map与自定义比较器的结合

回溯

全排列

给定一个不含重复数字的数组 nums ,返回其所有可能的全排列。

核心在于每次选一个未选过的数放入output数组,数组满了则存储结果

可以用n分割当前未放入的数与已经放入的数

时间复杂度为O(n*n!)

分割回文串

将字符串 s 分割成一些子串,使每个子串都是回文串 ,返回 s 所有可能的分割方案

动态规划预处理所有是回文串的子串,再回溯

二分算法

主要在判断边界条件的地方比较麻烦

搜索旋转排序数组

nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了旋转,例如 [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

仍然能进行二分查找

cpp 复制代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = (int)nums.size();
        if (!n) {
            return -1;
        }
        if (n == 1) {
            return nums[0] == target ? 0 : -1;
        }
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            if (nums[0] <= nums[mid]) {
                if (nums[0] <= target && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
};

寻找两个有序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2,找出并返回这两个正序数组的中位数

等效于找两个数组的第 k=(n+m)/2 个数

可以比较 num1[k/2−1] 和 num2[k/2−1],如果num1[k/2-1]较小,则可以排除 num1[0] 到 num1[k/2−1]的数,然后更新k,继续进行比较直到找到目标值。

时间复杂度O(log(n+m))

cpp 复制代码
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int l=nums1.size();int r=nums2.size();
        int count=l+r;
        if(count%2==1){
            return getKthElement(nums1,nums2,(count+1)/2);
        }else{
            return (getKthElement(nums1,nums2,count/2)+getKthElement(nums1, nums2,count/2+1))/2.0;
        }       
        
    }
    int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
        int m = nums1.size();
        int n = nums2.size();
        int index1 = 0, index2 = 0;
        while(true){
            if(index1==m){
                return nums2[index2+k-1];
            }
            if(index2==n){
                return nums1[index1+k-1];
            }
            if(k==1){
                return min(nums1[index1],nums2[index2]);
            }
            int newIndex1 = min(index1 + k / 2 - 1, m - 1);
            int newIndex2 = min(index2 + k / 2 - 1, n - 1);
            int pivot1 = nums1[newIndex1];
            int pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
            else {
                k -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }
        }
    }
};

贪心算法

跳跃游戏

每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。如果在 nums[i] 处,可以跳转到任意 nums[i + j] 处,返回到达 nums[n - 1] 的最小跳跃次数。

java 复制代码
class Solution {
    public int jump(int[] nums) {
        int position = nums.length - 1;
        int steps = 0;
        while (position > 0) {
            for (int i = 0; i < position; i++) {
                if (i + nums[i] >= position) {
                    position = i;
                    steps++;
                    break;
                }
            }
        }
        return steps;
    }
}

动态规划

最长有效括号(时间复杂度O(n))

如果找到每个可能的子串后判断它的有效性,时间复杂度会达到 O ( n 3 ) \ O(n^3) O(n3)

用动态规划就只有 O ( n ) \ O(n) O(n)

java 复制代码
class Solution {
    public int longestValidParentheses(String s) {
        int maxans = 0;
        int[] dp = new int[s.length()];
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == ')') {
            	//情况1: s[i]=')' 且 s[i−1]='('
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                } 
                //情况2: s[i]=')' 且 s[i−1]=')',需要继续判断 s[i−dp[i−1]−1]='('
                else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                maxans = Math.max(maxans, dp[i]);
            }
        }
        return maxans;
    }
}

单词拆分(别用回溯太慢了)

一个只包含正整数的非空数组 nums ,判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等

dp[i][j] 表示从数组的 [0,i] 下标范围内选取若干个正整数(可以是 0 个),是否存在一种选取方案使得被选取的正整数的和等于 j

cpp 复制代码
class Solution {
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        if (n < 2) {
            return false;
        }
        int sum = 0, maxNum = 0;
        for (int num : nums) {
            sum += num;
            maxNum = Math.max(maxNum, num);
        }
        if (sum % 2 != 0) {
            return false;
        }
        int target = sum / 2;
        if (maxNum > target) {
            return false;
        }
        boolean[][] dp = new boolean[n][target + 1];
        for (int i = 0; i < n; i++) {
            dp[i][0] = true;
        }
        dp[0][nums[0]] = true;
        for (int i = 1; i < n; i++) {
            int num = nums[i];
            for (int j = 1; j <= target; j++) {
                if (j >= num) {
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[n - 1][target];
    }
}

编辑距离

两个单词 word1 和 word2, 返回将 word1 转换成 word2 所使用的最少操作数

可以对一个单词进行三种操作:插入一个字符;删除一个字符;替换一个字符

用 dp[i][j] 表示 A 的前 i 个字母和 B 的前 j 个字母之间的编辑距离

java 复制代码
public int minDistance(String word1, String word2) {
        int c1=word1.length();
        int c2=word2.length();
        int[][] dp=new int[c1+1][c2+1];

        for(int i=0;i<=c1;++i){
            dp[i][0]=i;
        }
        for(int j=0;j<=c2;++j){
            dp[0][j]=j;
        }
        for(int i=1;i<=c1;++i){
            for(int j=1;j<=c2;++j){
            	//取1.在a中插入一个单词 2.在b中插入一个单词 3.在a中替换一个单词 操作中的最小值
                if(word1.charAt(i-1)!=word2.charAt(j-1)){                 
                    dp[i][j]=1+Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1]);
                }else{
                    dp[i][j]=1+Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1]-1);
                }               
            }
        }
        return dp[c1][c2];
    }

最长回文子串

可用一维/二维动态规划做,这里是另一种方法

枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止

java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    public int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return right - left - 1;
    }
}

技巧

找唯一一个只出现一次的数

全部的数进行异或运算,相同的数异或后为0抵消,最后留下唯一只出现一次的数

java 复制代码
for (int num : nums) {
     single ^= num;
 }

找出现次数大于一半的数

如果一个数组有大于一半的数相同,那么任意删去两个不同的数字,新数组还是会有相同的性质

java 复制代码
int winner = nums[0];
 int count = 1;
 for (int i = 1; i < nums.length; i++) {
     if (winner == nums[i]) {
         count++;
     } else if (count == 0) {
         winner = nums[i];
         count++;
     } else {
         count--;
     }
 }
 return winner;

寻找重复数

数字都在 [1, n] 范围内,重复数一定在数值和索引之间成环且在环的入口,然后通过快慢指针找到重复数

java 复制代码
public int findDuplicate(int[] nums) {
        int slow = 0, fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while (slow != fast);
        //找到环入口
        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }

快速求x的y次方

常规时间复杂度是O(y),使用快速幂算法的时间复杂度为O(logy),关键在于将y转换为二进制数组再进行计算

例:x=5, y=13, 即y=1101,遍历每一位,计算当前底数x *= x,并判断指数,为1则结果乘上当前底数,为0则不用乘

java 复制代码
public static double power(double x, int y) {
        double result = 1.0; // 初始化结果为1
        long exp = Math.abs((long)y); // 将指数y转为正数(避免负数问题)

        while (exp > 0) {
            // 如果当前指数是奇数,将当前底数乘入结果
            if ((exp & 1) == 1) {
                result *= x;
            }
            // 平方当前底数
            x *= x;
            // 指数右移(等价于除以2)
            exp >>= 1;
        }

        // 如果原指数为负数,返回1/result
        return y < 0 ? 1 / result : result;
    }
相关推荐
流年如夢1 小时前
栈和列队(LeetCode)
数据结构·算法·leetcode·链表·职场和发展
苍煜1 小时前
Java开发IO零基础吃透:BIO、NIO、同步异步、阻塞非阻塞
java·python·nio
折哥的程序人生 · 物流技术专研2 小时前
Java面试85题图解版(一):基础核心篇
java·开发语言·后端·面试
AllData公司负责人2 小时前
通过Postgresql同步到Doris,全视角演示AllData数据中台核心功能效果,涵盖:数据入湖仓,数据同步,数据处理,数据服务,BI可视化驾驶舱
java·大数据·数据库·数据仓库·人工智能·python·postgresql
Hello.Reader2 小时前
算法基础(十)——分治思想把大问题拆成小问题
java·开发语言·算法
一只大袋鼠2 小时前
JavaWeb四种文件上传方式(下篇)
java·开发语言·springmvc·javaweb
TE-茶叶蛋3 小时前
深入研究 yudao-framework 模块:Java 编程能力提升指南
java·开发语言
逻辑驱动的ken3 小时前
Java高频考点场景题24
java·开发语言·面试·职场和发展·求职招聘
绛橘色的日落(。・∀・)ノ3 小时前
机器学习之评估与偏差方差分析
算法