线性表-单向循环链表

555 阅读4分钟
  • 单向循环链表基础

    • 概念

    循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。

    与单链表的区别在于尾结点的指针不是指向NULL,而是指向头结点或者是首元结点

    • 图解
  • 单向循环链表的初始化以及赋值

#define OK 1
#define ERROR 0

typedef int Status;
typedef int ElemType;

typedef struct Node {
    ElemType data;
    struct Node * next;
} Node;

typedef struct Node *LinkList;
//单向循环链表操作
Status createLinkList(LinkList *L){
    int number;
    LinkList temp = NULL;
    LinkList target = NULL;
    printf("可输入一个数值用于插入懂啊链表,输入0跳出循环\n");
    //向链表中插入数据
    while(1){
        scanf("%d",&number);
        if(number == 0) break;
        if(*L == NULL){
            //列表是NULL
            //1.创建一个新的节点
            //2.节点的next指向*L
            *L = (LinkList)malloc(sizeof(Node));
            if(L == NULL) exit(0);
            (*L)->data = number;
            (*L)->next = *L; //首元节点的指针需要指向自己
        }else{
            //列表不是NULL
            //1.创建一个新的节点
            //2.找到尾节点
            //3.新节点的next指向*L
            //4.尾节点的next 指向新节点
            temp = (LinkList)malloc(sizeof(Node));
            if(temp == NULL) exit(0);
            for(target = *L; target->next != *L; target = target->next);
            temp->data = number;
            temp->next = *L;
            target->next = temp;
        }
    }
    
    return OK;
}

Status createLinkList2(LinkList *L){
    int number;
    LinkList temp = NULL;
    LinkList lastNode = NULL;
    printf("可输入一个数值用于插入懂啊链表,输入0跳出循环\n");
    while (1) {
        scanf("%d",&number);
        if(number == 0) break;
        if(*L == NULL){
            //列表是NULL
            //1.创建一个新的节点
            //2.节点的next指向*L
            //3.lastNode = *L
            *L = (LinkList)malloc(sizeof(Node));
            if(*L == NULL) exit(0);
            (*L)->data = number;
            (*L)->next = *L;
            lastNode = *L;
        }else{
            //列表不是NULL
            //1.创建一个新的节点
            //2.新节点的next指向*L
            //3.lastNode->next 指向新节点
            //4.lastNode等于新节点
            temp = (LinkList)malloc(sizeof(Node));
            if(temp == NULL) exit(0);
            temp->data = number;
            temp->next = *L;
            lastNode->next = temp;
            lastNode = temp;
        }
    }
    
    return OK;
}
  • 单向循环链表插入

    • 不含头结点链表插入

      • 插入位置在首元结点上
        1. 创建新节点target并赋值
        2. 找到尾结点
        3. target指向首元结点
        4. 尾结点next指针指向target
        5. L指向target

      与含有头结点的区别在于插入在首元结点时需要额外修改L的指向

      • 插入位置在其他位置
        1. 创建新节点target并赋值
        2. 找到插入位置的上一个结点temp
        3. target->next 指向 temp->next
        4. temp->next 指向target

      注:切搞反3、4的顺序否则会造成temp后面的结点丢失

    • 含有头结点的链表插入

//插入元素
Status insert(LinkList *L,int item, int place){
    if(*L == NULL) return ERROR;
    LinkList temp = NULL;
    LinkList target = NULL;
    if(place == 1){
        //当插入的位置是1
        //1.找到尾节点
        //2.创建一个新节点temp并复制data
        //3.将新节点的next指向*L
        //4.将尾节点的next指向temp
        //5.将*L指向temp
        
        //找到最后一个节点
        for(target = *L; target->next != *L; target = target->next);
        temp = (LinkList)malloc(sizeof(Node));
        temp->next = (*L)->next;
        target->next = temp;
        *L = temp;
    }else{
        //当插入的位置不是1的时候
        //1.找到插入位置的前一个节点target
        //2.创建一个新节点temp,并赋值data
        //3.将temp->next指向target->next
        //4.将target->next指向temp
        //注:3、4位置不能调换 否侧会造成后面节点丢失
        int i;
        for (target = *L,i = 1; target->next != *L && i != place - 1; i ++,target = target->next);
        if(i != place - 1){
            printf("插入位置超出链表长度");
            return ERROR;
        }
        temp = (LinkList)malloc(sizeof(Node));
        if(temp == NULL) return ERROR;
        temp->data = item;
        temp->next = target->next;
        target->next = temp;
    }
    return OK;
}
  • 单向循环链表删除

    • 不含头结点链表删除

      • 删除首元结点
        1. 创建一个指针target指向首元结点
        2. 找到尾结点temp
        3. temp->next指针指向target->next
        4. L指向target->next
        5. 释放target
      • 删除其他位置结点
        1. 找到待删除指针的上一个指针temp
        2. 创建一个指针target指向temp->next(待删除指针)
        3. temp->next指向target->next
        4. 释放target

      同样的注意顺序否则有可能造成结点丢失

    • 含头结点链表删除

//删除元素
Status delete(LinkList *L,int place){
    if(*L == NULL) return ERROR;
    LinkList target = NULL;
    LinkList temp = NULL;
    if(place == 1){
        //删除的是首元结点
        //1.找到尾结点
        //2.temp指向首元结点
        //3.*L 指向 *L->next
        //4.尾结点指向*L
        //5.释放temp
        for(target = *L; target->next != *L; target = target->next);
        temp = *L;
        target->next = (*L)->next;
        *L = (*L)->next;
        free(temp);
    }else{
        //删除的不是首元结点
        //1.找到要删除位置的上一个接点target
        //2.将target->next赋值给temp
        //3.target->next 指向temp->next
        //4.释放temp
        int i;
        for(i = 1,target = *L; target->next != *L && i < place-1; target = target->next,i++);
        if(i < place) return ERROR;
        temp = target->next;
        target->next = temp->next;
        free(temp);
    }
    return OK;
}

注:代码展示的是含有头结点链表的操作,感兴趣的朋友可以尝试一下不含有头结点链表的实现