将一个双链表首尾相连的时候就是一个环链表,一一相连,循环不息

  1. 数据结构
typedef struct node
{
    int data;                                           //数据域
    struct node *next;                                  //指针域
    struct node *prep;
}NODE_t;

这里和双向链表是一致的,指针域包含前驱和后继

  1. 创建
NODE_t *CreatNodeList()
{
    NODE_t *head = NULL;

    head = (NODE_t *)malloc(sizeof(NODE_t));
    if(!head)
        exit(-1);
    head->next = head;
    head->prep = head;

    return head;
}

当创建空链时,将头节点的前驱和后继都指向自己

  1. 插入
int InsertNode(NODE_t *head,int data)
{
    NODE_t *cur = NULL;

    if(!head)
        exit(-1);

    cur = (NODE_t *)malloc(sizeof(NODE_t));
    if(!cur)
        exit(-1);

    cur->data = data;

    /*
        next->prev = new;
        new->next = next;
        new->prev = prev;
        prev->next = new;

        next ===> head->next;
        prev ===> head;
    */
    // 链表在插入时有一个规则,不易出错,就是让新产生的节点先有所指向
    cur->prep = head;
    cur->next = head->next;
    head->next->prep = cur;
    head->next = cur;

    return 0;
}
  1. 查找

    NODE_t *findNode(NODE_t *head,int data)
    {
    NODE_t *p = head->next;
    
    while( p != head)
    {
        if(p->data == data)
        {
            break;
        }
        p = p->next;
    }
    if(head == p)
    {
        printf("sorry,%d is not in the list\n",data);
        return NULL;
    }
    
    return p;
    }
  2. 删除

    int DeleteNodeOfList(NODE_t *pfind)
    {
    
    pfind->prep->next = pfind->next;
    pfind->next->prep = pfind->prep;
    
    free(pfind);
    pfind = NULL;
    
    return 0;
    }

    6,修改

    int UpdateNode(NODE_t *head,int olddata,int newdata)
    {
    NODE_t *p = findNode(head,olddata);
    if(p)
    {
        p->data = newdata;
    }
    
    return 0;
    }

    只要找得到需要操作的指针,剩下的就是操纵数据域的问题

  3. 遍历打印
void showList(NODE_t *head)
{
    NODE_t *p = NULL;
    p = head->next;

    while(p != head)
    {
        printf("%d ==> ",p->data);
        p = p->next;
    }
    printf("end..\n");
}

当再回到头结点时,意味着遍历了一遍,此处可以结束了

  1. 得到list的长度
int ListNodeLen(NODE_t *head)
{
    int len = 0;
    NODE_t *p = NULL;

    p = head->next;
    while(p!=head)
    {
        len++;
        p = p->next;
    }

    return len;
}

9.排序

int sortList(NODE_t *head)
{
    int i = 0,j = 0;
    int listlen = 0;
    int tmpData = 0;
    NODE_t *p = NULL;

    // 使用冒泡排序,不动指针域,比较数据域,使用临时变量,将有大小之别的节点的数据域交换
    // 得到链表长度,方便冒泡
    listlen = ListNodeLen(head);

    // 指到首节点
    p = head->next;
    for(i = 0;i < listlen-1;i++)
    {
        // 每一轮从头开始
        p = head->next;
        for(j = 0;j<listlen - i-1;j++)
        {
            // 将小值排在前面
            if(p->data > p->next->data)
            {
                tmpData = p->data;
                p->data = p->next->data;
                p->next->data = tmpData;
            }
            p = p->next;
        }
    }

    return 0;
}

约瑟夫问题:
所有人围成一圈
顺时针报数,每次报到q的人将被杀掉
被杀掉的人将从房间内被移走
然后从被杀掉的下一个人重新报数,继续报q,再清除,直到剩余一人

这个问题可以用环链解决,当符合条件的结点删除,直到最后一个结点
假设每次报数报3的人杀掉

    NODE_t *p = NULL;
    int cnt = 0;
    p = head->next;
    while(1)
    {
        // 头节点无效,指向下一个节点,继续循环
        if(p == head)
        {
            p = p->next;
            continue;
        }
        printf("ID:%d 报数 %d\n",p->data,++cnt);

        if(cnt == 3)
        {                                       // 报数为3的人,杀掉
            cnt = 0;
            NODE_t *ptemp = p->next;            // 杀掉后,后续节点将无法找到,所以需要记录后续节点
            printf("will kill id %d\n",p->data);
            DeleteNodeOfList(p);
            p = ptemp;
            showList(head);

        }
        else
        {                                       // 不杀人的情况,继续报数
            if(ListNodeLen(head) != 1)
            {
                p = p->next;
            }
            else                                // 杀至最后一个人,游戏结束
            {
                break;
            }
        }
//        sleep(1);
    }
    printf("the Index of people who can survive is %d\n",p->data);

有时候想,这些数学问题充满了残忍,比如说小学数学的启蒙就是从打鸟开始,这个有是排成一圈杀人,包括好多医学书籍,不知道为了一本书后面充满了多少残忍和血腥。。。。

链表还是很有用的,比如看linux驱动代码就可以看到,有好多数据结构中包含一个 struct list_head head;这种数据结构,其实内核大神们将链表的指针域单独封装出来,然后,讲个东东包到任意一个结构体内,这个结构体就变成一个环链,这个可以参考 linux/list.h头文件,可以看到人家实现的很牛逼,大道致简,很简单的抽象封装,组合起来,造成了一个操作系统的底层数据结构,值得学习