简体   繁体   中英

C swapping with doubly linked list

I'm trying to swap the position of nodes in a linked list and later on sorting with a sort function. I'm getting a logical error in either one of these functions. When I run the program it goes in infinite loop.

updated code

int adjuctNodes(struct student_record_node** n1, struct student_record_node** n2)

{
      struct student_record_node* prev_;
      struct student_record_node* next_;  
      return((*n1)->next_==(*n2) && (*n2)->prev_ == (*n1))||
      ( (*n1)->prev_ ==(*n2) && (*n2)->next_ == (*n1) );
}

void updateOuterPointer(struct student_record_node** n)

{
     struct student_record_node* next_;
     struct student_record_node* prev_;
     if((*n)->next_!=NULL)
             (*n)->prev_->next_=(*n);
     if((*n)->next_ !=NULL)
             (*n)->next_->prev_=(*n);
}


/*Swaping */

void swap(struct student_record_node** node1, struct student_record_node** node2)

{


          struct student_recod_node* prev_;
          struct student_recod_node* next_;
          struct student_record_node* ptr=(*node1)->next_;

             if(adjucntNodes((node1),(node2)))
     {
             (node1)->prev_=pnode2;
             (node2)->prev_=pnode0;
             (node1)->next_=pnode3;
             (node2)->next_=pnode1;

     }else{

             (node1)->prev_=pnode1;
             (node2)->prev_=pnode0;
             (node1)->next_=pnode3;
             (node2)->next_=pnode2;
     }

     updateOuterPointer((node1));
     updateOuterPointer((node2));

} 
 /*Sorting linked list*/


void sort(struct student_record_node**recordsHead,int(*compare_fcn)(struct  
student_record_node*,struct student_record_node*))

 {

         int swapped;
         struct student_record_node *ptr1=*recordsHead;
         struct student_record_node *lptr = NULL;

         if (ptr1 == NULL)
                 return;

         do
         {
                 swapped = 0;
                 ptr1 = *recordsHead;


                 while (ptr1->next_ != lptr)
                 {
                              if (compare_fcn(ptr1,ptr1->next_))
                         {
                                 printf("swapping\n");
                                swap(&ptr1,&ptr1->next_);
                                if(ptr1==*recordsHead)
                                 {
                                    (*recordsHead)=ptr1->next_;
                                 }
                                 swapped=1;

                         }

                         else ptr1 = ptr1->next_;
                 }
                    lptr = ptr1;
                         ;
         }
         while (swapped);


}

To handle both the cases where nodes are adjacent or not adjacent with common code, first swap the (external) pointers to the two nodes, then swap the two node's (internal) pointers. This will end up rotating the pointers as needed if the nodes are adjacent, and swapping pairs of pointers if the nodes are not adjacent. Note if the nodes are adjacent, one of the "external" pointers will be one of the "other" nodes internal pointers, and vice versa, but it still works out: swap "external" pointers first, then "internal" pointers next.

Be sure to use temporary pointers (technically pointers to node pointers) as needed when doing the swaps, otherwise you overwrite node pointers part way through a swap operation. If stuck, I can update with an example later.

update - diagram type example to show what happens, using a single linked list with just next pointers as an example. Assume you start with 5 nodes, 0 to 4:

0->1->2->3->4

swap 1 and 3, 0-> and 2-> are the external pointers, 1-> and 3-> are internal. First swap 0-> and 2->

0->3
2->1

then swap 1-> and 3->

1->4
3->2

resulting in

0->3->2->1->4

Starting from 0->1->2->3->4 swap 1 and 2, 0-> and 1-> are external, 1-> and 2-> are internal. Swap 0-> and 1->

0->2
1->1

then swap 1-> and 2->

1->3
2->1

resulting in

0->2->1->3->4

Example swap code. This code assume that there is head pointer to point to the first node, and a tail pointer to point to the last node (or NULL).

struct student_record_node *Head = &firstnode;  /* head */
struct student_record_node *Tail = &lastnode;   /* tail (NULL is ok) */

/* swap function */

void swap(struct student_record_node **Head,
          struct student_record_node **Tail,
          struct student_record_node *node1,
          struct student_record_node *node2)
{
struct student_record_node **en1 /* & external next ptr to 1 */
struct student_record_node **en2 /* & external next ptr to 2 */
struct student_record_node **ep1 /* & external prev ptr to 1 */
struct student_record_node **ep2 /* & external prev ptr to 2 */
struct student_record_node  *tmp /* temp node ptr */
    en1 = (node1->prev_ != NULL) ? &(node1->prev_->next_) : Head;
    en2 = (node2->prev_ != NULL) ? &(node2->prev_->next_) : Head;
    ep1 = (node1->next_ != NULL) ? &(node1->next_->prev_) : Tail;
    ep2 = (node2->next_ != NULL) ? &(node2->next_->prev_) : Tail;
    /* swap en1, en2 */
    tmp = *en1;
    *en1 = *en2;
    *en2 = tmp;
    /* swap ep1, ep2 */
    tmp = *ep1;
    *ep1 = *ep2;
    *ep2 = tmp;
    /* swap n1, n2 next_ */
    tmp = node1->next_;
    node1->next_ = node2->next_;
    node2->next_ = tmp;
    /* swap n1, n2 prev_ */
    tmp = node1->prev_;
    node1->prev_ = node2->prev_;
    node2->prev_ = tmp;
}

    /* call to swap function */
    swap(&Head, &Tail, node1, node2);

