news 2026/10/10 3:23:46

用Kotlin刷力扣基础算法:从Java迁移到实战模板

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Kotlin刷力扣基础算法:从Java迁移到实战模板

如果你打开力扣的题解区,满眼是Java、Python、C++,Kotlin几乎属于稀有物种。我一开始也是用Java刷力扣基础算法的,刷到差不多两百题的时候换成了Kotlin,换完才发现这个选择真的回不去了。同样一个思路,用Kotlin写的代码通常能比Java少掉三分之一的样板;跑大型测试用例,性能也没有让我等得心慌。这篇文章主要分享我用Kotlin刷力扣基础算法以来最实在的实战经验:热题100里到底翻来覆去考哪些基础算法、Kotlin有哪些其他语言没有的惯用法可以直接拿来解题、评测环境下有哪些你搜半天也找不到答案的细节坑,以及我自己沉淀下来的刷题模板和本地开发流程。无论你是已经熟悉Java、Golang、Python,还是刚学完Kotlin想找点题练手,看完这篇应该都能直接上手。

1. 从“用Java刷腻了”到“用Kotlin真香”:我的选型过程

1.1 力扣选择Kotlin的人为什么不多

说句大实话,力扣支持Kotlin提交很多年了,但用的人一直不多。原因很简单:大部分刷题的人是学生或者准备校招,主力语言要么是C++、Python,要么是学校课上教的Java;Kotlin在国内更多出现在Android开发岗位的简历里,和算法面试关系不大。

可我自己的体验是:刷力扣基础算法这件事,语言选对了,效率能高很多。Kotlin和Java是同一个JVM生态,几乎零迁移成本;它有Java没有的空安全、类型推断、表达式化流程控制,还有一套非常顺手的标准库函数。对已经在JVM系语言里泡过的人来说,切到Kotlin刷题,不是“换一门新语言重新学”,而是“把以前写过的冗余代码删掉”。

1.2 我和Java对比后的真实感受

举一个最简单的例子,两数之和。Java版往往长这样:

class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[]{map.get(complement), i}; } map.put(nums[i], i); } return new int[0]; } }

Kotlin版我最后落地的是这样:

class Solution { fun twoSum(nums: IntArray, target: Int): IntArray { val seen = mutableMapOf<Int, Int>() for (i in nums.indices) { seen[target - nums[i]]?.let { return intArrayOf(it, i) } seen[nums[i]] = i } return intArrayOf() } }

两段代码逻辑完全一样,但Kotlin少写了不少东西:HashMap<Int, Int>()变成了mutableMapOf(),循环索引不用从0开始手动维护,map.containsKey加map.get两步判断合并成一句seen[...]?.let。这种简化在单题里看不出来,当你连续刷几十道题,累积节省的时间非常可观。

1.3 标准库让解题代码变短的那些瞬间

Kotlin最让我上头的不是语法本身,而是标准库。比如字母异位词分组这一题,Java要先把字符数组排序,再转回字符串作为key;Kotlin里groupBy一问,加上一段排序逻辑,就完成了分组。

fun groupAnagrams(strs: Array<String>): List<List<String>> { return strs.groupBy { it.toCharArray().sorted().joinToString("") }.values.toList() }

这种写法爽归爽,但我必须提醒一句:链式调用会创建中间集合,在数据量特别大的时候有额外开销。刷基础算法阶段,前两百题完全可以用;等遇到卡性能的题再改成循环也不迟。

Java和Kotlin的常见等价写法,我整理成了一张表:

场景Java 写法Kotlin 写法
定义不可变变量final int x = 5;val x = 5
遍历数组for (int i = 0; i < arr.length; i++)for (i in arr.indices)
创建哈希表new HashMap<>()mutableMapOf()
判空后调用方法if (obj != null) obj.method();obj?.method()
按规则分组手写循环维护MapgroupBy { ... }
元素计数手写循环containsKeygroupingBy { it }.eachCount()

