让我们先从什么,我认为你应该做的是不同的:
- 你的函数的名称为
connecting
。鉴于你想要的功能的描述,这是而不是一个很好的名字。
- 我可以从使用中推断出
linkk
是一个typedef指针。在大多数情况下,隐藏指针并不是一个好主意。
- 你的函数返回一个
int
。它总是0
。为什么?这没有任何意义。
- 因为
linkk
可能是一个指向节点的指针,并且您将头指针按值(即它的副本)传递给该函数,所以无法处理列表头部最小的情况。返回“新”头指针,或将指针传递给头指针以便能够修改头指针。
- 您已经暗示您使用
sizeof
是完全错误的。 sizeof(L)
会给你指针的大小(在char
s),因此可能是8位在64位系统或4位在32位系统上。
- 您并未更改节点,而是移动节点之间的值。如果它是你想要做的,这是可以的,但是你的算法的描述表明你想要移动节点。
- 您的功能太多了,IMO。这可能很难分开,但更好的方法是分开查找/提取最小值并将其放在列表的末尾。
- 您的修改比您原来想要的要多得多。考虑一个列表,如
1, 2, 3, 0, 4
。使用您的算法,该列表将更改为2, 3, 1, 4, 0
。这样做不仅对性能不利,而且对于呼叫者来说也是非常令人惊讶的。当谈到编程时,惊喜并不好!
所以,让我们的希望良好的实施,分步实施:
struct node {
int item;
struct node * next;
};
我假设你要移动的节点包含的最低值到列表的末尾,如您描述。为了保持与原始代码更接近,我也将这个函数保存为一个函数,接收struct node * head
指针,尽管我的观点如上所述。让我们先来看一下特殊/基本情况:移动空列表的最小元素以及单个元素列表是微不足道的:不做任何事情。
if (head == NULL || head->next == NULL) {
return head;
}
我返回列表的“新”头允许调用者更新它自己的头指针。(正如已经说过的,head
只是调用者头指针的副本,修改它在调用站点不会有任何影响)。
因为我们在这里处理单向链表,并且实现不应该在列表中不必要地迭代,所以我们应该记住我们以前访问过的节点。否则,我们不能轻易从列表中提取一个节点:直接
struct node * follow, * point;
follow
遵循point
后面。
最初,我们将该点放在列表的第二个节点(我们已经检查了列表中至少有2个节点)。因此follow
将指向头:
point = head->next;
follow = head;
因为我们想找到最低的项目,我们需要保持最小的已经搜索列表的一部分的轨道。我们用头节点的值初始化它:
int currentMinimum = head->item;
现在我们准备遍历列表,以便找到包含最小值的节点。但是我们不仅需要找到包含最小值的节点,还需要找到它之前的节点和之后的节点,以便能够轻松地提取它。所以,3个指针:
struct node * predecessor,* minimum,* successor;
我们设置currentMinimum
到head
小号的项目,我们也应该相应地设置指针:
predecessor = NULL; // Nothing preceding the head
minimum = head;
successor = head->next;
现在让我们重复,完全动点在列表中,直到它在最后脱落:
while (point != NULL) {
// to be continued
follow = point;
point = point->next;
}
// when we're here, follow will point to the last node
在每次迭代中,我们需要检查,如果发现比目前的最低较小的值,并最终记住的节点包含它:
if (point->item < currentMinimum) {
predecessor = follow;
minimum = point;
successor = point->next;
currentMinimum = point->item;
}
现在,当我们走出循环,下面的状态应该达到:
minimum
指向包含最小的节点。
follow
指向列表的最后一个节点。
- 上面两个可能是相同的,这是一个特例!
predecessor
仍然可能是NULL
,这是另一种特殊情况!
首先考虑minimum = follow
的特殊情况:在这种情况下,最小值已经在列表的末尾,所以赢利!否则,我们需要minimum
“开刀”的节点淘汰之列,并追加到最后一个节点,指向follow
:
if (follow != minimum) {
if (predecessor != NULL) {
predecessor->next = successor; // Cut out
minimum->next = NULL; // will be the last node
follow->next = minimum; // append at end
} else {
// to be continued
}
}
正如你所看到的,还有要考虑第二个特殊情况:如果predecessor
仍然是NULL
,那么没有项目小于head
s项目。(因此,我们也可以测试minimum == head
)因此,列表的第一个节点将移动到最后。我们需要通知来电者!
head = head->next; // Second node is now the first one, though this is not all we need to do, see further down!
minimum->next = NULL; // Will be the last node
follow->next = minimum; // Append at end
由于分配给head
不仅改变了功能参数(这是与该功能被称为指针的拷贝),我们需要返回(可能修改!)头指针,给发话更新了自己的头指针的能力:
return head;
来电者将因此使用此功能,像这样:
struct node * head = get_a_fancy_list();
head = move_minimum_to_end(head); // This is our function being called!
最后,要考虑的:为y你可以看到,移动节点(而不是项目)更复杂。我们需要修改至少2个指针才能实现我们想要的。相反:移动项目值需要对项目值进行两次修改(并且迭代更容易)。因此,当指针分配比项目分配快时,移动节点而不是项目。由于项目是int
这是而不是这里的情况。
移动项目而不是包含项目的节点相当容易。首先,我们需要跟踪最小的(价值以及节点):
要迭代,我们再次将使用两个指针。它可以用一个一个来完成,但代码将是更具可读性这样:
struct node * point, * follow;
我们开始用相同的初始状态:
minimum = head;
currentMinimum = head->item;
follow = head;
point = head->next;
迭代类似于其他实施,是迭代步骤:
while (point != NULL) {
if (point->item < currentMinimum) {
minimum = point;
currentMinimum = point->item;
}
follow = point;
point = point->next;
}
// follow points to the last node now
现在,做一样的以前的实现,我们可以交换的最后一个节点的项目和最小的节点:
minimum->item = follow->item;
follow->item = currentMinimum; // contains minimum->item
有在检查follow != minimum
像以前的做法是没有意义的:你可以这样做,但交换节点的项目有自己的项目不会做任何伤害。 OTOH添加if
将增加一个分支,因此可能会导致性能损失。
由于我们没有更改列表结构(节点之间的链接),因此没有更多需要考虑的事项。我们甚至不需要告诉来电者一个新的头像,因为它永远不会有任何改变。出于风格的目的,我会添加它:
return head;
好吧,这有点长,但希望它很容易理解!
1)'我
BLUEPIXY
请不要将指针隐藏在'typedef'或'#define'的后面,否则会导致并发症。此外,只设置一次节点会更好,因为这是一个链表,并且不会始终更改值。如果你有一个指向该节点的指针呢?插入后它会有错误的值。 –
你的链表没有其长度的信息; sizeof'操作符不会神奇地追踪它。一个链表由节点之间的链接完整描述,所以你的遍历应该通过'next'指针从头节点直到节点是'NULL'。因为你还需要一个有效的下一个节点来代替你的代码,所以你应该用'while(L && L-> next)替换外部'for'循环...'Sami想要交换节点而不是值的东西值得考虑。 –