循环链表简单介绍

循环链表

1、循环链表长什么样

1

​ 将单链表中终端结点的指针端自空指针改为指向头结点,就使整个单链表形成 个环,这种头尾相接的单链表称 单循环链表,简称循环链表

​ 如图,就是尾巴指向头结点

2、有什么用?

​ 当需要访问最后一个节点时,需要遍历整个单链表,有没有可能用 0(1) 的时间由链表指针访问到最后一个结点呢?当然可以。只需要在最后一个结点的指针域指向头结点就行,

​ 终端结点用尾指针 rear 示,则查找终端结点是 0(1), 而开始结点其实就是 rear->next->next ,其时间复杂也为 0(1)

2

3、实现

3.1实现概览

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

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef struct Node
{
    int data;
    struct Node *next;
} Node, *LinkList;

int createListTail(LinkList *L, int n);
int listInsert(LinkList *L, int i, int e);
int listDelete(LinkList *L, int i, int *e);
int listLength(LinkList L);
void traverse(LinkList L);
LinkList mergeTwoList(LinkList *A, LinkList *B);

3.2尾插法创建循环链表

/* 尾插法创建n个结点的双向链表 */
int createListTail(LinkList *L, int n)
{
    LinkList p, q;
    (*L) = (LinkList)malloc(sizeof(Node));
    p = (*L);
    for (int i = 1; i <= n; i++)
    {
        q = (LinkList)malloc(sizeof(Node));
        q->data = i;
        p->next = q;
        p = q;
    }

    p->next = (*L);//尾结点的指针域指向了头结点(*L是头结点,L存放*L的地址,是头指针)
    return OK;
}

3.3插入元素

/* 双向链表插入, 在第i个结点 前 插入元素值为e的结点 */
int listInsert(LinkList *L, int i, int e)
{
    LinkList p = (*L), q;
    int j = 1;
    while (p && j < i)
    {
        p = p->next;
        j++;
    }

    if (!p || j > i)
        return ERROR;
    q = (LinkList)malloc(sizeof(Node));
    q->data = e;
    q->next = p->next;
    p->next = q;

    return OK;
}

3.4删除元素

/* 双向链表删除第i个位置的结点,并用e返回删除的结点值 */
int listDelete(LinkList *L, int i, int *e)
{
    LinkList p = (*L), temp;
    int j = 1;
    while ((p->next) && j < i)
    {
        p = p->next;
        j++;
    }

    if (!(p->next) || j > i)
        return ERROR;

    temp = p->next; // temp存放待删除的结点
    p->next = temp->next;
    *e = temp->data;
    free(temp);

    return OK;
}

注意:这里的插入删除都和单链表一样

3.5合并两个循环链表

引用的是大话数据结构的图

3

/* 以A为头结点,将两个双向链表A B合并,返回合并后的双向链表A */
LinkList mergeTwoList(LinkList *A, LinkList *B)
{
    LinkList p = (*A)->next, tailA, tailB;
    //找到A链表的最后一个结点
    while (p->next != *A) //p->next != *A,当p->next == *A时, 说明此时p结点是最后一个结点
        p = p->next;
    tailA = p;// 保存A的尾结点

    p = (*B)->next;
    while (p->next != *B)
        p = p->next;
    tailB = p;// 保存B的尾结点

    LinkList headA, headB;
    headA = tailA->next; // 1,保存A的头结点
    headB = tailB->next; // 1,保存B的头结点

    tailA->next = headB->next; // 2, A的尾结点的指针域,指向了B的第一个的结点
    tailB->next = headA;       // 3, B的尾结点的指针域,指向了A的头结点

    return headA;//返回合并后的双向链表
}

3.6求循环链表的长度

/* 返回双向链表长度 */
int listLength(LinkList L)
{
    LinkList p = L->next;
    int k = 0;
    while (p)
    {
        k++;
        p = p->next;
    }

    return k;
}

3.7遍历双向链表

/* 遍历双向链表 */
void traverse(LinkList L)
{
    LinkList p = L->next;
    while (p != L)//为什么不是p->next != L?,p == L时,说明不满足条件,此时p是最后一个结点
    {
        printf("%d ", p->data);//先打印数据第一个结点的数据,所以放在p = p->next;前面
        p = p->next;
    }
}

