C中的双向链表排序函数

Doubly Linked List sorting function in C

我正在使用冒泡排序来更改节点位置。我知道有几种情况需要注意节点 1 是否在列表的开头。如果节点 2 在列表的末尾。如果node1在开头,node2在结尾。所以我相信问题是当我交换作为邻居的节点时,如 node1->next_ = node2;因为如果我进行常规交换,我最终会得到 node2->next_ = node2。

我想知道我是否走上正轨,因为我尝试了类似于我写的东西并最终无限循环。我觉得有些事情我不明白,就像我在某处丢失了一个指针。

我相信这是正确的,除非这两个节点在链表中是邻居。

编辑澄清的链接命名。恢复原状。

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

  struct student_record_node *p1, *x1, *n1, *p2,*x2,*n2, *temp;

  p1 = (*node1)->prev_;
  x1 = *node1;
  n1 = (*node1)->next_;

  p2 = (*node2)->prev_;
  x2 = *node2;
  n2 = (*node2)->next_;

  /* swap next_  */

  if (p1 == NULL && n2 == NULL)
  {



  /* step one swap nodes */
    temp = *node1;
    *node1 = *node2;
    *node2 = temp; 
    /* step two swap node1 prev to be node2 prev */
    (*node2)->prev_ = NULL;
    /* step three swap node1 next to be node 2 next */
    (*node2)->next_ = n1;
    /* step four swap node1 next prev to be node2 next prev */
    (*node2)->next_->prev_ = x2;

    /* step 5 swap node2 next to be node1 next */
    (*node1)->next_ = NULL;
    /* step 6 swap node2 prev to be node1 prev */
    (*node1)->prev_ = n2;
    /* step 7 swap node2 prev next to be node1 prev next */
    (*node1)->prev_->next_ = x1;


  }
  else if (p1 == NULL)
  {
   /* step one swap nodes */
    temp = *node1;
    *node1 = *node2;
    *node2 = temp; 
     /* step two swap node1 prev to be node2 prev */
    (*node2)->prev_ = NULL;
    /* step three swap node1 next to be node 2 next */
    (*node2)->next_ = n1;
    /* step four swap node1 next prev to be node2 next prev */
    (*node2)->next_->prev_ = x2;

    /* step 5 swap node2 next to be node1 next */
    (*node1)->next_ = n2;
    /* step 6 swap node2 prev to be node1 prev */
    (*node1)->prev_ = p2;
    /* step 7 swap node2 prev next to be node1 prev next */
    (*node1)->prev_->next_ = x1;
    /* step 8 swap node2 next prev to be node1 next prev */
    (*node1)->next_->prev_ = x1;



  }
  else if(n2 == NULL)
  {
     /* step one swap nodes */
    temp = *node1;
    *node1 = *node2;
    *node2 = temp; 
     /* step two swap node1 prev to be node2 prev */
    (*node2)->prev_ = p1;
    /* step three swap node1 next to be node 2 next */
    (*node2)->next_ = n1;
    /* step four swap node1 next prev to be node2 next prev */
    (*node2)->next_->prev_ = x2;
        /* step 5 node1 prev next swapped with node2 prev next */
    (*node2)->prev_->next_ = x2;


    /* step 6 swap node2 next to be node1 next */
    (*node1)->next_ = NULL;
    /* step 7 swap node2 prev to be node1 prev */
    (*node1)->prev_ = p2;
    /* step 8 swap node2 prev next to be node1 prev next */
    (*node1)->prev_->next_ = x1;


  }
  else
  {
       /* step one swap nodes */
    temp = *node1;
    *node1 = *node2;
    *node2 = temp; 
     /* step two swap node1 prev to be node2 prev */
    (*node2)->prev_ = p1;
    /* step three swap node1 next to be node 2 next */
    (*node2)->next_ = n1;
    /* step four swap node1 next prev to be node2 next prev */
    (*node2)->next_->prev_ = x2;
        /* step 5 node1 prev next swapped with node2 prev next */
    (*node2)->prev_->next_ = x2;


    /* step 6 swap node2 next to be node1 next */
    (*node1)->next_ = n2;
    /* step 7 swap node2 prev to be node1 prev */
    (*node1)->prev_ = p2;
    /* step 8 swap node2 prev next to be node1 prev next */
    (*node1)->prev_->next_ = x1;
    /* step 9 swap node2 next prev to be node1 next prev */
    (*node1)->next_->prev_ = x1;


  }
  /* swap surrounding */

}

