C语言 - 基础数据结构和算法 - 单向链表

听黑马程序员教程《基础数据结构和算法 (C版本)》,照着老师所讲抄的,

视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1

喜欢的朋友可以去看看。

02 单向链表

复制代码
#include<stdio.h >
#include<stdlib.h>
#include<string.h>


// 用户自定义数据
typedef struct Student{
    int id;                // 学号 
    char name[32];        // 姓名 
    char classs[32];    // 班级 
    int ch,ma,en;    // 语、数、外、总分 
}st; 

// 链表结点
typedef struct LINKNODE{
    //int i_data;    // 这样写就写死了,只能放int数据了,
    void* data;    // void* 是无类型指针,可以指向任何类型的数据。 
    struct LINKNODE* next;
}LinkNode;

// 链表结构体
typedef struct LINKLIST{
    LinkNode* head;        // 头结点 
    int size;            // 链表节点数 
    // 要像动态数组一样需要内存容量吗?不需要, 没有容量概念。 
}LinkList; 

// 打印函数指针
typedef  void(*PRINTLINKNODE)(void*);

// -----------------------------------------------------------------------------
// 下面函数,就是针对上面链表结构体和结点的操作。
// -----------------------------------------------------------------------------

// 初始化链表 ,返回一个链表结构体
LinkList* Init_LinkList();

// 指定位置插入。参数:所插入的链表,位置,值
void Insert_LinkList(LinkList* list,int pos,void* data);

// 删除指定位置的值。 参数:链表,位置 
void RemoveByPos_LinkLIst(LinkList* list,int pos); 
 
// 获得链表的长度(结点数量)
int Size_LinkList(LinkList* list);

// 根据值,查找位置
int Find_LinkList(LinkList* list,void* data);
 
// 返回第一个节点
void* Front_LinkList(LinkList* list); 

// 打印链表结点
void Print_LinkList(LinkList* list,PRINTLINKNODE print);
 
// 释放内存 
void FreeSpace_LinkList(LinkList* list);

// -----------------------------------     main()  -------------------- 


// 打印函数
void MyPrint(void* data){
    st* p = (st*)data;
    printf("学号:%d\t姓名:%s\t班级:%s\t语文%d\t数学%d\t英语%d\t总分%d\n",
                p->id,p->name,p->classs,p->ch,p->ma,p->en,p->ch+p->ma+p->en);
}
int main(){
    printf("好好学习,天天向上~!(单向链表20220603)\n\n");
    
    // 创建链表
    LinkList* stList = Init_LinkList();
    
    // 创建数据
    st st1 = {2001,"宗宗","364班",100,100,100};
    st st2 = {2002,"艳艳","364班",99,88,59};
    st st3 = {2003,"发发","364班",88,77,66};
    st st4 = {2004,"小小","364班",58,57,55};
    st st5 = {2005,"小五","364班",58,57,55};
    
    // 插入数据 
    Insert_LinkList(stList,0,&st1);
    Insert_LinkList(stList,0,&st2);
    Insert_LinkList(stList,0,&st3);
    Insert_LinkList(stList,0,&st4);
    Insert_LinkList(stList,0,&st5);
    
    // 打印
    Print_LinkList(stList,MyPrint); 
    
    printf("\n删除第3个节点\n");
    RemoveByPos_LinkLIst(stList,3);
    // 打印
    Print_LinkList(stList,MyPrint);
    
    printf("\n返回第一个节点\n");
    // 返回的是void* 类型,要转换成st*类型 
    st* ret = (st*)Front_LinkList(stList);
    printf("学号:%d\t姓名:%s\t班级:%s\t语文%d\t数学%d\t英语%d\t总分%d\n",
                ret->id,ret->name,ret->classs,ret->ch,ret->ma,ret->en,ret->ch+ret->ma+ret->en);    
    
    // 销毁释放
    FreeSpace_LinkList(stList); 
    return 0;
}

// -----------------------------------------------------------------------------
// 下面函数,就是针对上面链表结构体和结点的操作。
// -----------------------------------------------------------------------------

// 初始化链表 ,返回一个链表结构体
LinkList* Init_LinkList(){
    LinkList* list = (LinkList*)malloc(sizeof(LinkList));
    list->size = 0;
    // 添加头节点,头节点不保存数据
    list->head = (LinkNode*)malloc(sizeof(LinkNode));
    list->head->next = NULL;     // 头结点初始化一下 
    
    return list;
}

// 指定位置插入。参数:所插入的链表,位置,值(是数据指针) 
void Insert_LinkList(LinkList* list,int pos,void* data){
    
    // 判断检查参数 
    if(list==NULL){
        return;
    }
    if(data==NULL){
        return;
    }
    if(pos<0 || pos > list->size){    // 友好判断,如果位置pos越界,由插入到尾部 
        pos = list->size;
    }
    
    // 创建新的节点
    LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
    newNode->data = data;    // 新节点赋值
    
    // 找结点
    LinkNode* pCurrent =  list->head;    // 辅助指针,用于找当前节点(插入位置的前一个节点)
    int i;
    for(i=0;i<pos;i++){        // 通过循环找到插入位置的前一个节点 
        pCurrent = pCurrent->next;
    }
    // 插入
    newNode->next = pCurrent->next;
    pCurrent->next = newNode;
    
    list->size++;    // 更新节点数量的记录。 
     
}

// 删除指定位置的值。 参数:链表,位置 
void RemoveByPos_LinkLIst(LinkList* list,int pos){
    // 判断检查参数 
    if(list==NULL){
        return;
    }
    if(pos<0 || pos >= list->size){     
        //pos = list->size;    // 这里就不能友好判断了,
        return;        // 指定位置不存在,直接返回。 
    }
    
    // 删除 
    // 找结点
    LinkNode* pCurrent =  list->head;    // 辅助指针,用于找当前节点(删除节点的前一个节点)
    int i;
    for(i=0;i<pos;i++){        // 通过循环找到要删除结点的前一个节点 
        pCurrent = pCurrent->next;
    }
    // 删除(先缓存要删除的节点) 
    //pCurrent->next = pCurrent->next->next;
    //free(pCurrent->next);    
    LinkNode* pDel = pCurrent->next;
    pCurrent->next = pDel->next;
    free(pDel);
    list->size--;    // 更新链表节点数量 
    
        
}
 
// 获得链表的长度(结点数量)
int Size_LinkList(LinkList* list){
    
    return -1;
}

// 根据地址,查找位置,void* data 是地址(指针),不是值。 
int Find_LinkList(LinkList* list,void* data){
    // 判断检查参数 
    if(list==NULL){
        return -1;
    }
    if(data==NULL){
        return -1;
    }
    
    // 遍列查找节点,从头节点的下一个节点开始找
    LinkNode* pCurrent = list->head->next;
    int i = 0;
    while(pCurrent){
        if(pCurrent->data==data){
            break;
        }
        pCurrent = pCurrent->next;
        i++;
    }

    return i;
}
 
// 返回第一个节点
void* Front_LinkList(LinkList* list){
    return list->head->next->data;
}

// 打印链表结点
void Print_LinkList(LinkList* list,PRINTLINKNODE print){
    if(list==NULL){
        return;
    }
    // 辅助指针变量
    LinkNode* pCurrent = list->head->next;
    while(pCurrent){
        print(pCurrent->data);    // 调用用户函数,传入数据参数 
        pCurrent = pCurrent->next;
    } 
    
}
 
// 释放内存 
void FreeSpace_LinkList(LinkList* list){
    if(list==NULL){
        return;
    }
    // 辅助指针变量
    LinkNode* pCurrent = list->head;
    while(pCurrent){        // 循环释放节点内存。 
        LinkNode* pNext = pCurrent->next;     // 用指针先记录下一个节点,如果不记录的话,释放内存后就没有了, 
        free(pCurrent);
        pCurrent = pNext;
    }
    free(list);        // 节点内存释放完毕后,再释放链表内存 
}
复制代码

 

posted @   油腻老张  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示