所以我正在编写这个函数,使得最小的节点位于链表的末尾。除了最后一个元素之外,它还有效。如果链接列表创建为包含0~9项的节点,则此函数后面的列表为{1,2,3,4,5,6,7,8,0,9}且零不会出现在端。
任何想法为什么?
我的功能;
int connecting(linkk A)
{
linkk L = A;
for (int i = 0; i<sizeof(L);i++)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
L = L->next;
}
else{L=L->next;}
}
return 0;
}
答案 0 :(得分:1)
让我们从我认为你应该做的不同开始:
connecting
。鉴于您对该功能要做什么的描述,这是不是一个好名字。linkk
是一个typedef&#39; ed指针。在大多数情况下,以这种方式隐藏指针并不是一个好主意。int
。它总是0
。为什么?这没有任何意义。linkk
可能是指向节点的指针,并且您通过值(即它的副本)将头指针传递给函数,所以您无法处理头部的情况你的清单是最低的。要么返回&#34;新&#34;头指针,或传递指向头指针的指针,以便能够修改头指针。sizeof
是完全错误的。 sizeof(L)
将给出指针的大小(char
s),因此在64位系统上可能为8或在32位系统上为4。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;
}
我正在回复&#34;新&#34;列表的头部允许调用者更新它自己的头指针。 (如上所述,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
的特殊情况:在这种情况下,最小值已经在列表的末尾,所以获利!否则,我们需要&#34; cut&#34; 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
项目。 (因此,我们也可以测试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!
最后,需要考虑的事情:正如您所看到的,移动节点(而不是项目)更复杂。我们需要修改至少2个指针才能达到我们想要的效果。相反:移动项目值需要对项目值进行两次修改(迭代更容易)。因此,仅当指针分配比项目分配更快时,移动节点而不是项目才有意义。由于项目属于int
类型,因此
移动项目而不是包含项目的节点要容易得多。首先,我们需要跟踪最小值(值和节点):
struct node * minimum;
int currentMinimum;
为了迭代,我们再次使用两个指针。它可以用一个完成,但代码将以这种方式更具可读性:
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 :(得分:0)
尝试此功能
int connecting(linkk A)
{
linkk L = A;
while(L->next!=NULL)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
}
L=L->next;
}
return 0;
}