这张表里的每一项,直接对应刷题时的常见写法差异。看得懂这张表,Kotlin刷题基本不会卡在语法上。

2. 力扣“必刷基础算法”到底刷什么:热题100的真实画像

2.1 热题100里反复出现的基础算法类型

我刷力扣热题100最大的感受是:这些题并不是随机分布的,背后有非常清晰的算法板块。把它们按类型切开,大概是这样:

算法类型代表题目核心考察点
数组与哈希两数之和、字母异位词分组空间换时间,哈希表的覆盖范围
双指针与滑动窗口移动零、盛最多水的容器、无重复字符的最长子串区间状态维护
链表反转链表、合并两个有序链表、回文链表指针操作和哨兵节点
二叉树二叉树中序遍历、最大深度、翻转二叉树递归结构与遍历框架
图与DFS/BFS岛屿数量、腐烂的橘子连通性和层次扩散
回溯全排列、子集、组合总和选择与撤销选择
动态规划爬楼梯、打家劫舍、最大子数组和状态定义与转移方程
数学技巧整数反转、回文数、x的平方根边界处理和位运算

不要小看这个分类。刷题最怕的是“今天看一道矩阵题,明天做一道链表题”,大脑完全串联不起来。按板块刷,每一类前两三道题打底,后面遇到同类的直接套框架,效率会高很多。

2.2 先刷哪几类最划算

我的推荐顺序是:数组哈希 -> 双指针 -> 链表 -> 二叉树 -> DFS/BFS -> 回溯 -> 动态规划 -> 图论。

前四类是基础中的基础,也是热题100里占比最重的部分。尤其是数组哈希和双指针,几乎每场面试都可能出其中一道。而且这批题难度整体温和,用新语言上手特别合适:语言不熟的时候,题目本身不该成为额外障碍。

动态规划和回溯建议放在后面,不是因为它们重要程度低,而是因为它们需要前面的编码基础。如果你连二叉树递归都还没写顺,直接做爬楼梯以外的DP题,很容易被状态转移绕晕。

2.3 怎么判断一道题算不算“基础算法”

很多人有个误解,觉得基础算法 = 简单的题。我在力扣上见过很多难度标记为“简单”但设计得很偏的题,也见过标记为“中等”但真的是纯基础套路的题。我的判断标准有三条:

  • 解法是否依赖某种通用算法框架,比如双指针、二分、递归、DP、回溯;
  • 核心数据结构是否是数组、链表、栈、队列、哈希表、树这些基本功;
  • 这个思路是否能迁移到至少两到三道其他题上。

符合这三条的,不管标注难度是简单还是中等,都属于“必刷基础算法”。热题100里的大多数题都符合,这也是它为什么值得反复刷。

3. 用Kotlin重写经典题:从暴力到最优的六道代码实战

3.1 两数之和:暴力循环到哈希的一步之遥

这道题我见过太多人第一反应是双重循环,我也一样。但刷题经验告诉我,看到“给一个数组,找两个数满足某个条件”,第一时间就应该想哈希表。暴力是O(n^2),哈希是O(n),在力扣上差别不是一点点。

class Solution { fun twoSum(nums: IntArray, target: Int): IntArray { val seen = mutableMapOf<Int, Int>() for (i in nums.indices) { seen[target - nums[i]]?.let { return intArrayOf(it, i) } seen[nums[i]] = i } return intArrayOf() } }

这里最值得记住的写法是seen[target - nums[i]]?.let { ... }。seen[key]返回的是Int?,如果key不存在就是null,?.let只在不为null时执行,天然替代了if (map.containsKey(key))的套路。不用感叹号,也不用两步判断,一行搞定。

有个小细节:我们必须在确认答案存在之后再把当前值写进map,否则可能出现同一个下标被用两次的情况。顺序写反是最常见的提交错误。

3.2 无重复字符的最长子串:滑动窗口加Map的经典套路

