简体   繁体   English

此代码未按预期对链表进行完全排序

[英]This code does not completely sort a linked list as expected

I am new to programing and so I am trying out a program to sort a linked list by insertion sort algorithm.我是编程新手,所以我正在尝试一个程序来通过插入排序算法对链表进行排序。 I want to swap the nodes and not just the elements.我想交换节点而不仅仅是元素。 Although I have seen alternative implementations on the inte.net, but I'm curious to understand why my current implementation isn't giving the expected output.虽然我在 inte.net 上看到了替代实现,但我很想知道为什么我当前的实现没有给出预期的 output。

My code returns immediately after one swap operation without going through the entire list.我的代码在一次交换操作后立即返回,而没有遍历整个列表。 Here is my current implementation:这是我当前的实现:

sort.h排序.h

/**
 * struct listint_s - Doubly linked list node
 *
 * @n: Integer stored in the node
 * @prev: Pointer to the previous element of the list
 * @next: Pointer to the next element of the list
 */

typedef struct listint_s
{
const int n;
    struct listint_s *prev;
    struct listint_s *next;
} listint_t;

void print_list(const listint_t *list);
void insertion_sort_list(listint_t **list);

sort.c排序.c

#include "sort.h"
/**
 * insertion_sort_list - Sorts a list of integers using insertion sort algorithm
 *
 * @list: list of integers to be sorted
 * Return: Nothing
 */


void insertion_sort_list(listint_t **list)
{
   listint_t *left, *right, *temp;

        if (list == NULL)
                return;
        left = *list;
        right = left->next;

        if (right == NULL)
                return;

        while (right)
        {
                while (1)
                {
                        if (right->n < left->n)
                        {
                                //swap operation

                                temp = left->prev;
                                left->next = right->next;
                                right->next = left;
                                left->prev = right;
                                right->prev = temp;

                                //incase of a list with only two elements                                                    
                                if (right->prev == NULL)
                                {
                                        *list = right;
                                        return;
                                }
                                  //update the swapped nodes with the rest of the list
                                else
                                {
                                        right->prev->next = right;
                                        left->next->prev = left;
                                }
                        }

                        //if no swap operation
                        else
                                break;

                        //if swap occurred, update the transversal positions
                        right = left;
                        left = right;

                        //prints list after each swap
                        print_list(*list);

                        //move a step backwards and reiterate the loop
                        if (left->prev != NULL)
                        {
                                right = right->prev;
                                left = left->prev;

                        }

                }

                //continue with the next iteration
                right = right->next;
                left = left->next;
        }
}

    #include <stdio.h>
#include <stdlib.h>
#include "sort.h"

/**
 * create_listint - Creates a doubly linked list from an array of integers
 *
 * @array: Array to convert to a doubly linked list
 * @size: Size of the array
 *
 * Return: Pointer to the first element of the created list. NULL on failure
 */
listint_t *create_listint(const int *array, size_t size)
{
    listint_t *list;
    listint_t *node;
    int *tmp;

    list = NULL;
    while (size--)
    {
        node = malloc(sizeof(*node));
        if (!node)
            return (NULL);
        tmp = (int *)&node->n;
        *tmp = array[size];
        node->next = list;
        node->prev = NULL;
        list = node;
        if (list->next)
            list->next->prev = list;
    }
    return (list);
}

/**
 * main - Entry point
 *
 * Return: Always 0
 */
int main(void)
{
    listint_t *list;
    int array[] = {19, 48, 99, 71, 13, 52, 96, 73, 86, 7};
    size_t n = sizeof(array) / sizeof(array[0]);

    list = create_listint(array, n);
    if (!list)
        return (1);
    print_list(list);
    printf("\n");
    insertion_sort_list(&list);
    printf("\n");
    print_list(list);
    return (0);
}


    #include <stdio.h>
#include "sort.h"

/**
 * print_list - Prints a list of integers
 *
 * @list: The list to be printed
 */
void print_list(const listint_t *list)
{
    int i;

    i = 0;
    while (list)
    {
        if (i > 0)
            printf(", ");
        printf("%d", list->n);
        ++i;
        list = list->next;
    }
    printf("\n");
}

I tried to sort a list with the following set of integers:我尝试使用以下整数集对列表进行排序:

Input: 19, 48, 99, 71, 13, 52, 96, 73, 86, 7输入:19、48、99、71、13、52、96、73、86、7

Output: 19, 48, 71, 99, 13, 52, 96, 73, 86, 7 //swapped only 71 Output: 19, 48, 71, 99, 13, 52, 96, 73, 86, 7 // 仅交换 71

Expected output:预计 output:

19, 48, 71, 99, 13, 52, 96, 73, 86, 7
19, 48, 71, 13, 99, 52, 96, 73, 86, 7
19, 48, 13, 71, 99, 52, 96, 73, 86, 7
19, 13, 48, 71, 99, 52, 96, 73, 86, 7
13, 19, 48, 71, 99, 52, 96, 73, 86, 7
13, 19, 48, 71, 52, 99, 96, 73, 86, 7
13, 19, 48, 52, 71, 99, 96, 73, 86, 7
13, 19, 48, 52, 71, 96, 99, 73, 86, 7
13, 19, 48, 52, 71, 96, 73, 99, 86, 7
13, 19, 48, 52, 71, 73, 96, 99, 86, 7
13, 19, 48, 52, 71, 73, 96, 86, 99, 7
13, 19, 48, 52, 71, 73, 86, 96, 99, 7
13, 19, 48, 52, 71, 73, 86, 96, 7, 99
13, 19, 48, 52, 71, 73, 86, 7, 96, 99
13, 19, 48, 52, 71, 73, 7, 86, 96, 99
13, 19, 48, 52, 71, 7, 73, 86, 96, 99
13, 19, 48, 52, 7, 71, 73, 86, 96, 99
13, 19, 48, 7, 52, 71, 73, 86, 96, 99
13, 19, 7, 48, 52, 71, 73, 86, 96, 99
13, 7, 19, 48, 52, 71, 73, 86, 96, 99

7, 13, 19, 48, 52, 71, 73, 86, 96, 99 //sorted list

Removing the break statement in the code threw it into an infinite loop.删除代码中的 break 语句使其陷入无限循环。 Please help.请帮忙。

What you have there is not an insertion sort.你所拥有的不是插入排序。 It's close, but there are a number of things breaking it, including the way you are thinking about it.它很接近,但有很多事情打破了它,包括你思考它的方式。

The main problem is that it is, as coded, too complicated to keep everything straight in your head, so you mixed things up.主要问题是,按照编码,它太复杂了,无法让所有内容都清楚地记在脑海中,所以你把事情搞混了。 Part of that is also the names you are using.其中一部分也是您使用的名称。

Insertion Sort插入排序

Insertion sort divides a sequence up into two partitions: sorted and unsorted , in that order.插入排序将一个序列分为两个分区:已排序排序,按此顺序。

[1 2 3 4][7 8 0 6 9 5]
 ↑        ↑
 sorted   unsorted

You have already thought about this a little.你已经稍微考虑过了。 The sort starts with a sorted partition of length 1.排序长度为 1 的已排序分区开始。

[3][4 1 2 7 8 0 6 9 5]

Then, while there are unsorted elements (the unsorted partition is not empty), take the first unsorted and find its new position. Move it there, shifting all elements between the new position (inclusive) and old position (exclusive) rightward one .然后,当有未排序的元素(未排序的分区不为空)时,取第一个未排序的并找到其新的 position。将其移动到那里,将新的 position(含)和旧的 position(不含)之间的所有元素向右移动

[3 4][1 2 7 8 0 6 9 5]
[1 3 4][2 7 8 0 6 9 5]
[1 2 3 4][7 8 0 6 9 5]
...
[0 1 2 3 4 6 7 8 9][5]
[0 1 2 3 4 5 6 7 8 9][]

This is a pretty simple algorithm.这是一个非常简单的算法。 Two loops: one to traverse the list, and one to scan backwards for the place to insert each time.两次循环:一次遍历链表,一次向后扫描每次插入的地方。

Doubly-Linked List Insertion Sort双向链表插入排序

Linked lists give us an interesting twist here.链接列表在这里给了我们一个有趣的转折。 A normal insertion sort has to repeatedly swap the unsorted element leftward until it is in place.正常的插入排序必须反复向左交换未排序的元素,直到它就位。 But with a linked list that is a lot of work.但是使用链表需要大量工作。

Instead, we can simply unlink the node, and insert it in the correct position. There is no need to do repeated adjacent element swaps!相反,我们可以简单地取消链接节点,并将其插入正确的 position。不需要重复相邻元素交换!

while (unsorted elements remain):
  found ⟵ scan back to find the node to insert after (this is your inner loop)
  node ⟵ extract first unsorted node
  if (found == NULL):
    insert node at front of list
  else:
    insert node after found

Graphically, that looks like this:从图形上看,它看起来像这样:

[1]→[3]→[4]→[2]→[7]→[8]→[0]→[6]→[9]→[5]
             ↑
             unsorted

Start searching backwards...开始向后搜索...

         found
         ↓
[1]→[3]→[4]→[2]→[7]→[8]→[0]→[6]→[9]→[5]
             ↑
             unsorted

