单向链表在C语言上的实现

作为几乎是初学C语言的人,在指针的问题还没有完全搞明白的时候,就看到了数据结构里面链表这样一个庞然大物,完全不懂的怎么操作。在阅读了多方资料后,终于根据自己的理解,写出了第一个单向链表程序,放在此处与大家分享,也让自己在复习的时候可以再回顾一下。

程序实现了链表的创建、添加单个元素、删除单个元素的功能;对象为单个int型,即程序中的data。

因为都是用的函数,所以如果要添加其他的操作,只需要写另外的函数就可以了。

下面点明几个个人感觉比较重要的思想。

一、找到前一个元素

想要处理某个元素的时候,要用指针指向该元素(方法是遍历搜索),但同时,如果要执行添加或删除元素的操作,要用另外一个指针找到被处理元素之前的一个元素,这样才能让链表中的指针连接起来,因为链表的核心就是上一个指向下一个,下一个再指向下一个。举个例子,在删除元素的时候,要让被删除元素的上一个元素的指针,指向被删除元素指向的元素,所以在操作上需要找到被删除元素的上一个元素;添加同样是如此,要让上一个元素指向新添加的元素。

在循环创建元素的过程中,用了一个*current指针指向被创建元素的之前一个元素,其作用是解决下面所述的一个问题:

创建头部之后进入循环,创建第一个元素s1后,要把头部的指针指向第一个元素s1,即 head->next = s1;创建第二个元素s2后,要让s1指向s2,即 s1->next = s2;但在循环中,因为s1和s2并不是数组,所以并不能实现这样换元素的操作,自然而然,程序中用了*current来解决这个问题。

进入循环之前创建*current,代表当前创建元素的前一个元素,具体实现方法:

1、头:current = head;(为了之后创建s1后,对head的指针进行操作;因为current也是指针,所以操作current就等于操作head);

2、创建第一个元素s1后:current->next = s1;(刚创建的元素,利用current对head操作,使head指向s1);

current = s1;(因为接下来要创建s2了,为了创建s2后,对s1的指针进行操作(使s1的指针指向s2),所以把s1的值赋给current,等下对current进行操作,就相当于操作s1);

3、创建第二个元素s2后:current->next = s2(此处的current即s1,实现了s1->next = s2);

current = s2;(再为接下来创建新的元素做准备);

···

n、创建最后一个元素sn:current->next = NULL;(最后一个指针指向NULL,链表结束)。

虽然上面写了s1,s2,看起来像是分开的,但在链表中,每一个元素都是*next,所以其实是一样的东西,在循环中并没有s1,s2之分,都是*next。

二、首位插入

本人解决首位插入的问题的方法很简单,可能也比较笨,其实就是把新建立的元素先放在head和第二个元素之间,再交换该元素与头元素的data数据。

1、新创建元素s1

2、s1插入到head后(与在链表中间插入无异,即listInsert函数)

s1->next = head->next;

head->next = s1;

3、交换s1和head的data

s1->data = head->data;

head->data = new data;

即实现把新加入的data放在头尾,head中的data放在第二位。


下面上程序:

#include <stdio.h>
#include <stdlib.h>
struct CELL {
    int data;
    struct CELL *next;
};
struct CELL * listCreate()
{
    printf("创建链表中···\n");
    struct CELL *head, *current, *next;
    int value;
    char flag = 'y';
    
    printf("请输入元素:\n");
    scanf("%d", &value);
    
    head = (struct CELL *) malloc( sizeof(struct CELL));
    if(head == NULL){
        printf("no memory\n");
        exit(0);
    }
    
    head->data = value;
    //current的作用在笔记上
    current = head;
    
    printf("是否要继续添加?(y/n)\n");
    scanf("%c", &flag);
    getchar();
    
    while(flag != 'n' && flag != 'N')
    {
        printf("请输入元素:\n");
        scanf("%d", &value);
        getchar();
        //每次循环都会给一个新的next的地址
        next = (struct CELL *) malloc( sizeof(struct CELL));
        if(next == NULL){
            printf("no memory\n");
            exit(0);
        }
        next->data = value;
        
        current->next = next;
        current = next;
        
        printf("是否要继续添加?(y/n)\n");
        scanf("%c", &flag);
        
    }
    current->next = NULL;
    return head;
}
//head把原函数中的头传进来作寻找用
void listInsert(struct CELL *head)
{
    int value, front;
    char flag;
    printf("新元素是否插入在首位?(y/n)\n");
    scanf("%c", &flag);
    printf("请输入新元素的值:\n");
    scanf("%d", &value);
    //首位插入
    if(flag == 'y'){
        struct CELL *next;
        next = (struct CELL *)malloc(sizeof(struct CELL));
        //新节点放在head和第二个元素中间
        next->next = head->next;
        head->next = next;
        //交换head和新元素的值,把新的值赋给head,使新值成为头;再把原来的头元素的值赋给新元素,成为第二个位置的值;
        next->data = head->data;
        head->data = value;
    }
    else{
    printf("请输入要插入位置的前一个元素的值:\n");
    scanf("%d", &front);
    //新节点
    struct CELL *next;
    //p为插入位置前一个指针
    struct CELL *p;
    next = (struct CELL *)malloc(sizeof(struct CELL));
    if(next == NULL){
        printf("no memory\n");
        exit(0);
        }
    //新节点赋值
    next->data = value;
    //找到链表的头
    p = head;
    //用值从头寻找插入元素之前那个元素的值
    while(p->data != front)
        p = p->next;
    //新插入的元素的指针指向下一个元素,即等于前一个元素的指针
    next->next = p->next;
    //前一个元素的指针指向该新插入的元素
    p->next = next;
    }
}
void listDelete(struct CELL *head)
{
    int value;
    //p用来找链表里的值;front指向p前面一个数据,用来连接被删除的前后元素
    struct CELL *p, *front;
    //找到链表的头
    p = front = head;
    printf("请输入要删除的元素的值:\n");
    scanf("%d", &value);
    while(p->data != value){
        front = p;
        p = p->next;
        }
    //p前一元素front指针指向原来p指向的下一个元素
    front->next = p->next;
    free(p);
}
void listViewall(struct CELL *p)
{
    while(p->next != NULL)
    {
        printf("%d\t", p->data);
        p = p->next;
    }
    printf("%d\n", p->data);
}
int main(){
    int flag = 0;
    struct CELL *p;
    //p为头指针,所有的探索都要从头开始
    p = listCreate();
    printf("以下为所有已添加的数据:\n");
    listViewall(p);
    
    while(flag != 4){
        printf("请选择功能:\n(1)显示当前所有\n(2)添加一个数据\n(3)删除一个数据\n(4)清除所有数据并退出\n");
        scanf("%d", &flag);
        getchar();
        
        switch (flag) {
            case 1:
                listViewall(p);
                break;
            case 2:
                listInsert(p);
                listViewall(p);
                break;
            case 3:
                listDelete(p);
                listViewall(p);
                break;
            case 4:
                free(p);
                printf("程序正在退出···\n");
                break;
            default:
                break;
        }
    }
    return 0;
}

程序没有纠错,希望大家输入的时候谨慎小心。

如果存在bug,或者有什么问题,或者有什么改进方案,亦或是本人犯了什么错误,欢迎大家指出,我会及时回复的。

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值