我在理解以下代码块时遇到了一些麻烦:
void InsertSorted(Entry * & list, Entry * newOne) {
if (list == NULL || newOne->name < list->name) {
newOne->next = list;
list = newOne;
} else {
InsertSorted(list->next, newOne);
}
}
我尝试了遍历代码,但只是设法达到了第一个if语句的地步。一旦我到达执行第一个if语句的位置,我就不明白先前对InsertSorted的调用如何管理将列表的前部连接到新创建的列表。
由于
答案 0 :(得分:6)
要理解这个功能,只需绘制每次调用时获得的数据。
假设您有一个类似的列表(假设名称是int
):
1 -> 4 -> 6 -> 7 -> 10 -> NULL
您想插入5
。
在第一次通话时,list
通过原始来电者指针引用1
。也就是说,如果你这样做:
InsertSorted(myList, someNode);
该函数内的list
引用该函数外的myList
,并将其更改为 更改外部 >。现在,由于list
不是NULL
且newOne->name
不是< list->name
,因此未通过if条件。因此,该函数使用list
的{{1}}指针和next
调用自身。这就是我们现在所处的位置:
newOne
在下一个电话中,1 -> 4 -> 6 -> 7 -> 10 -> NULL
^ list refers to this one
5
^ this is newOne, floating off somewhere by itself
引用上一次通话中的list
,这意味着它引用list->next
。同样4
不满意,所以我们继续if
:再次使用else
调用该函数(请记住list->next
现在引用list
,使4
引用此调用中的list->next
。这就是我们现在所处的位置:
6
在下一个电话中,1 -> 4 -> 6 -> 7 -> 10 -> NULL
^ list refers to this one through 1's next pointer
5
^ this is newOne, floating off somewhere by itself
引用list
的{{1}}指针,代表next
。这是列表的样子:
4
这次,6
满意(因为5 <6),所以我们
将1 -> 4 -> 6 -> 7 -> 10 -> NULL
^ list refers to this one through 4's next pointer
5
^ this is newOne, floating off somewhere by itself
指向if
。这使得代表newOne->next
的新节点指向其list
节点的5
。
将6
设为next
。这可能令人困惑,但请记住list
是引用,这意味着更改它会更改原始内容。当newNode
引用list
时,原始文件为list->next
,因此它与设置指向list
的{{1}}指向点的节点相同到4
。
这意味着列表现在看起来像这样:
4
该函数不进行任何调用,因此函数终止,并且控制返回到首先调用它的函数。
您刚刚按排序顺序插入新元素。
您需要考虑三个角落案例:
next
)假设您总是尝试插入newOne
作为这些测试的新元素。
因此,对于第一个,当1 -> 4 -> 5 -> 6 -> 7 -> 10 -> NULL
^ here's newOne
为NULL时 - 也就是说,您的列表看起来像
list == NULL
5
语句会立即生效,您将list
设置为NULL
(这意味着if
为newOne->next
),{{1}到list
。该函数退出,您的列表如下所示:
newOne->next
到目前为止,非常好。
如果要插入的元素小于 比其他所有元素都要小,比如说:
NULL
然后立即触发list
。您将newOne
设置为5 -> NULL
,使其指向7 -> 9 -> NULL
5
^ newOne
,并将if
设置为newOne->next
。
list
这是照顾的。
最后一个角落的情况是新元素大于所有现有元素。说你有
7
作为你的清单。在第一次传递时,list
将指向3,newOne
将不会被触发。因此,您可以使用5 -> 7 -> 9 -> NULL
调用该函数,该函数指向3 -> NULL
。触发list
(因为if
)并将list->next
设置为NULL
(if
),然后将list == NULL
设置为{ {1}},newOne->next
指向list
,因为在第一次调用中,您通过引用传递了其NULL
指针,这意味着更改list
会更改它。现在你有:
newOne
这一切都很好。因此,此函数似乎可以为任何列表生成所需的结果。
作为旁注,这个函数是尾递归的,但是可以通过迭代而不是递归来使其更快。这是一个很好的学习练习。
另请注意,这是 not 插入排序,因为您没有采用未排序的列表并对其进行排序,您只是以类似于插入排序的方式在现有列表中插入新数据
答案 1 :(得分:0)
这是插入排序,该项目将根据订单插入其放置的列表中。递归将在您到达列表末尾后结束,或者在列表中找到该项目所在的位置。
请注意,对于每个递归调用,您在列表中前进一个链,列表指针的“head”基本上通过列表移动。
答案 2 :(得分:0)
基本上,如果这是正确的位置,这段代码会在列表的开头插入新元素 - 否则,它会向下移动并将下一个元素视为“开头”。
关键是列表指针是通过引用传递的,所以当我们说“list = newOne;”时它实际上对调用者的范围有影响。因此,当我们调用“InsertSorted(list-&gt; next,newOne)”时,它实际上可以更新我们的列表。
答案 3 :(得分:0)
newOne->next = list;
将newOne列表节点的下一个字段设置为当前列表的前面。
list = newOne;
设置跟踪列表前面的指针(称为“list”)以指向列表中的新第一个节点newOne。
以上仅在列表为空的情况下发生。 else条件沿着列表向下走一个节点,直到它找到列表中的最后一个节点,由if语句条件控制。
由于参数是通过引用传递的(如'&amp;'字段所示),因此函数内部所做的更改将在程序中的任何位置持续存在。
答案 4 :(得分:0)
我不明白先前对InsertSorted的调用如何管理将列表的前部连接到新创建的列表。
“列表的前面部分”已经完全与自身连接 - 这就是它的列表。
算法是:
1)找到插入点。 2)将新条目连接到位。现在一切都已连接。
我们通过递归找到当前列表的结尾:如果我们在那里,那么我们就在那里;否则,我们检查下一个链接。我们以同样的方式“检查下一个链接”,所以如果结束那么我们就完成了,否则我们继续下一个等等。
当我们结束时,我们执行以下步骤:
newOne->next = list;
list = newOne;
这意味着:
1)告诉新条目链接到列表的尾部。 2)告诉列表的head部分链接到新条目。
这是有效的,因为list
是对指针的引用。
newOne->next = list
将指针值从list
复制到newOne->next
。这意味着Entry
所指向的newOne
,现在有一个next
字段,指向list
指向的相同位置 - 即列表中的下一个元素。所以我们将新条目链接到列表的尾部。
list = newOne
将指针值从newOne
复制到list
。这意味着指针list
本身 - 它是列表头部的最后一个Entry
的一部分 - 现在指向newOne
指向的示例位置 - 即新的Entry
。因此,我们将列表的头部与新条目相关联。
由于引用,list
是列表中Entry
节点的一部分的实际指针,而不仅仅是一些恰好指向相同的随机Entry*
局部变量的地方。