这题我刷了三遍才彻底吃透。最简单的暴力思路是枚举每个子串去重判断,但滑动窗口才是这一类题的标准解。

class Solution { fun lengthOfLongestSubstring(s: String): Int { var left = 0 var ans = 0 val pos = mutableMapOf<Char, Int>() s.forEachIndexed { right, c -> pos[c]?.let { p -> if (p >= left) left = p + 1 } pos[c] = right if (right - left + 1 > ans) ans = right - left + 1 } return ans } }

pos记录每个字符最近一次出现的位置。遇到重复字符时,如果它出现在当前窗口内,就把窗口左边界推进到它的下一位;如果它出现在窗口外,说明不影响当前窗口,不需要移动。每次右指针移动后,窗口长度right - left + 1可能更新答案。

很多人在这一步会犯错:pos[c]返回的位置可能已经小于left了,这时直接left = p + 1会把左边界拉回去,导致结果错误。所以必须先判断p >= left。这个边界条件就是这道题和信息不完全的“背答案”之间的差距。

3.3 合并两个有序链表:递归与可空类型

链表题在Kotlin里最突出的体验是空安全。力扣给出的ListNode定义中,val是关键字,所以属性名是val,写的时候必须加反引号。另一个特点是next和参数都可能是null,这意味着你写代码时被迫思考边界,反而比Java省心。

class Solution { fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? { return when { list1 == null -> list2 list2 == null -> list1 list1.`val` < list2.`val` -> { list1.next = mergeTwoLists(list1.next, list2) list1 } else -> { list2.next = mergeTwoLists(list1, list2.next) list2 } } } }

这段代码里,when被当作表达式使用,每个分支末尾的值就是返回值。递归终止条件是某个链表为空,直接返回另一个链表;否则比较两个头节点值,把较小节点的next指向剩余部分的合并结果。

我第一次写这个解法时,总觉得递归会创造出很长的调用链,后来实测下来链表题递归深度一般不会超过节点数,在力扣的默认栈深度下完全没问题。如果不想用递归,迭代加哨兵节点的写法也值得练,尤其是cur.next = a ?: b这句,把“处理剩余链表”压缩成了一行。

3.4 二叉树中序遍历:递归必须会,迭代也要懂

二叉树是Kotlin空安全最受益的领域。力扣的TreeNode定义中,左右孩子都是TreeNode?,递归遍历时再也不用写一堆判空。

class Solution { fun inorderTraversal(root: TreeNode?): List<Int> { val ans = mutableListOf<Int>() root ?: return ans ans.addAll(inorderTraversal(root.left)) ans.add(root.`val`) ans.addAll(inorderTraversal(root.right)) return ans } }

root ?: return ans这行是我最喜欢的Kotlin写法。它不是简单的判空,而是把“如果节点为null,直接返回当前list”这个意图表达得很清晰。递归版本背下来只需要一个顺序:左、根、右。前序只是把root.val放到最前,后序放到最后。

但如果面试官继续问“你的递归会不会栈溢出”,你就需要迭代版本。用显式栈模拟,核心逻辑是:一直往左走到底,沿途入栈;到底后弹出栈顶访问,再转入右子树。

class Solution { fun inorderTraversal(root: TreeNode?): List<Int> { val ans = mutableListOf<Int>() val stack = ArrayDeque<TreeNode>() var cur = root while (cur != null || stack.isNotEmpty()) { while (cur != null) { stack.addLast(cur) cur = cur.left } cur = stack.removeLast() ans.add(cur.`val`) cur = cur.right } return ans } }

本地IDE里写ArrayDeque时如果编辑器报错,记得import java.util.ArrayDeque。力扣网页编辑器一般会给出提示,但本地不自动导入。

3.5 爬楼梯:最朴素的动态规划“肌肉记忆”

爬楼梯是最适合做DP入门的一道题,因为状态转移太清晰了:到第n阶只能从第n-1阶跨一步,或者从第n-2阶跨两步。所以f(n) = f(n-1) + f(n-2),本质就是斐波那契。