...until we find a node where found->nunsorted->n ...直到我们找到一个节点,其中found->nunsorted->n

 found
 ↓
[1]→[3]→[4]→[2]→[7]→[8]→[0]→[6]→[9]→[5]
             ↑
             unsorted

Extract the unsorted node, making sure to update unsorted to point to the next available node (if any):提取unsorted的节点,确保更新unsorted以指向下一个可用节点(如果有):

            extracted node
            ↓
 found     [2]
 ↓
[1]→[3]→[4]→[7]→[8]→[0]→[6]→[9]→[5]
             ↑
             (the new) unsorted

Insert the extracted node after the found found node:在 found found node插入提取的节点:

 found
 ↓
[1]→[2]→[3]→[4]→[7]→[8]→[0]→[6]→[9]→[5]
     ↑           ↑
     node        unsorted

Done.完毕。 Ready for the next loop.为下一个循环做好准备。

[1]→[2]→[3]→[4]→[7]→[8]→[0]→[6]→[9]→[5]
                 ↑
                 unsorted

Extract and Insert Nodes提取和插入节点

If you don't already, you will need some functions to unlink and insert nodes from/into a list.如果您还没有,您将需要一些函数来取消链接和插入列表中的节点。 I recommend:我建议:

void unlink_node (listint_t **list, listint_t *node);

void append_node (listint_t **tail, listint_t *node);
void prepend_node(listint_t **head, listint_t *node);
// Unlike other list functions, `head` and `tail` need not point to the
// head of the list (though they could)! Their values are updated, however, 
// to maintain the whole list and their head/tail position in the sublist.

You should not be doing this manually inside your sort function. All it does is add complexity that makes it harder to see what you are doing.不应该在你的排序 function 中手动执行此操作。它所做的只是增加复杂性,使你更难看到你在做什么。 Abstract those operations elsewhere and keep your sort function short and clean.在别处抽象这些操作并使您的排序 function 简短明了。

My version of your insertion sort function is about 12 lines long.我的插入排序版本 function 大约有 12 行长。 You should be able to do something similar — twenty lines maximum with heavy commentary.您应该能够做类似的事情——最多 20 行,带有大量评论。

Apologies道歉

I know this is a bit to think about, and not a fix-your-code kind of answer.我知道这需要考虑一下,而不是一种修复代码的答案。 Honestly, you got lost and your code is pretty badly broken.老实说,你迷路了,你的代码被严重破坏了。 Toss the sorting code you wrote and start over.扔掉你写的排序代码,重新开始。 The hints I give above are far more direct than you may think.我上面给出的提示比你想象的要直接得多。

Always think about your algorithm before you start hacking out code.在开始编写代码之前,请始终考虑您的算法。 In this case we can see that it benefits us to have the link and unlink helper functions.在这种情况下,我们可以看到使用 link 和 unlink 辅助函数对我们有好处。 That abstracts the complexity of those operations away so that we don't overwhelm ourselves when trying to sort.这将这些操作的复杂性抽象化了,这样我们在尝试排序时就不会不知所措。 By breaking your code up into smaller chunks, everything you do is significantly less complex, letting you focus on a simpler problem without stumbling into the weeds.通过将您的代码分解成更小的块,您所做的一切都大大降低了复杂性,让您可以专注于更简单的问题,而不会陷入困境。

Main主要的

Here is the main function I wrote to play with this:这是我写的主要 function 来玩这个:

#include <stdio.h>
#include <stdlib.h>
#include "listint.h"

int main(int argc, char **argv)
{
    listint_t *list = NULL;

    // Convert program arguments into linked list of integers    
    listint_t *tail = list;
    for (int n = 1;  n < argc;  n++)
    {
        append_node(&tail, new_node(atoi(argv[n])));
        if (!list) list = tail;
    }
    
    // Sort the list and show the user
    insertion_sort_list(&list);
    print_list(&list);
    
    // Don’t forget to clean up!
    free_list(&list);  
    return 0;
}

And some example runs:一些例子运行:

C:\Users\Dúthomhas\programming\cpp\foo> main 2 1 -7 34 5
-7 1 2 5 34

C:\Users\Dúthomhas\programming\cpp\foo> main 3 3 3
3 3 3

C:\Users\Dúthomhas\programming\cpp\foo> main 0
0

C:\Users\Dúthomhas\programming\cpp\foo> main 1 2 3
1 2 3

C:\Users\Dúthomhas\programming\cpp\foo> main 4 2
2 4

Consistency一致性