4.完整代码

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

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef struct Node
{
    int data;
    struct Node *next;
} Node, *LinkList;

int createListTail(LinkList *L, int n);
int listInsert(LinkList *L, int i, int e);
int listDelete(LinkList *L, int i, int *e);
int listLength(LinkList L);
void traverse(LinkList L);
LinkList mergeTwoList(LinkList *A, LinkList *B);

/* 尾插法创建n个结点的双向链表 */
int createListTail(LinkList *L, int n)
{
    LinkList p, q;
    (*L) = (LinkList)malloc(sizeof(Node));
    p = (*L);
    for (int i = 1; i <= n; i++)
    {
        q = (LinkList)malloc(sizeof(Node));
        q->data = i;
        p->next = q;
        p = q;
    }

    p->next = (*L);//尾结点的指针域指向了头结点(*L是头结点,L存放*L的地址,是头指针)
    return OK;
}

/* 双向链表插入, 在第i个结点 前 插入元素值为e的结点 */
int listInsert(LinkList *L, int i, int e)
{
    LinkList p = (*L), q;
    int j = 1;
    while (p && j < i)
    {
        p = p->next;
        j++;
    }

    if (!p || j > i)
        return ERROR;
    q = (LinkList)malloc(sizeof(Node));
    q->data = e;
    q->next = p->next;
    p->next = q;

    return OK;
}

/* 双向链表删除第i个位置的结点,并用e返回删除的结点值 */
int listDelete(LinkList *L, int i, int *e)
{
    LinkList p = (*L), temp;
    int j = 1;
    while ((p->next) && j < i)
    {
        p = p->next;
        j++;
    }

    if (!(p->next) || j > i)
        return ERROR;

    temp = p->next; // temp存放待删除的结点
    p->next = temp->next;
    *e = temp->data;
    free(temp);

    return OK;
}

/* 以A为头结点,将两个双向链表A B合并,返回合并后的双向链表A */
LinkList mergeTwoList(LinkList *A, LinkList *B)
{
    LinkList p = (*A)->next, tailA, tailB;
    //找到A链表的最后一个结点
    while (p->next != *A) //p->next != *A,当p->next == *A时, 说明此时p结点是最后一个结点
        p = p->next;
    tailA = p;// 保存A的尾结点

    p = (*B)->next;
    while (p->next != *B)
        p = p->next;
    tailB = p;// 保存B的尾结点

    LinkList headA, headB;
    headA = tailA->next; // 1,保存A的头结点
    headB = tailB->next; // 1,保存B的头结点

    tailA->next = headB->next; // 2, A的尾结点的指针域,指向了B的第一个的结点
    tailB->next = headA;       // 3, B的尾结点的指针域,指向了A的头结点

    return headA;//返回合并后的双向链表
}

/* 返回双向链表长度 */
int listLength(LinkList L)
{
    LinkList p = L->next;
    int k = 0;
    while (p)
    {
        k++;
        p = p->next;
    }

    return k;
}

/* 遍历双向链表 */
void traverse(LinkList L)
{
    LinkList p = L->next;
    while (p != L)//为什么不是p->next != L?,p == L时,说明不满足条件,此时p是最后一个结点
    {
        printf("%d ", p->data);//先打印数据第一个结点的数据,所以放在p = p->next;前面
        p = p->next;
    }
}

int main()
{
    // LinkList L;
    // int e;
    // createListTail(&L, 5);
    // traverse(L);

    // // printf("\n");
    // // listInsert(&L, 6, 22);
    // // traverse(L);

    // printf("\n");
    // listDelete(&L, 5, &e);
    // traverse(L);

    LinkList A, B;
    createListTail(&A, 2);
    createListTail(&B, 2);

    LinkList mergeList = mergeTwoList(&A, &B);
    traverse(mergeList);


    // LinkList A, B;
    // createListTail(&A, 2);
    // traverse(A);

    return 0;
}

/ // traverse(L);

// printf("\n");
// listDelete(&L, 5, &e);
// traverse(L);

LinkList A, B;
createListTail(&A, 2);
createListTail(&B, 2);

LinkList mergeList = mergeTwoList(&A, &B);
traverse(mergeList);


// LinkList A, B;
// createListTail(&A, 2);
// traverse(A);

return 0;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值