class Solution { fun climbStairs(n: Int): Int { if (n <= 2) return n var a = 1 var b = 2 for (i in 3..n) { val c = a + b a = b b = c } return b } }

很多DP教程会先建一个IntArray(n+1)来存所有状态,但对这种只需要前两项的题,滚动变量就够了。a代表f(i-2),b代表f(i-1),每轮计算新的c = a + b,然后整体前移。这样做省内存,也顺便训练了“空间优化”的直觉。

顺便说,打家劫舍那题也是这个套路,只不过状态转移变成了f(i) = max(f(i-1), f(i-2) + nums[i])。把爬楼梯吃透,再去对照打家劫舍,你会发现DP没有想象中那么玄。

3.6 全排列:回溯算法在Kotlin里的干净写法

回溯题是Kotlin局部函数最好的秀场。你可以直接在fun里面再定义fun dfs(),变量自动共享,不需要额外传参传一堆。

class Solution { fun permute(nums: IntArray): List<List<Int>> { val ans = mutableListOf<List<Int>>() val path = mutableListOf<Int>() val used = BooleanArray(nums.size) fun dfs() { if (path.size == nums.size) { ans.add(path.toList()) return } for (i in nums.indices) { if (used[i]) continue used[i] = true path.add(nums[i]) dfs() path.removeAt(path.lastIndex) used[i] = false } } dfs() return ans } }

最核心的坑是ans.add(path.toList())。如果你直接ans.add(path),后面任何一次path.removeAt都会影响已经放进答案里的那个列表,因为它们是同一个对象。toList()相当于拍了一张快照,把当前路径复制一份存进去。

used数组负责标记哪些元素已经用过,这就是“选择-递归-撤销选择”的回溯骨架。没被选中的分支会走used[i] = false,这些代码看着重复,但删除任何一行都会让结果错得离谱。稍后我会在模板章节再展开。

4. 千万别踩的坑:力扣Kotlin环境下的性能与写法注意事项

4.1 提交物该写什么:只有类和方法,没有main

这是新手入坑最容易踩的编译错误。力扣的代码编辑区只需要你填写Solution类里的方法实现,不要写fun main(),也不要写println打印输入。本地跑需要main,提交时要删掉。

我见过不少人在本地IDE里写完整程序跑通了,然后把包含fun main的整个文件粘到力扣,编译直接报错。解法很简单:提交前检查文件末尾,确认没有fun main,确认所有测试代码都被注释掉。

4.2 高阶函数有代价:什么时候该换回for循环

Kotlin的高阶函数写起来很爽,但它本质上是lambda表达式加中间集合。比如nums.filter { ... }.map { ... },第一次filter生成一个中间list,第二次map再生成一个list。数据量小,这是优雅;数据量到了十万级,这就是负担。

热题100里的基础算法题,大部分数据规模在几百到几万之间,链式调用几乎都能过。但如果你在跑最大子数组和、最长递增子序列这类题时发现超时,优先检查自己的链式调用是不是创建了太多中间对象。

我的习惯是:思路确认后,先把关键循环用for写出来,再用Kotlin语法简化。举一个最大子数组和的例子:

var cur = 0 var best = Int.MIN_VALUE for (x in nums) { cur = maxOf(cur + x, x) best = maxOf(best, cur) }

这个版本没有中间集合,没有lambda调用,编译后和手写Java几乎没区别。不是说高阶函数不能写,而是要知道边界在哪。

4.3 可空性在链表和树题里的正确用法

力扣的链表、树节点天然是可空的,所以遍历时最常见的写法是:

var cur: ListNode? = head while (cur != null) { // 使用 cur.`val` cur = cur.next }

用while (cur != null)而不是while (cur!!.next != null),因为后者一旦cur为null就直接抛NPE。Kotlin的while条件会做智能转换:在循环体内部,编译器知道cur不为null,不需要加任何符号。

很多从Java转过来的人喜欢到处写!!,能用,但不推荐。!!的本意是“我确定它不是null”,如果这个确信是错的,程序直接崩溃。在链表题里用while (cur != null)让编译器帮你证明,远比手动打感叹号可靠。

4.4 by lazy在刷题里到底有没有用

热词里出现了by lazy,我得说点反直觉的实话:在力扣算法题里,by lazy几乎没什么用武之地。by lazy适合类属性级别的延迟初始化,比如一个类创建后,某些重对象到第一次访问时才构建;但力扣的Solution类通常每个测试用例都会新建,一次调用里你更希望用局部变量而不是类属性。

我唯一能想到的合理场景是把一个很大的预处理结果挂在Solution的属性上,通过by lazy避免重复构建。但力扣每题单独运行一个实例,实际收益非常小。by lazy是工程代码里的好东西,不是算法题里的银弹,别为了用而用。

4.5 递归深度和栈溢出的阈值判断

JVM的默认栈深度大概在数千层到上万层之间。二叉树中序遍历高度是n的极端情况,如果链表化,递归深度就很大。基础算法题里,链表反转、二叉树遍历用递归都没问题,但像岛屿数量那种在棋盘上蔓延的DFS,最坏情况递归深度可能是m*n,如果棋盘是200x200,就有四万层,栈必爆。

遇到这种题,要么把递归改成显式栈,要么用BFS。判断阈值很简单:递归深度超过几千就需要警惕。Kotlin没有额外优化,别赌栈不会爆。

5. 把刷题经验沉淀成模板:我整理的Kotlin模板清单

5.1 滑动窗口模板:一类题盖住大半双指针题

滑动窗口的核心框架很简单:右指针一步步前进,进入窗口;左指针在窗口不满足条件时收缩。这个框架吃透,无重复字符最长子串、最小覆盖子串、字符串排列一类题都能套。

fun slide(nums: IntArray, k: Int): Int { var left = 0 var ans = 0 var windowSum = 0 for (right in nums.indices) { windowSum += nums[right] while (windowSum > k) { windowSum -= nums[left] left++ } ans = maxOf(ans, right - left + 1) } return ans }

这里nums是整数数组,条件可能换成“子串无重复字符”“子串包含某个字符集”等等。模板的价值不在代码本身,而在于你遇到新题时知道:这类题可以用“窗口+两指针”去思考。

5.2 二叉树遍历模板:前中后序一个套路

基础算法里的树题,大多建立在遍历之上。前序、中序、后序的区别只是访问节点的位置不同,用递归写Kotlin可以非常精简:

fun inorder(root: TreeNode?): List<Int> = if (root == null) emptyList() else inorder(root.left) + listOf(root.`val`) + inorder(root.right)

这段代码可读性极高,但会创建很多中间List。如果题目数据量大,建议写成mutableListOf<Int>()配合辅助函数:

fun dfs(node: TreeNode?, ans: MutableList<Int>) { node ?: return dfs(node.left, ans) ans.add(node.`val`) dfs(node.right, ans) }

面试时我会先给这个版本,再聊递归转迭代的思路。模板的价值是让你不用在基础树上花时间思考结构,把精力留给题目真正的考点。

5.3 回溯模板:全排列子集组合的通用骨架

回溯是所有DFS类题的母版。把“选择-递归-撤销选择”这个循环记牢,全排列、组合、子集、N皇后都能套:

fun backtrack( path: MutableList<Int>, choices: List<Int>, used: BooleanArray, ans: MutableList<List<Int>> ) { if (path.size == choices.size) { ans.add(path.toList()) return } for (i in choices.indices) { if (used[i]) continue used[i] = true path.add(choices[i]) backtrack(path, choices, used, ans) path.removeAt(path.lastIndex) used[i] = false } }

子集和组合题往往不需要used数组,改成从startIndex开始枚举,避免重复组合。这就是模板的迁移:骨架不变,剪枝条件变。

5.4 并查集模板:图论题的免死金牌

岛屿数量、省份数量、冗余连接这类图论基础题,用并查集非常顺手。Kotlin版模板我常年存着:

class DSU(val n: Int) { private val parent = IntArray(n) { it } fun find(x: Int): Int { var i = x while (parent[i] != i) { parent[i] = parent[parent[i]] i = parent[i] } return i } fun union(a: Int, b: Int) { val ra = find(a) val rb = find(b) if (ra != rb) parent[rb] = ra } }

parent[i] = parent[parent[i]]是路径压缩,让每次find都更接近O(1)。按秩合并在这个基础上还能再优化一点,但基础题的约束下,路径压缩基本够用。

5.5 快速幂模板:数学类的底层引擎

求幂、取模这类数学题,循环乘会超时,快速幂是必备模板。Kotlin位运算写起来很自然:

fun qpow(base: Long, exp: Int, mod: Long): Long { var a = base % mod var b = exp var ans = 1L while (b > 0) { if ((b and 1) == 1) ans = ans * a % mod a = a * a % mod b = b shr 1 } return ans }

每一次循环,指数b右移一位,a自乘。如果当前位是1,就把a乘到结果里。这个过程等同于把指数拆成二进制,按位累乘,复杂度从O(n)降到O(log n)。

刷题不需要背住所有模板,只需要在需要用的时候能想起“这类题我整理过模板”,然后翻出来改一改。我建议你把模板单独放一个文件,比如Templates.kt,随用随取。

6. 本地IDE和快捷键:把刷题流程工程化的操作细节

6.1 为什么我把刷题主战场从网页搬到IDEA

力扣网页编辑器在浏览器里写题体验其实还可以,但它没有强大的重构、重命名、批量修改能力。刷题到一定程度,代码量变大之后,我越来越依赖IntelliJ IDEA:代码可以当场编译运行,断点调试能看变量状态,快捷键能瞬间完成重命名和导入。

我的流程很简单:从力扣把题目描述复制到本地,在src/main/kotlin里新建一个文件,粘上class Solution和题干旁的样例,写完先在本地跑测试,通过后再把方法体粘回力扣提交。一来一回看起来多了一步,实际调试效率提升非常明显。

6.2 最常用的八组快捷键

我自己常年用的快捷键如下,Windows和macOS我同时给了:

操作Windows/LinuxmacOS我的用途
重命名Shift+F6Shift+F6把a改成leftIndex
提取变量Ctrl+Alt+VCmd+Alt+V抽出重复表达式
提取函数Ctrl+Alt+MCmd+Alt+M把一大段递归逻辑拆成函数
自动补全Ctrl+SpaceCtrl+Space补全函数名和变量名
快速修复/导入Alt+EnterOption+Enter导入缺失的ArrayDeque
查找选中项Ctrl+GCmd+G跳到指定行
选择下一个相同词Alt+JCtrl+G批量修改多处相似变量
注释/取消注释Ctrl+/Cmd+/临时屏蔽调试代码

真正高频的其实就是Alt+Enter和Shift+F6。前者修导入,后者改变量名。名字起得好,代码可读性直接上一个台阶。

6.3 一个本地跑题模板,告别点击“运行”的焦虑

很多人喜欢用println验证结果,但输出要看一眼才能确定对不对。更好的办法是用check断言:

fun main() { val solution = Solution() check(solution.lengthOfLongestSubstring("abcabcbb") == 3) check(solution.lengthOfLongestSubstring("bbbbb") == 1) check(solution.lengthOfLongestSubstring("pwwkew") == 3) }

如果所有断言通过,程序静默退出;如果某个样例不通过,立刻抛异常告诉你预期值和实际值不一致。配合IDEA单测会更完整,但刷题阶段check就够用了。我每个题文件底部都留着这个main,提交时删掉,下次复习时把新样例加进去直接跑。

6.4 用Git按类型管理你的刷题仓库

目录结构我建议按算法类型分,而不是按日期分:

src/main/kotlin/ array/ slidingwindow/ linkedlist/ binarytree/ backtrack/ dp/ math/

每个文件开头写一两行注释,记录题目编号、思路、复杂度、踩坑点。比如:

// 3. 无重复字符的最长子串 // 双指针+哈希表,O(n) // 注意:p >= left 时才更新left class Solution { ... }

三个月后回头看,你不是刷了三百道题,而是建立了一个自己的算法查找表。遇到新题先翻这个表找相似思路,比从头回忆快得多。

我最满意的流程就是这套组合:本地IDEA + Git按类型归档 + 模板文件 + 每道题保留check断言。刚开始可能会觉得多花了一点时间,但坚持几十题后,你会发现自己对新题的拆解速度明显比单纯刷题更快。这是我用Kotlin刷力扣基础算法最值得分享的收获,也希望能帮正在这条路上坚持的你少走几步弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 3:23:33

机房静电地板运维全解:防静电性能、承重与接地系统常见问题

机房静电地板这玩意儿&#xff0c;平时没人搭理它&#xff0c;可一旦出点幺蛾子&#xff0c;轻则影响设备运行&#xff0c;重则直接引发业务中断。我做过不少机房的巡检和改造项目&#xff0c;发现很多运维团队对静电地板的认知还停留在“能走人就行”的层面&#xff0c;导致一…

作者头像 李华
网站建设 2026/10/10 3:23:08

深圳有哪些能促成交的AI公司可以选,讯灵智能科技实力参考

中小企业AI营销落地的4个常见踩坑点在数字化转型加速的当下&#xff0c;不少企业都想通过AI工具提升营销效率、拉动业务增长&#xff0c;但在实际选择和落地过程中&#xff0c;很容易踩进常见的四类陷阱。 内容不精准&#xff0c;获客转化双落空很多企业尝试过AI营销工具&#…

作者头像 李华
网站建设 2026/10/10 3:22:53

C++栈与队列深度解析:从底层原理到面试与工程实战

1. 栈和队列到底在解决什么问题&#xff1a;先别急着写代码先说句实在话&#xff1a;很多人在学C的时候&#xff0c;栈和队列这两个结构是"背定义"过去的——后进先出、先进先出&#xff0c;背得滚瓜烂熟&#xff0c;但到了真正写项目、刷题、做期末复习的时候&#…

作者头像 李华
网站建设 2026/10/10 3:22:45

链表刷题核心框架:虚拟头节点与三大经典操作实战

算法训练营进入第三天&#xff0c;今天正式从数组切到链表。昨天还在讲双指针和滑动窗口&#xff0c;今天就变成了指针的指向、节点的增删。今天安排的三道题——203. 移除链表元素、707. 设计链表、206. 反转链表&#xff0c;其实是一条非常丝滑的学习链路&#xff1a;先用最简…

作者头像 李华
网站建设 2026/10/10 3:22:30

照着用就行:2026年顶尖AI论文平台榜单,免费版也能写合规初稿

2026 年实测 10 款主流 AI 论文工具&#xff0c;千笔AI以全流程覆盖 语义级降重 免费查重领跑综合榜&#xff1b;ThouPen 稳坐留学生毕业全流程工具头把交椅&#xff1b;免费工具中DeepSeek Scholar、豆包学术版表现亮眼&#xff0c;30 分钟即可生成万字高质量初稿&#xff0…

作者头像 李华
网站建设 2026/10/10 3:22:28

SpringAI + MCP + SSE:Java后端接入AI工具调用的最佳实践

最近在忙一个 Java 后端接入大模型工具调用的项目&#xff0c;需求不复杂&#xff1a;让 AI Agent 能去查数据库、调内部 REST 接口&#xff0c;还要能实时拿结果。选型的时候卡了一下&#xff0c;最后定了SpringAI MCP SSE这条路线&#xff0c;整体跑下来比想象中省事&#…

作者头像 李华