【C语言】单链表的创建

单链表的创建的代码实现:

        链表是线性表的基础部分,它实现了将一组数据从逻辑上按顺序存储从物理上按分布存储的功能。接下来尝试利用C语言对线性表中的链表进行代码实现(此程序中规定用户输入的数据类型为int类型):

typedef struct Node{
    int data;
    struct Node *next;
}NODE,*PNODE;

首先定义一个结构体,在此结构体中,包含了存储整型数据的数据域,指向下一个结点的指针域。这样一个简单的链表结点就构造完成了。

        1、在构造好链表结点后,利用PNODE creat_Hlist();函数实现利用头插法创建链表。

PNODE creat_Hlist(){
    int len,i,a;
    PNODE s,pHead;
    printf("你要输入几个元素?\n");
    scanf("%d",&len);
    if(len<=0){
        printf("你不想输入数据!\n");
    }else{
        pHead=NULL;
        for(i=0;i<len;i++){
            s=(PNODE)malloc(sizeof(NODE));
            if(s==NULL){
                printf("存储空间申请失败,请使用尾插法输入数据!\n");
            }else{
                printf("请输入第%d个元素:\n",i+1);
                scanf("%d",&a);
                s->data=a;
                s->next=pHead;
                pHead=s;
            }
        }
    }
    return pHead;
}

首先声明结构体类型的两个指针变量:s、pHead。之后判断用户想要输入的元素个数,当用户想要输入一定数量的元素后,先将pHead指针变量置空,此时并没有为pHead分配实际存储空间,按照元素个数申请相应数量的空间,此操作由一个循环完成,在每个空间成功申请后将s指针变量指向这个空间,此时为s指针分配了实际的存储空间,将用户输入的第一个元素通过s指针变量存入相应空间结构体的数据域,相应空间的指针域指针指向pHead所指向的空间(此空间在最开始不是物理空间而是null),然后将pHead指向s指针指向的实际存储空间,在存储第二个数据后,第二个数据存储的结构体中的指针域中的指针便指向第一个数据的存储空间,而pHead指针则指向了第二个数据的存储空间;当所有数据存储完毕,此函数返回了pHead指针。因为pHead指针已经指向了最后一个存储元素的空间(在本人看来其实是指向了所有存储元素的空间)。

        2、在构造好链表结点后,利用PNODE creat_Tlist();利用尾插法创建链表。

PNODE creat_Tlist(){
    int len,i,a;
    PNODE pHead,pTail,pNew;
    printf("你想输入几个元素?\n");
    scanf("%d",&len);
    if(len<=0){
        printf("你不想输入数据!\n");
    }else{
        pHead=(PNODE)malloc(sizeof(NODE));
        if(pHead==NULL){
            printf("存储空间申请失败,请使用头插法输入数据!\n");
        }else{
            pTail=pHead;
            pHead->data='#';
            pTail->next=NULL;
            for(i=0;i<len;i++){
                printf("请输入第%d个元素的元素值:\n",i+1);
                pNew=(PNODE)malloc(sizeof(NODE));
                if(pNew==NULL){
                    printf("存储空间申请失败,请使用头插法输入数据!\n");
                }else{
                    scanf("%d",&a);
                    pNew->data=a;
                    pNew->next=NULL;
                    pTail->next=pNew;
                    pTail=pNew;
                }
            }
        }
    }
    return pHead;
}

先声明三个结构体类型指针变量pHead,pTail,pNew。在判断好用户属于合法输入后,将pHead指向第一个实际已经申请好的结构体存储空间(若存储空间申请无效后,那么不能实现尾插法),随后将pTail也指向pHead所指的存储空间,于此同时将第一个存储空间中的数据域中赋值为“#”(之所以这么赋值是因为用于后面对链表操作时和头插法创建的列表进行区分),之后将第一个存储空间将的指针域置空,在存储第一个数据时,利用pNew指向第二个申请的存储空间(若存储空间申请无效后,那么不能实现尾插法),通过pNew指针向存储空间数据域存放目标数据,将存储空间指针域置空,同时将第一个存储空间的指针(利用pTail->next表示)指向pNew指向的第二个存储空间,同时将pTail指针指向pNew指针向的存储空间。随后在合法(用户想要存储的数据个数)长度内,依次申请存储空间,同时利用pNew指针指向这些空间,通过pNew指针分别对存储空间中的数据域进行赋值,同时通过pNew将存储空间的指针域置空。重复之前的操作。最后返回pHead指针。因为pHead指针已经指向了最后一个存储元素的空间(在本人看来其实是指向了所有存储元素的空间)。

        3、利用int judge_list();函数判断这个链表是头插法生成还是尾插法生成:

int judge_list(PNODE pHead){
    int i;
    PNODE p;
    p=pHead;
    if(p->data=='#'){
        i=1;
        printf("此链表由尾插法构成\n");
    }else{
        i=0;
        printf("此链表由头插法构成\n");
    }
    return i;
}

根据头插法和尾插法在头指针处有没有数据对创建好的链表进行头插法和尾插法的判断。若是链表由尾插法构成则函数返回1;反之返回0.

        4、由void trave_list();函数进行链表的遍历打印:

void trave_list(PNODE pHead){
    int a;
    PNODE p;
    p=pHead;
    if(p==NULL){
        printf("这是一个空链表!\n");
    }else{
        a=judge_list(pHead);
        if(a==1){
            p=pHead->next;
        }
        while(p!=NULL){
                printf("%4d",p->data);
                p=p->next;
        }
    }
}

首先判断链表是否为空表,其次判断已经生成的链表是由头插法创建还是由尾插法创建,之后依据不同的创建方法对链表进行遍历打印。

        5、由int length_list();函数打印链表的长度:

int length_list(PNODE pHead){
    int i,a;
    i=0;
    PNODE p;
    p=pHead;
    if(p==NULL){
        printf("这是一个空表!\n");
    }else{
        a=judge_list(pHead);
        if(a==1){
            p=pHead->next;
        }
        while(p!=0){
            i=i+1;
            p=p->next;
        }
        printf("这个链表有%d个数据!\n",i);
    }
    return i;
}

先判断是否为空表、现有链表由什么方法创建,然后对链表中的元素进行计数。最后返回统计的数字即链表的长度。

        6、利用void find_list();函数实现链表数据的查询:

void find_list(PNODE pHead){
    int i,a,b;
    i=0;
    PNODE p;
    p=pHead;
    if(p==NULL){
        printf("这是一个空表!\n");
    }else{
        a=judge_list(p);
        if(a==1){
            p=pHead->next;
        }
        b=length_list(p);
        printf("你想查找第几号元素?\n");
        scanf("%d",&a);
        if(a<=0||a>b){
            printf("你查找的元素不在这个链表中!\n");
        }else{
            while(p!=NULL){
                i=i+1;
                if(i==a){
                    printf("第%d号元素是:%4d",a,p->data);
                    break;
                }
                p=p->next;
            }
        }
    }
}

在判断链表不是空表后,先判断此链表是用头插法生成还是用尾插法实现,然后通过元素在;链表中的号数来实现对链表中的元素的查询。

        7、利用void change_list();/函数实现对链表数据的修改:

void change_list(PNODE pHead){
    int i,a,b,c;
    i=0;
    PNODE p;
    p=pHead;
    if(p==NULL){
        printf("这是一个空表!\n");
    }else{
        a=judge_list(p);
        if(a==1){
            p=pHead->next;
        }
        b=length_list(p);
        printf("你想修改第几号元素?\n");
        scanf("%d",&a);
        if(a<=0||a>b){
            printf("你要修改的元素不在这个链表中!\n");
        }else{
            while(p!=NULL){
                i=i+1;
                if(i==a){
                    printf("第%d号元素是:%d  ,",a,p->data);
                    printf("请输入你要修改的值:\n");
                    scanf("%d",&c);
                    p->data=c;
                    printf("元素修改成功!\n");
                    break;
                }
                p=p->next;
            }
        }
    }
}