Your linked list functions should be as consistent as possible.您的链表函数应尽可能一致。 For example, the print_list() function should take a listint_t ** , just like all the other list functions.例如, print_list() function 应该采用listint_t ** ,就像所有其他列表函数一样。 Even though it doesn't need a reference to the list, it makes using it work just like any other list function.即使它不需要对列表的引用,它也可以像使用任何其他列表 function 一样使用它。

Your indentation and spacing is pretty good.您的缩进和间距非常好。 I assume the inconsistencies were just where you were messing with it.我认为不一致正是你搞砸的地方。 Personally, eight spaces is a very wide indent.就个人而言,八个空格是一个非常宽的缩进。 Switch to 2 or 4.切换到 2 或 4。

Names名称

Choose names that reflect what a thing is or does.选择能反映事物是什么或做什么的名称。 You seem to be already aware of this to some degree.你似乎已经在某种程度上意识到了这一点。 Choice of left and right in your algorithm suggests to me part of the logic you tried to implement (and why that logic failed — sorting a linked list is different than sorting an array).在你的算法中选择leftright向我暗示了你试图实现的部分逻辑(以及为什么该逻辑失败 - 对链表进行排序与对数组进行排序不同)。

As you think about the algorithm, before you even start writing code , you should be generating names for things that reflect what your algorithm is doing.当你考虑算法时,甚至在你开始编写代码之前,你应该为反映你的算法正在做什么的事物生成名称。 My version of the sort uses the very same names I created when I thought through the algorithm above: unsorted , node , find .我的 sort 版本使用了我在考虑上述算法时创建的完全相同的名称: unsortednodefind

Likewise, even the filenames you choose matter.同样,即使您选择的文件名也很重要。 The header file, for example, has as its primary purpose describing a doubly-linked list of integers and the operations that can be performed on them — one of which (the primary one for this homework, I assume) is sorting it.例如,header 文件的主要目的是描述整数的双向链表以及可以对它们执行的操作——其中之一(我假设是本作业的主要操作)是对其进行排序。 But sorting is still just an appendage to maintaining a list.但是排序仍然只是维护列表的附属物。 Hence, the files I created to play with this are named:因此,我创建的用于播放的文件被命名为:

listint.h
listint.c
main.c

Commentary and documentation评论和文档

The header file is your primary source of documentation for any library. header 文件是任何图书馆的主要文档来源。 Put the primary doxygen markup there.将主要的 doxygen 标记放在那里。

Remember, the purpose of documentation is for users of your library.请记住,文档的目的是为了图书馆的用户 Users should not care about anything except the interface presented to them.除了呈现给他们的界面之外,用户不应该关心任何事情。

Here we have a listint_t , and functions to work over a listint_t * .这里我们有一个listint_t和处理listint_t *的函数。

#ifndef LISTINT_H
#define LISTINT_H

/**
 * listint_t - Doubly linked list node
 *
 * @n: Integer stored in the node
 * @prev: Pointer to the previous element of the list
 * @next: Pointer to the next element of the list
 */
typedef struct listint_s
{
    const int n;
    struct listint_s *prev;
    struct listint_s *next;
} listint_t;

/**
 * new_node() - Create a new node
 *
 * @n: Integer to store in the node
 *
 * Return: The new, unlinked node, or NULL on allocation failure.
 */
listint_t *new_node(int n);

/**
 * free_list() - Delete an entire list
 *
 * @list: Reference to the list to delete. May not be NULL.
 *        (The referenced list itself may be NULL, though.)
 */
void free_list(listint_t **list);

/**
 * append_node()
 * prepend_node()
 * unlink_node()  - Add or remove nodes in a list
 *
 * @tail: Reference to the last node in a sublist
 * @head: Reference to the first node in a sublist
 * @list: Reference to the list to modify.
 * @node: The node to add or remove. If adding,
 *        the node must not be part of any list.
 *        If removing, the node must be a member
 *        of the argument list.
 */
void append_node (listint_t **tail, listint_t *node);
void prepend_node(listint_t **head, listint_t *node);
void unlink_node (listint_t **list, listint_t *node);

/**
 * insertion_sort_list() - Sorts a list of integers using insertion sort algorithm
 *
 * @list: Reference to the list of integers to be sorted
 */
void insertion_sort_list(listint_t **list);

/**
 * print_list() - Print a list to stdout
 *
 * @list: Reference to the list
 */
void print_list(listint_t **list);

#endif

(Sorry for abusing the Doxygen markup a little, but my patience with it is thin for some fun question I found on the inte.net.) (抱歉有点滥用 Doxygen 标记,但我对它的耐心很薄,因为我在 inte.net 上发现了一些有趣的问题。)

This stuff is hard.这东西很难 But it will make sense eventually.但它最终会有意义。 Keep going!继续!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM