博客记录-day143-力扣+数据结构,智商题

一、力扣

1、二叉树展开为链表

114. 二叉树展开为链表

将给定的二叉树展开为一个单链表,链表中的节点顺序与二叉树的前序遍历顺序相同。 展开后的链表中,每个节点的右指针指向链表中的下一个节点,左指针始终为null。

实现思路:

  • 如果当前节点为空,返回。
  • 递归右子树。
  • 递归左子树。
  • 把 root.left 置为空。
  • 头插法,把 root 插在 head 的前面,也就是 root.right=head。
  • 现在 root 是链表的头节点,把 head 更新为 root。

时间复杂度:O(n),每个节点被访问一次。

空间复杂度:O(h),递归栈的深度取决于树的高度,最坏情况为O(n)(当树退化为链表时)。

java 复制代码
class Solution {
    // 维护当前链表的头节点,用于连接后续处理的子树
    TreeNode head;

    public void flatten(TreeNode root) {
        if (root == null) return;

        // 后序遍历处理右子树
        flatten(root.right);
        // 后序遍历处理左子树
        flatten(root.left);

        // 将当前节点的右指针指向已展开的左子树链表(初始为null)
        root.right = head;
        // 左指针置空,符合链表结构要求
        root.left = null;
        // 更新head为当前节点,作为新的链表头(后续父节点将连接到此)
        head = root;
    }
}

2、员工的直属部门

1789. 员工的直属部门

sql 复制代码
select employee_id, department_id
from Employee
where primary_flag = 'Y' or employee_id in (
    select employee_id
    from Employee
    group by employee_id
    having count(employee_id) = 1
)

3、排序链表

148. 排序链表

java 复制代码
class Solution {
    public ListNode sortList(ListNode head) {
        if(head==null||head.next==null) return head;
        ListNode head2=middle(head);
        head=sortList(head);
        head2=sortList(head2);
        return merge(head,head2);
    }
    public ListNode middle(ListNode head){
        ListNode pre=head;
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            pre=slow;
            slow=slow.next;
        }
        pre.next=null;
        return slow;
    }
    public ListNode merge(ListNode list1, ListNode list2) {
        ListNode dummy = new ListNode(); // 用哨兵节点简化代码逻辑
        ListNode cur = dummy; // cur 指向新链表的末尾
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                cur.next = list1; // 把 list1 加到新链表中
                list1 = list1.next;
            } else { // 注:相等的情况加哪个节点都是可以的
                cur.next = list2; // 把 list2 加到新链表中
                list2 = list2.next;
            }
            cur = cur.next;
        }
        cur.next = list1 != null ? list1 : list2; // 拼接剩余链表
        return dummy.next;
    }
}

4、下一个排列

31. 下一个排列

java 复制代码
class Solution {
    public void nextPermutation(int[] nums) {
        int n = nums.length;
        // 第一步:从右向左找到第一个小于右侧相邻数字的数 nums[i]
        int i = n - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        // 如果找到了,进入第二步;否则跳过第二步,反转整个数组
        if (i >= 0) {
            // 第二步:从右向左找到 nums[i] 右边最小的大于 nums[i] 的数 nums[j]
            int j = n - 1;
            while (nums[j] <= nums[i]) {
                j--;
            }
            // 交换 nums[i] 和 nums[j]
            swap(nums, i, j);
        }
        // 第三步:反转 nums[i+1:](如果上面跳过第二步,此时 i = -1)
        reverse(nums, i + 1, n - 1);
    }
    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    private void reverse(int[] nums, int left, int right) {
        while (left < right) {
            swap(nums, left++, right--);
        }
    }
}

5、 x 的平方根

69. x 的平方根

java 复制代码
class Solution {
    public int mySqrt(int x) {
        // 初始化二分查找的左右边界:最小可能值0,最大可能值x
        int l = 0, r = x;
        // 存储最终结果,初始值为-1(当x=0时能正确返回)
        int ans = -1;

        // 二分查找核心逻辑
        while (l <= r) {
            // 计算中间值,使用(l + r)/2的变形避免整数溢出
            int mid = l + (r - l) / 2;

            // 关键点:将mid转换为long类型进行平方计算,防止int溢出
            // 如果mid² <= x,说明可能是候选解,尝试寻找更大的值
            if ((long) mid * mid <= x) {
                ans = mid;       // 更新当前最优解
                l = mid + 1;     // 左边界右移,搜索更大值
            } else {
                r = mid - 1;     // mid²过大,右边界左移
            }
        }
        return ans;
    }
}

6、比较版本号

165. 比较版本号