交换双链表中的两个节点时,有8个指针需要修改。请参见下图中的 8 个箭头。

+---+    +---+    +---+        +---+    +---+    +---+
|   |--->| A |--->|   | ...... |   |--->| B |--->|   |
|   |<---|   |<---|   | ...... |   |<---|   |<---|   |
+---+    +---+    +---+        +---+    +---+    +---+

现在,A 或 B 中的任何一个都可能位于列表的开头或结尾。它们也可以紧挨着(我们稍后会讨论一种特殊情况)。

这里最简单的事情是首先存储指向那四个未标记节点的指针。这是全部您需要的信息:

struct node *a_prev = a->prev;
struct node *a_next = a->next;
struct node *b_prev = b->prev;
struct node *b_next = b->next;

现在,由于这些指针中的任何一个都可以为 NULL,您只需在对它们执行操作之前进行完整性测试即可:

if (a_prev) a_prev->next = b;  //(1)
if (a_next) a_next->prev = b;  //(2)
if (b_prev) b_prev->next = a;  //(3)
if (b_next) b_next->prev = a;  //(4)

然后您可以从实际节点更新链接:

a->prev = b_prev;  //(5)
a->next = b_next;  //(6)
b->prev = a_prev;  //(7)
b->next = a_next;  //(8)

现在,那个特例呢?有两个版本:

a_prev   b_prev   a_next   b_next
+---+    +---+    +---+    +---+
|   |--->| A |--->| B |--->|   |
|   |<---|   |<---|   |<---|   |
+---+    +---+    +---+    +---+

b_prev   a_prev   b_next   a_next
+---+    +---+    +---+    +---+
|   |--->| B |--->| A |--->|   |
|   |<---|   |<---|   |<---|   |
+---+    +---+    +---+    +---+

我们来看第一个。哪些代码行会破坏列表的结构?您可以看到,由于 b_prev == aa_next == b,以下内容:

if (a_prev) a_prev->next = b;  //(1) OK
if (a_next) a_next->prev = b;  //(2) ERROR b->prev = b 
if (b_prev) b_prev->next = a;  //(3) ERROR a->next = a
if (b_next) b_next->prev = a;  //(4) OK
a->prev = b_prev;  //(5) ERROR a->prev = a
a->next = b_next;  //(6) OK and fixes 3
b->prev = a_prev;  //(7) OK and fixes 2
b->next = a_next;  //(8) ERROR b->next = b

所以有两个语句(5 和 8)中断了。首先,应该他们是什么?

a->prev = b;
b->prev = a;

你还可以看到,如果A和B颠倒了(第二种情况),那么就会出现相反的问题(5和8会坏;6和7会固定1和4;2和3会好的)。

如果你想要紧凑,你可以用三元运算符扩展这最后四个语句。不过,它可能会让您有点头疼:

a->prev = (b_prev == a ? b : b_prev);  //(5)
a->next = (b_next == a ? b : b_next);  //(6)
b->prev = (a_prev == b ? a : a_prev);  //(7)
b->next = (a_next == b ? a : a_next);  //(8)

要挤出最后一个分支,您可以在没有三元运算符的情况下重新排列这四个语句(因为一半的测试是多余的)。或者,您可以重写语句以使用两个测试(而不是我为对称性编写的四个测试)并依靠编译器进行优化。

我可能不会打扰,但你也可以展开整个事情:

if (a_next == b)
{
    a->prev = b;       //(5)
    a->next = b_next;  //(6)
    b->prev = a_prev;  //(7)
    b->next = a;       //(8)
}
else if (b_next == a)
{
    a->prev = b_prev;  //(5)
    a->next = b;       //(6)
    b->prev = a;       //(7)
    b->next = a_next;  //(8)
}
else
{
    a->prev = b_prev;  //(5)
    a->next = b_next;  //(6)
    b->prev = a_prev;  //(7)
    b->next = a_next;  //(8)
}

您的 swap() 中的问题是 x4 和 y4 ,它们都具有 NULL 值,并且您正试图取消对这两个指针的引用,这会导致分段错误。