数据结构4-循环链表与双向链表

1.基本概念

需要注意的是,虽然循环链表成环状,但本质上还是链表,因此在循环链表中,依然能够找到头指针和首元节点等。循环链表和普通链表相比,唯一的不同就是循环链表首尾相连,其他都完全一样。

当表中添加或删除数据元素时,你只需要通过 mallocfree 函数来申请或释放空间即可,实现起来比较简单。

linklist.h

#ifndef LINKLIST_H__
#define LINKLIST_H__

#include <stdio.h>

extern struct node *tail;
extern struct node *head;         //main.c 也要使用 head tail
//节点结构体
struct node
{
	unsigned int elem;
	struct node *next;
};  //注意;

//函数声明
void create_list(unsigned int elem);              //节点的创建
void insert_node(int pos, unsigned int elem);     //插入节点
void delete_node(int pos);                        //删除节点
void print_linklist(void);                        //输出链表
int search(unsigned int elem);                    //查询链表中的某个数值 

#endif

linklist.c

#include "linklist.h"
#include <stdlib.h>


struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针 

//节点的创建
void create_list(unsigned int elem)
{
      struct node *p = (struct node *)malloc(sizeof(struct node));
      p->elem = elem;
      p->next = NULL;
    
      if(head == NULL)       // 第一个节点
            head = p;          
      else
            tail->next = p;   
      tail = p;
      tail->next = head;     //尾节点指针指向头节点               
}


// 插入节点
void insert_node(int pos, unsigned int elem)
{
      struct node *pre; // 前趋点
      pre = head;       // 前趋点从头开始
      int i = 0;
      struct node *p = (struct node *)malloc(sizeof(struct node));  // 创建节点
      
      if(pos == 0)            //如果更新头点节点
      {
             p->elem = elem;
             p->next = head;
             head = p;
             tail->next = head; // 更新尾节点指向的头节点
      }
      else
      { 
            while(i < pos - 1)
            {
                  pre = pre->next;   // pre是一个指针,pre->next是该指针所指向结构的一个成员
                  i++;
            }  
            p->elem = elem;         // 新节点数据域填充
            p->next = pre->next;    // 新节点指向的位置为前驱点原来指向位置
            pre->next = p;          // 前驱点指向的位置变成新节点
            
            if(p->next == head)  // 尾节点
                    tail = p;
      }    
}

//删除节点
void delete_node(int pos)
{
         struct node *pre, *p; // 前趋点
         pre = head;           // 前趋点从头开始
         int i = 0;
         
         if(pos == 0)         // 如果删除头结点
         {
              head = head->next;
              tail->next = head;
              free(pre);
         }
         else
         {
              while(i < pos-1)       //更新前趋点直到要删除的前一个
              {
                  pre = pre->next;
                  i++;
              }
         
              p = pre->next;
              pre->next = p->next;
              if(p->next == head)   //如果删除尾节点
                  tail = pre;
              free(p);
              
         }
}

//输出链表
void print_linklist(void)   // 结构体型指针
{
        struct node *p;
        p = head; // 循环的初始条件
        do{   
            printf("%5d", p->elem);
            p = p->next;  
        }while(p != head);
        
        printf("\n");
}

//查询链表中的某个数值 
int search(unsigned int elem)
{
        struct node *p;
        p = head; // 循环的初始条件
        do{   
             if(p->elem == elem)
                   return 1;
             p = p->next;
        }while(p != head);
        
        return 0;
}

main.c

#include <stdio.h>
#include "linklist.h"


int main(void)
{ 
         int n;
         // 创建链表1
         create_list(1);
         create_list(2);
         create_list(3);
         create_list(4);
         create_list(5);
         create_list(6);
         create_list(7);
         create_list(8);
         create_list(9);
         create_list(10);
         create_list(11);
         create_list(12);
         print_linklist();

         insert_node(0, 11);
         print_linklist();

         delete_node(6);
         print_linklist();
         
         delete_node(0);
         print_linklist();

         search(12);
         printf("\n");

         return 0;
}

2.循环链表的应用-约瑟夫环

