核心原因:力扣场景下对象需要持久化!
为什么指针+new可以实现持久化?
指针+new其实创建的是堆对象!
| 特性 | 栈对象 (Stack Object) | 堆对象 (Heap Object) |
|---|---|---|
| 创建方式 | 直接声明(如 Person p;),自动分配 | 用 new/make_unique/make_shared 创建,手动分配 |
| 内存管理 | 编译器自动分配 / 释放,无需手动干预 | 需手动释放(delete),或依赖智能指针自动释放 |
| 生命周期 | 随作用域结束销毁(如函数执行完、代码块结束) | 直到手动释放(delete)或程序结束才销毁 |
| 内存大小 | 栈空间小(通常几 MB),易栈溢出 | 堆空间大(接近系统可用内存),适合大对象 |
| 访问方式 | 直接通过对象名访问(. 运算符) | 通过指针访问(-> 运算符) |
| 灵活性 | 固定大小,创建后不可调整 | 动态分配,大小可灵活调整(如动态数组) |
为什么力扣中需要持久化?
算法逻辑需要「跨作用域操作同一个对象」
核心操作对象需要在多个函数 / 代码逻辑中被持续访问和修改,而不是局限在某个临时作用域里。
// 创建栈节点并让外部指针指向它
void createStackNode(ListNode*& head) {
ListNode node(10); // 栈对象:仅在当前函数作用域有效
head = &node; // 让外部指针指向栈对象
cout << "函数内访问:" << head->val <<"\n";
}
int main() {
ListNode* head = nullptr;
createStackNode(head); // 函数执行完,栈对象已销毁,后续访问=野指针
if (head) cout << head->val << "\n"; // 无效内存,结果乱码/崩溃
return 0;
}
数据结构的「链式引用」依赖持久化
链表、二叉树的核心是「指针引用关系」:比如链表节点的next、树节点的left/right,这些指针需要指向稳定存在的对象。
如果用栈对象:
- 每个节点都是临时的,作用域结束就销毁;
- 指针引用的是 “消失的对象”,整个数据结构就成了 “空中楼阁”。
如果用堆对象:
- 每个节点都持久化存在;
- 指针可以安全地指向其他节点,形成稳定的链式结构(比如
node1->next = node2),这是链表 / 树能正常工作的基础。
力扣的判题机制要求对象「全程有效」
void createNode(ListNode*& head, int val) {
ListNode* node = new ListNode(val); // 堆对象,持久化存在
head = node; // head指向堆对象,函数结束后对象仍在
}
int main() {
ListNode* head = nullptr;
createNode(head, 1); // 函数执行完,堆对象还在
traverse(head); // 正常访问,输出1
return 0;
}
力扣的判题系统会调用你写的函数(比如createNode),并在函数执行完后,遍历你返回的链表 / 树来验证结果
如果你的对象没有持久化,判题系统遍历的时候就会访问到无效内存,直接判定你的代码错误。
你的代码不仅要 “执行完”,还要让返回的对象在判题系统检查时依然有效,这必须依赖堆对象的持久化特性。