【问题标题】:What is wrong with my logic to rotate a doubly linked list?我旋转双向链表的逻辑有什么问题?
【发布时间】:2020-11-23 14:12:45
【问题描述】:

给定一个双向链表,将链表逆时针旋转 P 个节点。这里 P 是给定的正整数,并且小于链表中的节点数 (N)。第一行包含测试用例的数量。每个测试用例由两行组成,一行指定 N 和 P,另一行指定值列表。

示例: 输入:

1
6 2
1 2 3 4 5 6

输出:

3 4 5 6 1 2

我已经编写了一个函数来进行给定的旋转。我不断收到运行时错误。官方问题定义可以参考https://practice.geeksforgeeks.org/problems/rotate-doubly-linked-list-by-p-nodes/1/

struct node *rotate(struct node *head){ 
    int num, count=1;
    struct node *current, *nnode;
    current=head;
    printf("\nEnter the number across which you wanna rotate: ");
    scanf("%d", &num);
    if(num==0){
        return;
    }
    while(count<num && current!=NULL){
        current=current->next;
        count++;
    } 
    if(current==NULL){
        return;
    }
    nnode=current;
    while(current->next!=NULL){
        current=current->next;
    }
    current->next=head;
    head->prev=current;
    head=nnode->next;
    head->prev=NULL;
    nnode->next=NULL;
    return head;
}

【问题讨论】:

  • 输入不清楚。例如第一行数字 1 是什么意思?
  • 并显示列表定义。
  • @Vlad:1 可能是“旋转测试次数”,所以后面只有两行。但这应该在问题中说。如果它说2,那么(根据我的假设),会有第二对线,一个是节点数和旋转,另一个是列表中的数字。
  • 您不应该在定义为返回值的函数中写入return;。你需要return NULL; 两次才能编译代码。
  • @JonathanLeffler 即使我删除了这些回报;语句,它仍然给我一个运行时错误。

标签: c data-structures linked-list doubly-linked-list


【解决方案1】:

除非您遇到线性列表,否则我建议您使用循环列表,即最后一个链接的 next 指向第一个链接的列表。您可以像使用任何其他“线性”列表一样使用它们;您只想检查循环遍历它们时是否返回到开始的位置,而不是测试是否到达NULL。创建链接时会遇到特殊情况,有时如果您有一个空列表,则在插入它们时会遇到特殊情况,但没有比其他情况更糟糕的了。 (如果您不需要像旋转一样移动头部,那么您可以使用虚拟节点来处理它,并且您可以摆脱非循环列表的许多特殊情况)。

循环列表的一些简单代码(没有虚拟链接,因此可以是NULL 的列表)如下所示:

struct link {
  int value;
  struct link *prev;
  struct link *next;
};

static inline
void connect_neighbours(struct link *x)
{
  x->next->prev = x;
  x->prev->next = x;
}
static inline
void link_after(struct link *x, struct link *y)
{
  y->prev = x;
  y->next = x->next;
  connect_neighbours(y);
}
#define link_before(x, y) \
  link_after((x)->prev, y)

struct link *new_link(int val,
                      struct link *prev,
                      struct link *next)
{
  struct link *link = malloc(sizeof *link);
  if (!link) return 0;

  link->value = val;
  if (prev == 0 || next == 0) {
    // make a link circular if we don't have prev/next
    link->next = link->prev = link;
  } else {
    // otherwise, just set the links
    link->prev = prev;
    link->next = next;
  }
  return link;
}

这是用于创建链接的代码,如果我们不提供指向链接的指针,则将 prevnext 链接到节点本身,以及在链接之前或之后放置新链接。 link_after()link_before() 操作当然会失败,如果你给它们NULL 指针,所以你不能使用它们,直到你有一个至少有一个链接的列表。

要遍历一个列表,例如打印它,你可以编写如下代码:

void print_list(struct link *head)
{
  if (!head) {
    printf("[]\n");
    return;
  }
  printf("[ ");
  printf("%d ", head->value);
  for (struct link *link = head->next;
       link != head;
       link = link->next) {
    printf("%d ", link->value);
  }
  printf("]\n");
}

for-条件,我们检查我们是否回到了头脑,是我们认识到我们已经完成的方式。空列表的特殊情况,以及处理循环外的第一个链接,如果你有一个虚拟链接也不是必需的,但是对于旋转操作,如果我们没有一个虚拟链接会更容易。如果我们不这样做,那么旋转就像按照nextprev 指针正确的次数一样简单:

struct link *rotate(struct link *x, int rot)
{
  if (rot > 0) {
    for (; rot; rot--) x = x->next;
  } else {
    for (; rot; rot++) x = x->prev;
  }
  return x;
}

使用上面的代码,您可以像这样创建和旋转列表:

int main(void)
{
  struct link *x = 0;
  print_list(x);

  // not checking allocation errors here!
  x = new_link(1, 0, 0);
  for (int i = 2; i <= 5; i++)
    link_before(x, new_link(i, 0, 0));
  print_list(x);

  print_list(rotate(x, 2));
  print_list(rotate(x, -2));

  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-05
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多