在判断链表不为空后,通过修改链表长度内某号数对应的元素来修改指定位置处的元素值。

        8、利用void apend_list();函数实现链表数据的后面插入:

void apend_list(PNODE pHead){
    int i,a,b,d;
    i=0;
    PNODE p,c;
    p=pHead;
    if(p==NULL){
        printf("这是一个空表,请先建立链表!\n");
    }else{
        a=judge_list(p);
        if(a==1){
            p=pHead->next;
        }
        b=length_list(p);
        printf("你想在第几号元素后面插入数值?\n");
        scanf("%d",&a);
        if(a<=0||a>b){
            printf("你要插入的位置不在这个链表中!\n");
        }else{
            while(p!=NULL){
                i=i+1;
                if(i==a){
                    c=(PNODE)malloc(sizeof(NODE));
                    if(c==NULL){
                        printf("开辟空间失败,无法输入数据!\n");
                    }else{
                        printf("请输入你想输入的数值:\n");
                        scanf("%d",&d);
                        c->data=d;
                        c->next=p->next;
                        p->next=c;
                        printf("数值插入成功!\n");
                    }
                    break;
                }
                p=p->next;
            }
        }
    }
}

先判断链表是否为空,在不为空且知道链表长度(调用length_list()函数,详见【C语言】单链表的创建(三)部分描述)的前提下:用户输入想要在几号元素(目标元素)后面插入数据,当输入的号数合法之后,在目标元素后面插入数据。

        9、利用void befor_list();函数实现链表数据的前面插入:

void befor_list(PNODE pHead){
    int i,a,b,d,e;
    i=0;
    PNODE p,c;
    p=pHead;
    if(p==NULL){
        printf("这是一个空表,请先建立链表!\n");
    }else{
        e=judge_list(p);
        if(e==1){
            p=pHead->next;
        }
        b=length_list(p);
        printf("你想在第几号元素前面插入数值?\n");
        scanf("%d",&a);
        if(a<=0||a>b+1){
            printf("你要插入的位置不在这个链表中!\n");
        }else{
            c=(PNODE)malloc(sizeof(NODE));
            if(c==NULL){
                printf("开辟空间失败,无法输入数据!\n");
            }else{
                printf("请输入你想输入的数值:\n");
                scanf("%d",&d);
                c->data=d;
                c->next=NULL;
                if(a==1){
                    printf("此程序无法插入第一个结点,但可以修改第一个结点!\n");
                }else{
                    while(p!=NULL){
                        i=i+1;
                        if(i==a-1){
                            c->next=p->next;
                            p->next=c;
                            printf("数值插入成功!\n");
                            break;
                        }
                        p=p->next;
                    }
                }
            }
        }
    }
}

先判断是否为空表,在不为空且知道链表长度(调用length_list()函数,详见【C语言】单链表的创建(三)部分描述)的前提下,然后找到用户输入的合法目标元素号数的前一个号数,在前一个号数的后面实现元数的插入。这也引出一个问题:无法在第一个结点前插入元素。目前本人还没有想法对其进行完善和优化。

        10、利用void delet_list();函数实现对链表元素的删除:

void delet_list(PNODE pHead){
    int i,a,b,e,f;
    i=0;
    PNODE p,s;
    p=pHead;
    if(p==NULL){
        printf("这是一个空表!\n");
    }else{
        e=judge_list(p);
        if(e==1){
            p=pHead->next;
        }
        b=length_list(p);
        printf("你想删除第几号元素?\n");
        scanf("%d",&a);
        if(a<=0||a>b){
            printf("你删除的元素不在这个链表中!\n");
        }else{
            if(a==1){
                printf("此程序无法删除第一个结点,但可以修改第一个结点!\n");
            }else{
                while(p!=NULL){
                i=i+1;
                if(i==a-1){
                    s=p->next;
                    f=s->data;
                    p->next=p->next->next;
                    free(s);
                    printf("第%d号元素:%d 删除成功!\n",a,f);
                    break;
                    }
                p=p->next;
                }
            }
        }
    }
}