约瑟夫问题是个有名的问题:已知n个人(编号1~n表示)围坐在一张圆桌周围,从编号为k的人从1开始报数,数到m的那个人出列,他的下一个人继续从1开始报数,数到m的人继续出列,依次重复下去,直到所有人全部出列。

linklist.h

#ifndef LINKLIST_H__
#define LINKLIST_H__

#include <stdio.h>

extern struct node *tail;
extern struct node *head;         // main.c 也要使用 head tail
//节点结构体
struct node
{
	unsigned int elem;
	struct node *next;
};  //注意;

//函数声明
void create_list(unsigned int elem);              //节点的创建
void insert_node(int pos, unsigned int elem);     //插入节点
void delete_node(int pos);                        //删除节点
void print_linklist(void);                        //输出链表
int search(unsigned int elem);                    //查询链表中的某个数值 

#endif

linklist.c

#include "linklist.h"
#include <stdlib.h>


struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针 

//节点的创建
void create_list(unsigned int elem)
{
      struct node *p = (struct node *)malloc(sizeof(struct node));
      p->elem = elem;
      p->next = NULL;
    
      if(head == NULL)       // 第一个节点
            head = p;          
      else
            tail->next = p;   
      tail = p;
      tail->next = head;     //尾节点指针指向头节点               
}


// 插入节点
void insert_node(int pos, unsigned int elem)
{
      struct node *pre; // 前趋点
      pre = head;       // 前趋点从头开始
      int i = 0;
      struct node *p = (struct node *)malloc(sizeof(struct node));  // 创建节点
      
      if(pos == 0)            //如果更新头点节点
      {
             p->elem = elem;
             p->next = head;
             head = p;
             tail->next = head; // 更新尾节点指向的头节点
      }
      else
      { 
            while(i < pos - 1)
            {
                  pre = pre->next;   // pre是一个指针,pre->next是该指针所指向结构的一个成员
                  i++;
            }  
            p->elem = elem;         // 新节点数据域填充
            p->next = pre->next;    // 新节点指向的位置为前驱点原来指向位置
            pre->next = p;          // 前驱点指向的位置变成新节点
            
            if(p->next == head)  // 尾节点
                    tail = p;
      }    
}

//删除节点
void delete_node(int pos)
{
         struct node *pre, *p; // 前趋点
         pre = head;           // 前趋点从头开始
         int i = 0;
         
         if(pos == 0)         // 如果删除头结点
         {
              head = head->next;
              tail->next = head;
              free(pre);
         }
         else
         {
              while(i < pos-1)       //更新前趋点直到要删除的前一个
              {
                  pre = pre->next;
                  i++;
              }
         
              p = pre->next;
              pre->next = p->next;
              if(p->next == head)   //如果删除尾节点
                  tail = pre;
              free(p);
              
         }
}

//输出链表
void print_linklist(void)   // 结构体型指针
{
        struct node *p;
        p = head; // 循环的初始条件
        do{   
            printf("%5d", p->elem);
            p = p->next;  
        }while(p != head);
        
        printf("\n");
}

//查询链表中的某个数值 
int search(unsigned int elem)
{
        struct node *p;
        p = head; // 循环的初始条件
        do{   
             if(p->elem == elem)
                   return 1;
             p = p->next;
        }while(p != head);
        
        return 0;
}

main.c 

#include <stdio.h>
#include "linklist.h"
#include <stdlib.h>

int main(void)
{ 
         int n, k, m;
         int i;
         struct node *p;  
         struct node *q;

         printf("please enter the number of person:");
         scanf("%d", &n);
          
         for(i = 1; i <= n; i++)
         {
             create_list(i);    
         }
         print_linklist();
         p = head;
            
         printf("please enter the start number:");
         scanf("%d", &k);
   
         while(--k)
         {
            p = p->next;
         }
      
         printf("please enter the m:");
         scanf("%d", &m);
         
         if(1 == m)
         {
              for(i = 0; i < n; i++)
              {
                  printf("%3d", p->elem);
                  p = p->next; 
              }
              printf("\n");
         }
         else
         {
               while(n--)   // 
               {
                      for(i = 1; i < m - 1; i++)     // 找前驱点
                          p = p->next;
                      q = p;
                      p = p->next;
                      printf("%3d", p->elem);
                      q->next = p->next;
                      //free(p);
                      p = p->next; 
               }
               printf("\n");
               return 0;
         }
        
}

