0
点赞
收藏
分享

微信扫一扫

循环单链表的简单实现(C语言)

十里一走马 2022-03-21 阅读 55

循环单链表的简单实现(C语言)

简述

循环单链表和单链表的不同之处在于——单链表的表尾指向空,而循环单链表的表尾指向头部第一个结点。因此,他们的边界处理会有些不同。循环单链表的好处在于——给出任一结点都能找到链表中的所有结点。而单链表只能找到该结点后面的结点,对于该节点前面的结点单链表是完全未知的。

插入

前插结点

// 前插结点
bool InsertpriorNode(CLNode *p, ElemType e){
    if(p == NULL)   return false; // 数据合法性判断
    CLNode *s = (CLNode*)malloc(sizeof(CLNode));    // p为当前结点,s为欲插结点
    if(s == NULL)   return false;   // 内存不足,分配失败
    // 偷梁换柱法 将p存入s,再令p存入新数据.本质上还是后插
    s->data = p->data;  // p数据域存入s
    s->next = p->next;  // s下一结点指向p原后继结点
    p->next = s;        // p下一结点指向s
    p->data = e;        // p存入插入的数据
    return true;
}

后插结点

// 后插结点
bool InsertNextNode(CLNode *p, ElemType e){
    if(p == NULL)   return false;       // 数据合法性判断
    CLNode *s = (CLNode*)malloc(sizeof(CLNode));
    if((s) == NULL) return false;   // 内存不足,分配失败
    s->data = e;        // 存入数据域
    s->next = p->next;  // s联结p原后继
    p->next = s;        // p联结s
    return true;
}

头插法

// 头插法
CLinkList List_HeadInsert(CLinkList L){
   ElemType e;
   scanf("%d", &e);
   while(e != 9999){
       // 第一个结点要特殊处理
        if(L->next == L){   // 链表为空则创建一个新结点
            CLNode *p = (CLNode*)malloc(sizeof(CLNode));
            L->next = p;    // 令新结点成为头结点的后继结点
            p->next = L;
            p->data = e;
       }else{
            InsertpriorNode(L->next, e);    // 进行结点后插操作
       }      
       scanf("%d", &e);
   } 
   return L;
}

尾插法

// 尾插法
CLinkList List_TailInsert(CLinkList L){   
    ElemType e;
    scanf("%d", &e);
    while(e != 9999){  
        CLNode *p = (CLNode*)malloc(sizeof(CLNode));
        p = L;
        while(p->next != L){    // 第一个结点进行特殊处理
            if(L->next == L){   // 头结点为空则创建一个新结点
            CLNode *p = (CLNode*)malloc(sizeof(CLNode));
            L->next = p;    // L的下一结点指向p
            p->next = L;    // p的下一结点指向L
            p->data = e;    // p存入数据
        }
            p = p->next;        // 移到下一个结点
        }
        InsertNextNode(p, e);   // 调用后插结点函数进行后插操作
        scanf("%d", &e);
    }
    return L;
}

按位插入

// 按位插入
bool ListInsert(CLinkList *L, int pos, ElemType e){
    CLNode *p = *L;
    int i = 0;
    for(i; i < pos-1; i++){         // 定位到欲插入位序的前继结点
        p = p->next;
    }
    if(InsertNextNode(p, e)){   // 调用后插结点函数进行后插操作并判断操作是否成功
        return true;
    }else false;         
}

删除

结点后删

// 结点后删
bool DeleteNode(CLNode *p){     // 删除p结点的后继结点
    if(p == NULL) return false; // 数据非法性判断
    CLNode *s = p->next;        // s为p的后继结点
    p->next = s->next;          // p指向s的后继结点
    free(s);                    // 释放s的空间
    return true;
}

按位删除

// 按位删除
bool ListDelete(CLinkList *L, int pos, ElemType *e){
    if(L == NULL || pos < 1)    return false;   // 数据合法性判断
    CLNode *p = *L;
    int i = 0;
    for(i; i < pos-1; i++){     // 定位欲删除结点的前继结点
        p = p->next;
    }
    *e = p->next->data;
    if(DeleteNode(p)){          // 调用结点后删函数进行删除  
        return true;
    }else return false;     
}

销毁表

// 销毁表
bool DestoryList(CLinkList *L){
    while((*L)->next != *L){    // 循环遍历链表,逐个删除结点
        DeleteNode(*L);         // 调用结点后删函数
    }
    free(*L);   // 释放头结点的空间
    *L = NULL;
    return true;
}

查找

按位查找

// 按位查找
ElemType GetElem(CLinkList L, int pos){
    CLNode *p = L;
    int i = 0;
    for(i; i < pos; i++){   // 定位到位序上的结点
        p = p->next;
    }
    return p->data;         // 返回结点数据域
}

按值查找

// 按值查找
int LocateElem(CLinkList L, ElemType e){
    CLNode *p = L;
    int pos = 0;    // 记录位序
    while(p->next != L && p->data != e){    // 定位到结点数据域 ==e的前继结点
        p = p->next;
        pos++;
    }
    return pos++;   // 返回所在结点位序
}

其他

定义

typedef int ElemType;
typedef struct CLNode{
    ElemType data;
    struct CLNode *next;
}CLNode, *CLinkList;

初始化

// 初始化
bool InitList(CLinkList *L){
    (*L) = (CLinkList)malloc(sizeof(CLNode));
    if((*L) == NULL) return false;  // 内存不足,分配失败
    (*L)->next = (*L);              // 初始化让头结点指向自己
    return true;
}

求表长

// 求表长
int Length(CLinkList L){
    CLNode *p = L;
    int i = 0;              // 头结点为0号位序
    while(p->next != L){    // 遍历链表,用i来记录长度
        p = p->next;
        i++;
    }
    return i;
}

输出表

// 输出表
void PrintList(CLinkList L){
    CLNode *p = L;
    while(p->next != L){    // 遍历链表          
        p = p->next;        // 还未到达表尾则进行结点读取后移
        printf("%d ", p->data);  
    }
    printf("\n");
}

表判空

// 判空
bool Empty(CLinkList L){
    if(L->next == L)    // 如果头结点指向自己,则表为空
        return true;
    else return false;
}

判断表尾

// 判断结点是否位于表尾
bool IsTail(CLinkList L, CLNode *p){
    if(p->next == L){   // 后继结点为头结点则为表尾
        return true; 
    }else 
    return false;
}

完整代码

// 循环单链表
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int ElemType;
typedef struct CLNode{
    ElemType data;
    struct CLNode *next;
}CLNode, *CLinkList;
    
// 初始化
bool InitList(CLinkList *L){
    (*L) = (CLinkList)malloc(sizeof(CLNode));
    if((*L) == NULL) return false;  // 内存不足,分配失败
    (*L)->next = (*L);              // 初始化让头结点指向自己
    return true;
}

// 后插结点
bool InsertNextNode(CLNode *p, ElemType e){
    if(p == NULL)   return false;       // 数据合法性判断
    CLNode *s = (CLNode*)malloc(sizeof(CLNode));
    if((s) == NULL) return false;   // 内存不足,分配失败
    s->data = e;        // 存入数据域
    s->next = p->next;  // s联结p原后继
    p->next = s;        // p联结s
    return true;
}

// 前插结点
bool InsertpriorNode(CLNode *p, ElemType e){
    if(p == NULL)   return false; // 数据合法性判断
    CLNode *s = (CLNode*)malloc(sizeof(CLNode));    // p为当前结点,s为欲插结点
    if(s == NULL)   return false;   // 内存不足,分配失败
    // 偷梁换柱法 将p存入s,再令p存入新数据.本质上还是后插
    s->data = p->data;  // p数据域存入s
    s->next = p->next;  // s下一结点指向p原后继结点
    p->next = s;        // p下一结点指向s
    p->data = e;        // p存入插入的数据
    return true;
}

// 尾插法
CLinkList List_TailInsert(CLinkList L){   
    ElemType e;
    scanf("%d", &e);
    while(e != 9999){  
        CLNode *p = (CLNode*)malloc(sizeof(CLNode));
        p = L;
        while(p->next != L){    // 第一个结点进行特殊处理
            if(L->next == L){   // 头结点为空则创建一个新结点
            CLNode *p = (CLNode*)malloc(sizeof(CLNode));
            L->next = p;    // L的下一结点指向p
            p->next = L;    // p的下一结点指向L
            p->data = e;    // p存入数据
        }
            p = p->next;        // 移到下一个结点
        }
        InsertNextNode(p, e);   // 调用后插结点函数进行后插操作
        scanf("%d", &e);
    }
    return L;
}

// 头插法
CLinkList List_HeadInsert(CLinkList L){
   ElemType e;
   scanf("%d", &e);
   while(e != 9999){
       // 第一个结点要特殊处理
        if(L->next == L){   // 链表为空则创建一个新结点
            CLNode *p = (CLNode*)malloc(sizeof(CLNode));
            L->next = p;    // 令新结点成为头结点的后继结点
            p->next = L;
            p->data = e;
       }else{
            InsertpriorNode(L->next, e);    // 进行结点后插操作
       }      
       scanf("%d", &e);
   } 
   return L;
}

// 按位插入
bool ListInsert(CLinkList *L, int pos, ElemType e){
    CLNode *p = *L;
    int i = 0;
    for(i; i < pos-1; i++){         // 定位到欲插入位序的前继结点
        p = p->next;
    }
    if(InsertNextNode(p, e)){   // 调用后插结点函数进行后插操作并判断操作是否成功
        return true;
    }else false;         
}

// 结点后删
bool DeleteNode(CLNode *p){     // 删除p结点的后继结点
    if(p == NULL) return false; // 数据非法性判断
    CLNode *s = p->next;        // s为p的后继结点
    p->next = s->next;          // p指向s的后继结点
    free(s);                    // 释放s的空间
    return true;
}

