(C/C++学习)18.C语言双向链表

说明:数组提供了连续内存空间的访问和使用,而链表是对内存零碎空间的有效组织和使用。链表又分为单向链表和双向链表,单向链表仅提供了链表的单方向访问,相比之下,双向链表则显得十分方便。


一.单向链表的节点

如下代码所示,双向链表的节点包含两个指向关系和一个数据空间,两个指向分别连接该节点的上一个和下一个节点,数据类型可以是一个结构体类型,也可以是其他类型。

  1 typedef struct node
  2 {
  3   int data;
  4   struct node *pre;
  5   struct node *next;
  6 }Node;



二.双向链表的创建

头插法和尾插法只看代码比较难以与理解,建议画出插入节点时的链表变化图则会非常容易理解,另外,个人认为头插法较优,因为其插入逻辑一步到位,不像尾插法还需在后面完成双向链表的环状连接。

1.头插法

  1 Node * creatList()
  2 {
  3    Node * head = (Node*)malloc(sizeof(Node));
  4    Node * cur = NULL;
  5    head->next = head;
  6    head->pre = head;
  7    int data;
  8    scanf("%d",&data);
  9    while(data)
 10      {
 11        cur = (Node*)malloc(sizeof(Node));
 12        cur->data = data;
 13        cur->next = head->next;
 14        cur->pre = head;
 15        head->next = cur;
 16        cur->next->pre = cur;
 17        scanf("%d",&data);
 18      }
 19   return head;
 20 }


2.尾插法

  1 Node * creatList()
  2 {
  3   Node * head = (Node*)malloc(sizeof(Node));
  4   Node * phead = head;
  5   Node * cur = NULL;
  6   phead->next = NULL;
  7   phead->pre = NULL;
  8   int data;
  9   scanf("%d",&data);
 10   while(data)
 11   {
 12      cur = (Node*)malloc(sizeof(Node));
 13      cur->data = data;
 14      phead->next = cur;
 15      cur->pre = phead;
 16      phead = cur;
 17      scanf("%d",&data);
 18   }
 19 //完成回环
 20   cur->next = head;
 21   head->pre = cur;
 22   return head;
 23 }



三.双向链表的元素插入(头插法)

插入方法:先让插入的节点有所指向,再考虑指向它的指针,具体插入如下:

  1 void insertList(Node * head)
  2 {
  3    printf("intsert new node:");
  4    int data;
  5    scanf("%d",&data);
  6    Node * cur;
  7    cur = (Node*)malloc(sizeof(Node));
  8    cur->data = data;
  9    cur->next = head->next;
 10    cur->pre = head;
 11    head->next = cur;
 12    cur->next->pre = cur;
 13 }



四.双向链表的查找

既然是双向链表,其查找方式也应该是是双向查找,比起单向链表的单向查找,双向查找同时从两个方向进行查找,加快了查找的速度。

  1 Node * searchList(Node * head,int find)
  2 {
  3    Node * pClock = head->next;
  4    Node * pAntiClock = head->pre;
  5    while (pAntiClock != pClock->pre)
  6    {
  7        if(pClock->data == find)
  8        return pClock;
  9        if(pAntiClock->data == find)
 10        return pAntiClock;
 11        if(pClock == pAntiClock)
 12        return NULL;
 13        pClock = pClock->next;
 14        pAntiClock = pAntiClock->pre;
 15 }
 16    return NULL;
 17 }



五.删除某个节点

  1 void deleteList(Node * pfind)
  2 {
  3    if(pfind == NULL)
  4    return;
  5    else
  6     {
  7       pfind->pre->next = pfind->next;
  8       pfind->next->pre = pfind->pre;
  9       free(pfind);
 10     }
 11 }



六.链表排序(按值排序)

  1 void sortList(Node * head,int n)
  2 {
  3     Node * p,*q;
  4     for(int i=0; i<n-1; i++)
  5     {
  6       p = head->next;
  7       q = p->next;
  8       for(int j=0; j<n-1-i; j++)
  9       {
 10         if(p->data > q->data)
 11         {
 12           p->data = p->data ^ q->data;
 13           q->data = p->data ^ q->data;
 14           p->data = p->data ^ q->data;
 15         }
 16       p = p->next;
 17       q = q->next;
 18       }
 19     }
 20 }



七.链表的销毁

  1 void destroyList(Node * head)
  2 {
  3    head->pre->next = NULL;
  4    Node * pre = head;
  5    while(head != NULL)
  6    {
  7      head = head->next;
  8      free(pre);
  9      pre = head;
 10    }
 11 }


说明:链表的操作还有如求长度、打印以及交换指针排序等,由于相对较为简单,此处就不再例举。重要提醒:链表的某些操作较难理解,最好画图加以辅助理解。

原文地址:https://www.cnblogs.com/tuihou/p/10004441.html