3.双向链表

从名字上理解双向链表,即链表是 "双向" 的,如 1 所示:

“双向”指的是各节点之间的逻辑关系是双向的,头指针通常只设置一个。
从图 1 中可以看到,双向链表中各节点包含以下 3 部分信息(如图 2 所示):

1.指针域:用于指向当前节点的直接前驱节点;
2.数据域:用于存储数据元素。
3.指针域:用于指向当前节点的直接后继节点;

linklist.h

#ifndef LINKLIST_H__
#define LINKLIST_H__

#include <stdio.h>

extern struct node *tail;
extern struct node *head;         // main.c 也要使用 head tail

//节点结构体
struct node
{
	unsigned int elem;
        struct node *pre;     // 前驱点地址
	struct node *next;    // 下一个地址
};  //注意;

//函数声明
void create_list(unsigned int elem);              //节点的创建
void insert_node(int pos, unsigned int elem);     //插入节点
void delete_node(int pos);                        //删除节点
void print_linklist(void);                        //输出链表
int search(unsigned int elem);                    //查询链表中的某个数值 

#endif

linklist.c

#include "linklist.h"
#include <stdlib.h>


struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针 

//节点的创建
void create_list(unsigned int elem)
{
      struct node *p = (struct node *)malloc(sizeof(struct node));
      p->elem = elem;
      p->pre = NULL;
      p->next = NULL;
    
      if(head == NULL)        // 第一个节点,指针都是NULL
            head = p;          
      else
      {
            tail->next = p;   // 
            p->pre = tail;
      }
      tail = p;  
                
}

// 插入节点
void insert_node(int pos, unsigned int elem)
{
      struct node *pre;  // 前趋点
      pre = head;        // 前趋点从头开始
      int i = 0;
      struct node *p = (struct node *)malloc(sizeof(struct node));  // 创建节点
      
      if(pos == 0)            //如果更新双向链表的头节点
      {
             p->elem = elem;
             p->next = head;
             head->pre = p;
             p->pre = NULL;
             head = p;
      }
      else
      { 
            while(i < pos - 1)
            {
                  pre = pre->next;   // pre是一个指针,pre->next是该指针所指向结构的一个成员
                  i++;
            }  
            p->elem = elem;         // 新节点数据域填充
            p->pre = pre;
            p->next = pre->next;    // 新节点指向的位置为前驱点原来指向位置
            if(p->next != NULL)
                    pre->next->pre = p;
            pre->next = p;          // 前驱点指向的位置变成新节点
            if(p->next == NULL)
            {
                   tail = p; 
            }          

      }    
}

//删除节点
void delete_node(int pos)
{
         struct node *pre, *p; // 前趋点
         pre = head;           // 前趋点从头开始
         int i = 0;
         
         if(pos == 0)         // 如果删除头结点
         {
              head = head->next;
              head->pre = NULL;  // 新头的前驱点为空
              free(pre);
         }
         else
         {
              while(i < pos-1)       //更新前趋点直到要删除的前一个
              {
                  pre = pre->next;
                  i++;
              }
         
              p = pre->next;
              pre->next = p->next;

              if(p->next != NULL)
                   p->next->pre = pre;
              else                 //如果删除尾节点
                   tail = pre;
              free(p);             
         }
}

//输出链表
void print_linklist(void)   // 结构体型指针
{
        struct node *p;
        for(p = head; p; p = p->next)
                   printf("%5d", p->elem);
        printf("\n");
}

//查询链表中的某个数值 
int search(unsigned int elem)
{
        struct node *p;
        for(p = head; p; p = p->next)
              if(p->elem == elem)
                 return 1;      
        return 0;
}

main.c

#include <stdio.h>
#include "linklist.h"


int main(void)
{ 
         int n;
         // 创建链表1
         create_list(1);
         create_list(2);
         create_list(3);
         create_list(4);
         create_list(5);
         create_list(6);
         create_list(7);
         create_list(8);
         create_list(9);
         create_list(10);
         create_list(11);
         create_list(12);
         print_linklist();

         insert_node(0, 11);
         print_linklist();

         delete_node(6);
         print_linklist();
         
         delete_node(0);
         print_linklist();

         search(12);
         printf("\n");

         return 0;
}