// 按位删除
bool ListDelete(CLinkList *L, int pos, ElemType *e){
    if(L == NULL || pos < 1)    return false;   // 数据合法性判断
    CLNode *p = *L;
    int i = 0;
    for(i; i < pos-1; i++){     // 定位欲删除结点的前继结点
        p = p->next;
    }
    *e = p->next->data;
    if(DeleteNode(p)){          // 调用结点后删函数进行删除  
        return true;
    }else return false;     
}

// 按位查找
ElemType GetElem(CLinkList L, int pos){
    CLNode *p = L;
    int i = 0;
    for(i; i < pos; i++){   // 定位到位序上的结点
        p = p->next;
    }
    return p->data;         // 返回结点数据域
}

// 按值查找
int LocateElem(CLinkList L, ElemType e){
    CLNode *p = L;
    int pos = 0;    // 记录位序
    while(p->next != L && p->data != e){    // 定位到结点数据域 ==e的前继结点
        p = p->next;
        pos++;
    }
    return pos++;   // 返回所在结点位序
}

// 销毁表
bool DestoryList(CLinkList *L){
    while((*L)->next != *L){    // 循环遍历链表,逐个删除结点
        DeleteNode(*L);         // 调用结点后删函数
    }
    free(*L);   // 释放头结点的空间
    *L = NULL;
    return true;
}

// 输出表
void PrintList(CLinkList L){
    CLNode *p = L;
    while(p->next != L){    // 遍历链表          
        p = p->next;        // 还未到达表尾则进行结点读取后移
        printf("%d ", p->data);  
    }
    printf("\n");
}

// 判空
bool Empty(CLinkList L){
    if(L->next == L)    // 如果头结点指向自己,则表为空
        return true;
    else return false;
}

// 求表长
int Length(CLinkList L){
    CLNode *p = L;
    int i = 0;              // 头结点为0号位序
    while(p->next != L){    // 遍历链表,用i来记录长度
        p = p->next;
        i++;
    }
    return i;
}

// 判断结点是否位于表尾
bool IsTail(CLinkList L, CLNode *p){
    if(p->next == L){   // 后继结点为头结点则为表尾
        return true; 
    }else 
    return false;
}

int main(){
    CLinkList L;    // 声明一个双链表
    ElemType e;     // 元素
    int status;     // 操作码
    int pos;        // 位序
    int len;        // 表长
    InitList(&L);   // 链表的初始化
    while(1){
        printf("Please enter a status code:\n1.List_TailInsert    2.List_HeadInsert    3.GetElem\n");
        printf("4.LocateElem         5.ListDelete         6.ListInsert\n7.Length             8.Empty              9.PrintList\n");
        printf("10.DestoryList       11.InitList          0.Exit\n");
        scanf("%d", &status);
        if(status == 0) break;
        switch (status){
        case 1:     // 尾插法正序创建双链表
            printf("Please enter the DlinkList elements one by one!(end with 9999)\n");
            L = List_TailInsert(L);
            PrintList(L);
            break;
        case 2:     // 头插法逆序创建双链表
            printf("Please enter the DlinkList elements one by one!(end with 9999)\n");
            L = List_HeadInsert(L);
            PrintList(L);
            break;
        case 3:     // 按位查找
            printf("Please enter the position of the node you want to find!\n");
            scanf("%d", &pos);
            e = GetElem(L, pos);
            printf("The value in the %d position is %d\n", pos, e);
            break;
        case 4:     // 按值查找
            printf("Please enter the element value you want to find!\n");
            scanf("%d", &e);
            pos = LocateElem(L, e);
            printf("The address of the linked list where the element with the value %d is located is %d\n", e, pos);
            break;
        case 5:     // 按位删除
            printf("Please enter the position of the node you want to delete!\n");
            scanf("%d", &pos);
            if(ListDelete(&L, pos, &e)){
                printf("Element value %d deleted successfully\n", e);
                PrintList(L);
            }else{
                printf("Failed to delete!\n");
            }
            break;
        case 6:     // 按位插入
            printf("Please enter the position and element value you want to insert\n");
            scanf("%d%d", &pos, &e);
            if(ListInsert(&L, pos, e)){
                printf("Inserted successfully\n");
                PrintList(L);
            }else{
                printf("Failed to insert!\n");
            }
            break;
        case 7:     // 求表长
            len = Length(L);
            printf("The length of the DLinkList is %d\n", len);
            break;
        case 8:     // 判空
            if(L == NULL){
                printf("DList not exist!\n");
            }
            else if(Empty(L)){
                printf("DLinkList is empty!\n");
            }else
            printf("DLinkList not empty!\n");
            break;
        case 9:     // 输出表
            PrintList(L);
            break;
        case 10:    // 销毁表
            if(DestoryList(&L)){
                printf("DList destroyed successfully\n");
            }else
                printf("Failed to destroy\n");
            break;
        case 11:    // 初始化
            if(InitList(&L)){
                printf("DList initialized successfully\n");
            }else
            printf("Failed to initialize!\n");
            break;
        default:
            printf("Error!\n");
            break;
        }
    }
    return 0;
}

运行展示
在这里插入图片描述

举报

相关推荐

0 条评论