在判断链表不为空后,找到用户想要删除的元素的前面一个元素,修改其指针域中指针指向的内容即可将目标元素删除,同时不要忘记将指向目标元素的指针释放。

        11、现在就来实现应用一个基本框架将这些基本功能串联起来,构成一个简单的单链表小程序。因此单链表系列在此做一个小完结。基本框架如下(和之前创建顺序表的框架相似):

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>          //引入函数库

typedef struct Node{
    int data;
    struct Node *next;
}NODE,*PNODE;  	      //定义结构体

//声明各个功能函数

void main(){
    int b=1,c;
    PNODE pHead;
    pHead=NULL;
    while(b){
        system("cls");
        printf("\n\n\t\t线性表之单链表的基本操作\n\n");
        printf("\t\t...1-n.链表的各种功能操作; \n");
        printf("\t\t...0 退出 \n");
        printf("\n\t\t请选择 [0,10]: ");
        scanf("%d",&c);
        printf("\n\n");
        switch(c){
            case 1-n:调用各种功能函数          ;break;
            case 0:b=0                            ;break;
        }
        if(c!=0){
             printf("\n 输入然后关键字继续...");
             getche();
        }
    }
    system("cls");
    printf("\t\t 程序!\n");
    exit(-1);
}

//具体的各种功能函数

单链表的运行结果:

1.程序开始运行界面:

2.由头插法创建链表:

 3.对链表进行遍历打印(顺便判断链表生成方式)

4.对链表进行后插入:

 打印插入数值后的链表状态:

 5.删除元素:

打印删除元素后链表状态:

 6.在链表中前插入数据:

打印插入数据后的链表状态:

 7.尾插法生成链表:

输出新生成的链表:

在此仅仅展示这几个简单功能,剩余功能也能正常实现。本程序的不足之处在于部分代码显得过于冗余,另外在每一次进行新操作之前还要判断生成方法是“头插法”还是“尾插法”才能做相应处理,这使得第一个结点的操作不同于其他结点,在此本人暂时没有想到更好的解决方法(摆烂先)

以上是本人对链表的C语言代码初步实现,不喜勿喷,感谢理解。

相关链接:

【C语言】顺序表的创建_lixxkv的博客-优快云博客

二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) C语言下的单链表,可以增加,删除,查找,销毁节点。
在 C 语言中,创建单链表的基本步骤包括定义链表节点结构、初始化链表头结点、插入节点、删除节点等操作。下面是一个简单的单向链表创建的示例: ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构 typedef struct Node { int data; // 数据域 struct Node* next; // 指针域,指向下一个节点 } Node; // 创建一个新的链表节点 Node* createNode(int value) { Node* newNode = (Node*) malloc(sizeof(Node)); // 动态分配内存 if (newNode != NULL) { newNode->data = value; newNode->next = NULL; // 初始时指针为NULL,表示链表结束 return newNode; } else { printf("Memory allocation failed.\n"); // 内存不足时处理 return NULL; } } // 插入节点到链表尾部 void appendNode(Node** headRef, int value) { Node* newNode = createNode(value); if (newNode != NULL) { // 如果节点创建成功 if (*headRef == NULL) { // 链表为空,新节点成为头结点 *headRef = newNode; } else { // 非空链表,找到尾节点并插入 Node* temp = *headRef; while (temp->next != NULL) { temp = temp->next; } temp->next = newNode; } } } // 打印链表 void printList(Node* node) { while (node != NULL) { printf("%d -> ", node->data); node = node->next; } printf("NULL\n"); } int main() { Node* head = NULL; // 初始化链表为空 appendNode(&head, 1); // 插入第一个元素 appendNode(&head, 2); appendNode(&head, 3); printf("Created single linked list:\n"); printList(head); return 0; } ``` 在这个例子中,我们首先定义了一个链表节点结构 `Node`,然后实现了创建节点(createNode)、在尾部插入节点(appendNode)以及打印链表(printList)的功能。在 `main` 函数中,我们动态地创建了一个包含数字 1、2 和 3 的链表并打印出来。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值