There are two main problems in the original code, and possibly a third:

  1. The swap function does not work when the nodes being swapped are adjacent, but the sort function only swaps adjacent nodes!
  2. After swapping two nodes ptr1 and ptr1->next_ , the sort function checks if the first node being swapped, ptr1 , was at the head of the list, and if so makes ptr1->next_ the head of the list. However, the two nodes are now in reverse order, so it should make ptr1->prev_ the head of the list in this case.
  3. Comparison functions usually return a negative value if the first argument is less than the second argument, zero if they are equal, or a positive value if the first argument is greater than the second argument. The sort function currently seems to expect the comparison function to return 0 if the first argument is less than or equal to the second argument. This may or may not be a bug, but it is unconventional.

Additionally, the interface to the swap function can be simplified as there is no need to pass pointers to pointers to the nodes.

The following example program fixes the above problems:

#include <stdio.h>
#include <string.h>

struct student_record_node {
    struct student_record_node *next_;
    struct student_record_node *prev_;
    const char *name;
    unsigned int age;
};

void swap(struct student_record_node *node1, struct student_record_node *node2)
{
    struct student_record_node *ptr1, *ptr2;

    /* Swap the 'next_' pointers, taking adjacency into account. */
    ptr1 = node1 == node2->next_ ? node2 : node2->next_;
    ptr2 = node2 == node1->next_ ? node1 : node1->next_;
    node1->next_ = ptr1;
    node2->next_ = ptr2;
    /* Swap the 'prev_' pointers, taking adjacency into account. */
    ptr1 = node1 == node2->prev_ ? node2 : node2->prev_;
    ptr2 = node2 == node1->prev_ ? node1 : node1->prev_;
    node1->prev_ = ptr1;
    node2->prev_ = ptr2;
    /* Fix the links from other nodes. */
    if (node1->next_) node1->next_->prev_ = node1;
    if (node1->prev_) node1->prev_->next_ = node1;
    if (node2->next_) node2->next_->prev_ = node2;
    if (node2->prev_) node2->prev_->next_ = node2;
}

int compare_ages(const struct student_record_node *a,
        const struct student_record_node *b)
{
    return a->age < b->age ? -1 : a->age > b->age ? 1 : 0;
}

int compare_names(const struct student_record_node *a,
        const struct student_record_node *b)
{
    return strcmp(a->name, b->name);
}

void sort(struct student_record_node **recordsHead,
        int(*compare_fcn)(const struct student_record_node *,
                const struct student_record_node *))
{
    int swapped;
    struct student_record_node *ptr1;
    struct student_record_node *lptr = NULL;

    if (*recordsHead == NULL)
        return;

    do
    {
        swapped = 0;
        ptr1 = *recordsHead;

        while (ptr1->next_ != lptr)
        {
            if (compare_fcn(ptr1, ptr1->next_) > 0)
            {
                printf("swapping (%s:%u <=> %s:%u)\n", ptr1->name, ptr1->age,
                        ptr1->next_->name, ptr1->next_->age);
                swap(ptr1, ptr1->next_);
                if (ptr1 == *recordsHead)
                {
                    /* ptr1 is now the second node. */
                    (*recordsHead) = ptr1->prev_;
                }
                swapped = 1;
            }
            else
            {
                ptr1 = ptr1->next_;
            }
        }
        lptr = ptr1;
    }
    while (swapped);
}

void dump(const struct student_record_node *students)
{
    const struct student_record_node *prev_ = NULL;
    unsigned int pos = 0;

    while (students)
    {
        if (students->prev_ != prev_)
        {
            printf("[%u] ** Bad prev_ link!\n", pos);
        }
        printf("[%u] %s:%u\n", pos, students->name, students->age);
        pos++;
        prev_ = students;
        students = students->next_;
    }
}

int main(void)
{
    static struct student_record_node testdata[] =
    {
        { testdata + 1, NULL, "susan", 20 },
        { testdata + 2, testdata + 0, "bill", 21 },
        { testdata + 3, testdata + 1, "joe", 18 },
        { testdata + 4, testdata + 2, "tom", 19 },
        { NULL, testdata + 3, "karen", 21 },
    };
    struct student_record_node *students = testdata;

    puts("Original order:");
    dump(students);
    puts("By name:");
    sort(&students, compare_names);
    dump(students);
    puts("By age:");
    sort(&students, compare_ages);
    dump(students);
    return 0;
}

Output:

Original order:
[0] susan:20
[1] bill:21
[2] joe:18
[3] tom:19
[4] karen:21
By name:
swapping (susan:20 <=> bill:21)
swapping (susan:20 <=> joe:18)
swapping (tom:19 <=> karen:21)
swapping (susan:20 <=> karen:21)
[0] bill:21
[1] joe:18
[2] karen:21
[3] susan:20
[4] tom:19
By age:
swapping (bill:21 <=> joe:18)
swapping (karen:21 <=> susan:20)
swapping (karen:21 <=> tom:19)
swapping (bill:21 <=> susan:20)
swapping (bill:21 <=> tom:19)
swapping (susan:20 <=> tom:19)
[0] joe:18
[1] tom:19
[2] susan:20
[3] bill:21
[4] karen:21

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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