在C中反转双链表的元素

Posted

技术标签:

【中文标题】在C中反转双链表的元素【英文标题】:Reversing the elements of a double linked list in C 【发布时间】:2013-12-16 11:21:18 【问题描述】:

这是我的方法——在列表的开头保留一个指针,在列表的末尾保留一个指针。将头指针向前推进,将尾指针向后推进,直到它们指向相同。 在转发之前交换值。该函数在调用时会抛出分段错误。为什么会这样? 这是我的列表节点结构

struct dll

 int number;
 struct dll *next;
 struct dll *prev;
;

这是我的反转列表和主函数

    int count=0;
void reverse(struct dll **root)

 int temp;
 struct dll *tail=NULL;
 struct dll *temproot=NULL;
 temproot =(*root);
 for(;(temproot)->next !=NULL;(temproot)=(temproot)->next); //traversing to the end
 tail= temproot;
 while(((*root)->next != tail->prev) || ((*root)->next != tail))//one for even no. of nodes and one for odd no.of nodes
 
  temp=(*root)->number; //swapping the numbers
  (*root)->number=tail->number;
  tail->number=temp;
  (*root)=(*root)->next;
  tail=tail->prev;
  //even after they are same, values need to be changed one last time
   temp=(*root)->number;
  (*root)->number=tail->number;
  tail->number=temp;
  (*root)=(*root)->next;
  tail=tail->prev;

void insert(struct dll **root,int num)

 struct dll *temp;
 if(count==0)
 
  if((*root)==NULL)  
  
  (*root)=(struct dll *)malloc(sizeof(struct dll));
  (*root)->next=NULL;
  (*root)->prev=NULL;
  (*root)->number=num;
  count++;
  printf("\n%d",count);
  
 
 else if((*root)->next==NULL) 
  
  temp=(struct dll *)malloc(sizeof(struct dll));
  temp->next=NULL;
  temp->prev=(*root);
  temp->number=num;
  (*root)->next=temp;
  count++;
  printf("\n%d",count);
  
 else
 
  insert(&(*root)->next,num);
 


main()

 struct dll *head=NULL;
 int i,n,num;
 while(1)
 
  printf("Enter 1 for insert, 3 for reverse, 0 for exit\n");
  scanf("%d",&n);
  switch(n)
  
   case 1:printf("Enter number\n");
      scanf("%d",&num);
      insert(&head,num);
      break;
   case 3:reverse(&head);break;
   case 0:exit(0);
   default:printf("Enter correct value\n");
  
 


【问题讨论】:

段错误在哪一行? 我是初学者,如何查看segfault在哪一行? 最好的方法是在调试器中运行它。我想如果你现在不想学习如何使用调试器,你甚至可以在每一行后面加上一个printf,看看哪个是最后打印的。 你需要给一个小的完整的复制器。所以有人可以在不做任何修改的情况下编译和运行。问题可能不在上面的代码中,而是在其他地方。 我在调试器中运行它。它在 while 语句中显示分段错误 【参考方案1】:

通过颠倒列表的顺序,您的意思是交换所有 item 的 next 和 prev 指针吗?这段未经检查的代码可能会奏效:

void reverse(struct dll **root)

  struct dll *buff=NULL;
  struct dll *start=*root;

  unsigned char stop=0;
  while(stop==0)
   if(*root==NULL)
    stop=1;
    break;
   
   buff=(*root)->prev;
   (*root)->prev=(*root)->next;
   (*root)->next=buff;

   *root=(*root)->prev;//the next to treat is the previous one now
   if(*root==start)
    //this test handle the case of "looping" double linked list
    stop=1;
    break;
  
 

此代码假定 *root 是列表的开头。最后,*root 仍然是列表的开头。

再见,

弗朗西斯

【讨论】:

感谢您的回答。虽然,这是一个更好的逻辑,但我使用的是不同的逻辑,如问题中所述。在我的逻辑中,列表的链接将相同,root 将相同。它只是交换数字,就像一个数组 正如我在回答中指出的那样,*root 在您的程序运行后将相同。【参考方案2】:

很难判断这段代码在哪里遇到了分段错误。但是,如果您的调试器说它在编写 while 语句的那一行引发了错误:

 while(((*root)->next != tail->prev) || ((*root)->next != tail))//one for even no. of nodes and one for odd no.of nodes

那么几乎可以肯定,root*roottail 在此时为 NULL 或未初始化的指针。

一种可能性:如果你用一个只包含一个元素的链表调用这个函数,那么tail*root 将在这个循环的开始指向同一个元素。这个条件的编写方式,程序会认为它需要继续反转元素,并将继续向后移动tail 和向前移动*root。由于程序从不检查是*root == NULL 还是tail == NULL,最终它会直接从列表的末尾走出来并尝试取消引用NULL 指针,这将导致分段错误。

在移动指针和解除引用之间检查指针是否为 NULL 是一个非常好的主意。考虑一下如果调用一个包含 0 个元素的列表、包含 1 个元素的列表等的函数会发生什么,这通常也很有帮助。

【讨论】:

【参考方案3】:

首先,我必须真的坚持你使用哨兵节点 对于您的链表,它大大简化了代码

一旦你转换为使用哨兵节点,创建reverse 变得相当琐碎:

struct dll* prev = sentry;

struct dll* curr = sentry->next;
if(curr==sentry) return; // empty list dont need reversing

struct dll* next = curr->next;
if(next==sentry) return; // list size 1 is already its reverse

while( curr != sentry )

    // reverse links
    prev->prev = curr;
    curr->next = prev;
    // move to next
    prev = curr;
    curr = next;
    next = next->next;


// close loop (first item is the one we found last)
sentry->next = prev;
prev->prev = sentry;

如果你需要一个哨兵节点如何工作的例子,你可以看here

【讨论】:

【参考方案4】:

我编写了这个函数,用递归方法反转双链表。

void Node::Swap()
 if(m_pNext != NULL)
   m_pNext->Swap();
   m_pNext->m_pNext = this;
 


void List::Reverse()
  if(m_pHead!=NULL)
    m_pHead->Swap();
    m_pHead->SetNext(NULL);
  
  Node* pTemp=m_pHead;
  m_pHead = m_pTail;
  m_pTail = pTemp;

【讨论】:

【参考方案5】:

结构节点

         int data;


         struct node *prev;


         struct node *next;


       ;

typedef 结构节点 *list; //只是使用 typedef 来缩短内容

list temp=NULL,head=NULL,last=NULL,r=NULL,p=NULL;

/这里使用了创建函数,所以 last 和 head 现在分别指向最后一个节点和第一个节点/

我的倒车函数(可能是最短的)=>

最后一个=头;

r=头;

而(r!=NULL)

  temp=r->next;


  r->next=r->prev;


  r->prev=temp;  //swapping next and previous using temp


  r=r->prev; //since now values swapped so reverse traversing

p=最后一个;

while(p->prev!=NULL)

  p=p->prev;

头=p;

【讨论】:

以上是关于在C中反转双链表的元素的主要内容,如果未能解决你的问题,请参考以下文章

有序的双链表的实现

[每日算法220508] 单链表和双链表的反转

[每日算法220508] 单链表和双链表的反转

反转单链表和双链表

深度解析数组单链表和双链表

数据结构之双链表