要理解这个函数,只需绘制它每次调用获得的数据。
假设您有一个这样的列表(假设名称是int):
1 -> 4 -> 6 -> 7 -> 10 -> NULL
你想插入5。
在第一次调用时,list 通过原始调用者指针引用1。也就是说,如果你这样做:
InsertSorted(myList, someNode);
函数内部的list 指的是函数外部的myList,更改它内部会改变它外部。现在,if 条件没有通过,因为list 不是NULL 并且newOne->name 不是< list->name。所以函数调用自己,使用list 的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引用4的next指针,代表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是满意的(因为 5
使newOne->next 指向list。这使得代表5 的新节点指向6,因为它是next 节点。
-
将list 设置为newNode。这可能令人困惑,但请记住list 是一个引用,这意味着更改它会更改原始文件。原来是list->nextlist引用4,所以和设置指向4的next指针的节点指向newOne是一样的。
这意味着列表现在看起来像这样:
1 -> 4 -> 5 -> 6 -> 7 -> 10 -> NULL
^ here's newOne
并且该函数不进行任何调用,因此该函数终止,并且控制权返回到最初调用它的函数。
您刚刚按排序顺序插入了新元素。
您需要考虑三种极端情况:
- 列表为空(立即
list == NULL)
- 您要插入的元素小于所有现有元素
- 您要插入的元素大于所有现有元素
假设您总是尝试插入 5 作为这些测试的新元素。
所以对于第一个,当 list 为 NULL 时 - 也就是说,您的列表看起来像
NULL
if 语句将立即为真,您将newOne->next 设置为list(这意味着newOne->next 是NULL),并将list 设置为newOne。函数退出,您的列表如下所示:
5 -> NULL
到目前为止,一切都很好。
如果您要插入的元素比所有其他元素小,例如:
7 -> 9 -> NULL
5
^ newOne
然后if 也会立即触发。您将newOne->next 设置为list,使其指向7,并将list 设置为newOne。
5 -> 7 -> 9 -> NULL
这已经处理好了。
最后一个极端情况是新元素大于所有现有元素。说你有
3 -> NULL
作为您的清单。在第一次通过时,list 将指向 3,并且不会触发 if。因此,您将使用指向NULL 的list->next 调用该函数。 if 被触发(因为list == NULL)并且您将newOne->next 设置为list(即NULL)然后您将list 设置为newOne,这使得3->next 指向@987654402 @,因为在第一次调用中,您通过引用传递了它的next 指针,这意味着更改list 会更改它。现在你有:
3 -> 5 -> NULL
这一切都很好。所以这个函数似乎可以为任何列表产生所需的结果。
附带说明,此函数是尾递归的,但可以通过使其迭代而不是递归来加快速度。这是一个很好的学习练习。
另请注意,这 不是 插入排序,因为您没有获取未排序的列表并对其进行排序,您只是以类似于插入排序的方式在现有列表中插入新数据.