java 复制代码
class Solution {
    public int compareVersion(String version1, String version2) {
        String[] v1 = version1.split("\\.");
        String[] v2 = version2.split("\\.");
        for (int i = 0; i < v1.length || i < v2.length; ++i) {
            int x = 0, y = 0;
            if (i < v1.length) {
                x = Integer.parseInt(v1[i]);
            }
            if (i < v2.length) {
                y = Integer.parseInt(v2[i]);
            }
            if (x > y) {
                return 1;
            }
            if (x < y) {
                return -1;
            }
        }
        return 0;
    }
}

split方法通过正则表达式分割字符串,其行为由​​分隔符正则​ ​和​​limit参数​​共同决定:

  1. ​默认用法​ (无limit):尽可能分割,但​丢弃末尾空字符串​ (如"a.b.c.".split(".") → ["a","b","c"])。
  2. limit > 0 :最多分割limit-1次,后续部分保留,​末尾空字符串保留​ (如"a.b.c.".split(".", 2) → ["a", "b.c."])。
  3. limit == 0 :尽可能分割,但​末尾空字符串全部丢弃​(等效于默认行为)。
  4. limit < 0 (如-1):完全分割,​保留所有空字符串​ (如"a.b.c.".split(".", -1) → ["a","b","c",""])。

6.1 正则表达式总结

Java 字符串分解常用简单正则总结

在 Java 中,String.split() 结合正则表达式可快速分解字符串,以下是针对常见场景的简洁正则及用法:


1. 基础分隔符分割

场景 正则表达式 Java 写法 示例 说明
空格分割 \\s+ str.split("\\s+") "a b c"["a", "b", "c"] 匹配连续空格、制表符、换行等
逗号分割 , str.split(",") "a,b,,c"["a","b","","c"] 默认保留末尾空字符串
点号分割 \\. str.split("\\.") "192.168.1.1"["192","168"...] 点号需转义

3. 多分隔符混合分割

场景 正则表达式 Java 写法 示例 说明
逗号、分号、空格分割 [;,\\s]+ str.split("[;,\\s]+") "a;b, c"["a","b","c"] 匹配任意组合的分隔符
斜杠分割 \\/ str.split("\\/") "2023/10/05"["2023","10","05"] 斜杠需转义

4. 保留末尾空字符串

场景 正则表达式 Java 写法 示例 说明
强制保留所有空值 使用 split(regex, -1) str.split(",", -1) "a,,b,".split(",", -1)["a","","b",""] -1 保留末尾空字符串

关键注意事项

  1. 转义字符 :正则中的特殊字符(如 .|/)需用 \\ 转义(Java 字符串中写成 \\\\)。
  2. limit 参数
    split(regex) → 默认 limit=0(丢弃末尾空字符串)。
    split(regex, -1) → 保留所有空字符串。

示例代码

java 复制代码
String str = "a,,b;c d";
// 分割逗号或分号,忽略空格,保留空值
String[] parts = str.split("[;,\\s]+"); // ["a", "b", "c", "d"]
// 强制保留所有空值(包括末尾)
String[] parts2 = str.split("[;,\\s]+", -1); // ["a", "", "b", "c", "d", ""]

7、合并二叉树

617. 合并二叉树

java 复制代码
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1==null) return root2;
        if(root2==null) return root1;
        TreeNode res=new TreeNode(root1.val+root2.val);
        res.left=mergeTrees(root1.left,root2.left);
        res.right=mergeTrees(root1.right,root2.right);
        return res;
    }
}

8、二叉搜索树中的众数

501. 二叉搜索树中的众数

java 复制代码
class Solution {
    // 存储结果的列表
    List<Integer> answer = new ArrayList<Integer>();
    // 当前正在统计的节点值
    int base;
    // 当前值出现的次数
    int count;
    // 最大出现次数
    int maxCount;

    public int[] findMode(TreeNode root) {
        // 通过中序遍历处理所有节点
        dfs(root);
        // 将列表转换为数组返回
        int[] mode = new int[answer.size()];
        for (int i = 0; i < answer.size(); ++i) {
            mode[i] = answer.get(i);
        }
        return mode;
    }

    public void dfs(TreeNode o) {
        if (o == null) {
            return;
        }
        // 递归遍历左子树
        dfs(o.left);
        // 处理当前节点值
        update(o.val);
        // 递归遍历右子树
        dfs(o.right);
    }

    public void update(int x) {
        // 如果当前值与统计值相同,增加计数
        if (x == base) {
            ++count;
        } else {
            // 否则重置统计:当前值作为新基准,计数归1
            count = 1;
            base = x;
        }
        
        // 当当前计数等于最大计数时,加入结果列表(可能有多个众数)
        if (count == maxCount) {
            answer.add(base);
        } 
        // 当当前计数超过最大计数时,更新最大计数并重置结果列表
        else if (count > maxCount) {
            maxCount = count;
            answer.clear();
            answer.add(base);
        }
    }
}

9、x升杯子,y升杯子,量出z升水,找出z的规律

365. 水壶问题

  • 判断是否可以使用容量为x和y的两个水壶量出恰好z升的水。
  • 根据贝祖定理,当且仅当z是x和y的最大公约数的倍数,且z不超过x+y时,才可能实现。
java 复制代码
class Solution {
    public boolean canMeasureWater(int x, int y, int z) {
        // 总容量不足时直接返回false
        if (x + y < z) {
            return false;
        }
        // 处理其中一个水壶容量为0的特殊情况:
        // 1. 若z为0,则无需测量直接返回true
        // 2. 若另一个水壶容量正好等于z,则可以量出
        if (x == 0 || y == 0) {
            return z == 0 || x + y == z;
        }
        // 当z是x和y的最大公约数的倍数时,可以通过倒水操作量出z
        return z % gcd(x, y) == 0;
    }

    public int gcd(int x, int y) {
        if(x==0) return y;
        return gcd(y%x,x);
    }
}

二、语雀-数据结构

1、数组和链表有何区别?

✅数组和链表有何区别?

2、栈和队列的区别

✅栈和队列的区别

3、什么是树?了解哪些树结构?

✅什么是树?了解哪些树结构?

4、什么是前缀树,有什么作用?

✅什么是前缀树,有什么作用?

5、什么是堆?什么情况下要用堆?

✅什么是堆?什么情况下要用堆?

6、什么是B+树,和B树有什么区别?

✅什么是B+树,和B树有什么区别?

1. B树

2. B+树

7、什么是红黑树?

✅什么是红黑树?

8、什么是BitMap?有什么用?

✅什么是BitMap?有什么用?

9、什么是小顶堆,可以用在哪些场景?

✅什么是小顶堆,可以用在哪些场景?

10、海量数据查找最大的 k 个值,用什么数据结构?

✅海量数据查找最大的 k 个值,用什么数据结构?

三、语雀-智商题

1、村庄有个约定,生男孩就结束,生女孩就继续生,直到生出男孩为止,若干年后,这个村子男女比例是多少?

✅村庄有个约定,生男孩就结束,生女孩就继续生,直到生出男孩为止,若干年后,这个村子男女比例是多少?

2、假设你有一个乒乓球盒子,里面有 3 个白球和 2 个黑球。从盒子中抽取一个球,放回后再抽取一个球。两次抽取得到的球颜色不同的概率是多少?

✅假设你有一个乒乓球盒子,里面有 3 个白球和 2 个黑球。从盒子中抽取一个球,放回后再抽取一个球。两次抽取得到的球颜色不同的概率是多少?

3、有8个球,其中7个重量相同,另一个球比其他球重,现在只有一个天平,请问最少需要称几次一定能找到那个比其他球重的球?

✅有8个球,其中7个重量相同,另一个球比其他球重,现在只有一个天平,请问最少需要称几次一定能找到那个比其他球重的球?

一定是分成三份,称两份。

4、有两个水桶,容量分别为5升和3升,请问如何使用这两个桶得到4升的水?

✅有两个水桶,容量分别为5升和3升,请问如何使用这两个桶得到4升的水?

5、有一堆桃子,猴子第一天吃了一半加一个,第二天又吃了一半加一个,... ,到第10天时剩下一个桃子,问这原来有多少个?

✅有一堆桃子,猴子第一天吃了一半加一个,第二天又吃了一半加一个,... ,到第10天时剩下一个桃子,问这原来有多少个?

6、1000瓶药水,1瓶有毒药,最少需要几只小白鼠一定能够找出?

✅1000瓶药水,1瓶有毒药,最少需要几只小白鼠一定能够找出?

7、一个天平,7g和2g砝码各一个,将140g盐分成90g和50g,需要称多少次?

✅一个天平,7g和2g砝码各一个,将140g盐分成90g和50g,需要称多少次?

相关推荐
uhakadotcom11 小时前
Amazon GameLift 入门指南:六大核心组件详解与实用示例
后端·面试·github
uhakadotcom13 小时前
一文读懂DSP(需求方平台):程序化广告投放的核心基础与实战案例
后端·面试·github
uhakadotcom13 小时前
拟牛顿算法入门:用简单方法快速找到函数最优解
算法·面试·github
qianmoQ14 小时前
GitHub 趋势日报 (2025年04月13日)
github
Danny_FD14 小时前
常用 Git 命令详解
前端·github
OpenTiny社区16 小时前
TinyPro 1.2.0 正式发布:增加综合搜索,解决数据筛选难题,后端单测覆盖率再提升!
前端·vue.js·github
uhakadotcom17 小时前
使用NLTK和jieba进行中文情感分析的简单教程
算法·面试·github
苏琢玉17 小时前
用 WakaTime + GitHub Actions 打造属于你的个性化 GitHub 主页
github
零零壹1119 小时前
使用 Node.js、Express 和 React 构建强大的 API
javascript·后端·github