问题
已知链表节点定义如下:
1 | class ListNode { |
如何用 C++ 初始化出指定链表,如:1->2->3?
一般做法
直接法,这种方法遇到长的链表会导致一行长度过长:
1
ListNode* list = new ListNode(1, new ListNode(2, new ListNode(3)));
分步法,缩短列宽,缺点是初始化长度为n的链表需要n行代码:
1
2
3ListNode* list = new ListNode(1);
ListNode* node2 = list->next = new ListNode(2);
node2->next = new ListNode(3);循环法,最通用的做法:
1
2
3
4
5
6
7int values[] = {1, 2, 3};
ListNode* list = new ListNode(values[0]);
ListNode* cur = list;
for (int i = 1; i < 3; ++i) {
cur->next = new ListNode(values[i]);
cur = cur->next;
}
可以看出,这3种方法都比较啰嗦,如果使用 C++11 的列表初始化,可以简化很多。
列表初始化
列表初始化是C++11在旧语法的基础上提出的新语法。其提出是为了解决C++混乱的初始化语法:
1 | int i = 1; // C风格的初始化 |
有了列表初始化,上面的初始化可以统一写成:
1 | int i {1}; |
STL 中的容器也支持这种初始化方法:
1 | std::vector<int> v {1, 2, 3}; |
这些容器是通过添加一个以std::initializer_list
为参数的构造函数实现的。
以std::vector
为例,其大致实现代码如下:
1 | template <typename T> |
初始化链表
现在,让我们再来看下开头的问题。如何利用列表初始化语法初始化链表呢?
可以直接这样做:
1 | ListNode* list {1, new ListNode {2, new ListNode {3}}}; |
但是这样,跟上面的方法 1 几乎是一样的,初始化语句中有许多冗余的new ListNode
。
如何去掉new ListNode
呢?
可以使用一个辅助类ListBuilder
把new ListNode
“包”起来:
1 | struct ListBuilder { |
用法是这样:1
ListNode* list = ListBuilder {1, {2, {3}}};
当编译器遇到 ListBuilder {1, {2, {3}}}
时,认识到这是一个列表初始化,
会在 ListBuilder的构造函数中寻找匹配函数签名(int, ...)
的函数。
ListBuilder(int d, ListBuilder b = ListBuilder())
被匹配到。
{1, {2, {3}}}
中的1
被用于初始化参数d
,{2, {3}}
被用于初始化参数b
。
因为b
也是ListBuilder
,上述过程会递归地进行下去,直到遇到{3}
,递归终止。
最后,ListBuilder {1, {2, {3}}}
初始化完成,得到一个ListBuilder
实例,将它赋值给ListNode* list
。
为了实现从ListBuilder
到ListNode
的转换,需要类型操作符重载:
1 | operator ListNode* () const { |
练习
已知二叉树节点定义如下:
1 | class TreeNode { |
如何用 C++ 初始化出二叉树?