4.双向链表的应用-逆置链表

linklist.h

#ifndef LINKLIST_H__
#define LINKLIST_H__

#include <stdio.h>

extern struct node *tail;
extern struct node *head;         // main.c 也要使用 head tail

//节点结构体
struct node
{
	unsigned int elem;
        struct node *pre;     // 前驱点地址
	struct node *next;    // 下一个地址
};  //注意;

//函数声明
void create_list(unsigned int elem);              //节点的创建
void insert_node(int pos, unsigned int elem);     //插入节点
void delete_node(int pos);                        //删除节点
void print_linklist(void);                        //输出链表
int search(unsigned int elem);                    //查询链表中的某个数值 
void reverse_printf_linklist(void);               //逆打印
#endif

linklist.c

#include "linklist.h"
#include <stdlib.h>


struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针 

//节点的创建
void create_list(unsigned int elem)
{
      struct node *p = (struct node *)malloc(sizeof(struct node));
      p->elem = elem;
      p->pre = NULL;
      p->next = NULL;
    
      if(head == NULL)        // 第一个节点,指针都是NULL
            head = p;          
      else
      {
            tail->next = p;   // 
            p->pre = tail;
      }
      tail = p;  
                
}

// 插入节点
void insert_node(int pos, unsigned int elem)
{
      struct node *pre;  // 前趋点
      pre = head;        // 前趋点从头开始
      int i = 0;
      struct node *p = (struct node *)malloc(sizeof(struct node));  // 创建节点
      
      if(pos == 0)            //如果更新双向链表的头节点
      {
             p->elem = elem;
             p->next = head;
             head->pre = p;
             p->pre = NULL;
             head = p;
      }
      else
      { 
            while(i < pos - 1)
            {
                  pre = pre->next;   // pre是一个指针,pre->next是该指针所指向结构的一个成员
                  i++;
            }  
            p->elem = elem;         // 新节点数据域填充
            p->pre = pre;
            p->next = pre->next;    // 新节点指向的位置为前驱点原来指向位置
            if(p->next != NULL)
                    pre->next->pre = p;
            pre->next = p;          // 前驱点指向的位置变成新节点
            if(p->next == NULL)
            {
                   tail = p; 
            }          

      }    
}

//删除节点
void delete_node(int pos)
{
         struct node *pre, *p; // 前趋点
         pre = head;           // 前趋点从头开始
         int i = 0;
         
         if(pos == 0)         // 如果删除头结点
         {
              head = head->next;
              head->pre = NULL;  // 新头的前驱点为空
              free(pre);
         }
         else
         {
              while(i < pos-1)       //更新前趋点直到要删除的前一个
              {
                  pre = pre->next;
                  i++;
              }
         
              p = pre->next;
              pre->next = p->next;

              if(p->next != NULL)
                   p->next->pre = pre;
              else                 //如果删除尾节点
                   tail = pre;
              free(p);             
         }
}

//输出链表
void print_linklist(void)   // 结构体型指针
{
        struct node *p;
        for(p = head; p; p = p->next)
                   printf("%5d", p->elem);
        printf("\n");
}

//查询链表中的某个数值 
int search(unsigned int elem)
{
        struct node *p;
        for(p = head; p; p = p->next)
              if(p->elem == elem)
                 return 1;      
        return 0;
}


void reverse_printf_linklist(void)
{
       struct node *p;
       for(p = tail; p; p = p->pre)   //直到p->pre = NULL
          printf("%5d", p->elem);
       printf("\n");
}

main.c

#include <stdio.h>
#include "linklist.h"


int main(void)
{ 
         int n;
         // 创建链表1
         create_list(1);
         create_list(2);
         create_list(3);
         create_list(4);
         create_list(5);
         create_list(6);
         create_list(7);
         create_list(8);
         create_list(9);
         create_list(10);
         create_list(11);
         create_list(12);
         print_linklist();

         reverse_printf_linklist();

         return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44177768/article/details/127422997