C语言之实现单链表的基本操作

目录

1、链表节点的定义

2、链表基操Function的声明

3、链表基操Function的定义与实现

 3.1 单链表的创建(头插法)

3.2 单链表的创建(尾插法)

3.3 有序单链表的创建

3.4 单链表的遍历 

3.5 在单链表中查找元素

3.6 在单链表中的指定位置后插入元素

3.7  在单链表中删除特定元素

3.8 两个有序单链表合并的问题 

总结



1、链表节点的定义

单链表节点的定义只需要有数据域和指针域即可,数据域用来存储节点数据的,指针域是用来存储后继节点的地址的(没有它,那你这还能叫链表嘛!),定义代码如下:

typedef int Status;     //状态码数据类型
typedef int ElemType;      //数据元素数据类型
typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;
typedef struct Node *LinkList;        //结构体链表指针

2、链表基操Function的声明

单链表的基操function无非就是创建、删除、插入、查询、遍历这几个基本操作,这里我就简单的将创建、遍历、插入、查询这几个function实现一下(我这里都是带“哨兵”的单链表哦!)

//创建单链表(头插法)
void CreateListHead(LinkList *L, int n);
//创建单链表(尾插法)
void CreateListTail(LinkList *L, int n);
//插入节点
//删除节点
//查询特定节点
Status Find_List_Node(LinkList L, ElemType data);
//遍历单链表
void DisplayList(LinkList L);
//创建特定有序链表(头插法)
void CreateOrderListH(LinkList *L, int n, int array[]);
//创建特定有序链表(尾插法)
void CreateOrderListT(LinkList *L, int n, int array[]);
//合并两个有序单链表
LinkList MergeTwoList(LinkList l1, LinkList l2);

这里面我加入了面试中常考的一个问题,就是“合并两个有序单链表的操作”问题,在LinkList  MergeTwoList(LinkList  l1,  LinkList  l2)中有展示如何实现的!

3、链表基操Function的定义与实现

 3.1 单链表的创建(头插法)

首先我们来实现一下单链表的创建function,单链表的创建分为头插法和尾插法这两种(记住,我这里可是带头结点的单链表哦!)。我们先说说头插法吧!

头插法呢!就是将一个一个节点插入到头结点的后继位置,也就是把每个新插入的节点放置在头结点(哨兵节点)的后继位置处!只要明白这一点,那实现起来,就和玩泥巴一样,首先你需要创建一个P结点,并且让它的指针域指向NULL,(怕他搞事情,所以最好指向空)这个P节点就是一个保姆,用来照顾我们新建的节点的数据和指针,我们为传进来的头结点分配内存空间,然后创建新节点,为其分配data(随机数即可),并把P节点的next指针域指向头结点的后继即可(head->next);具体的代码实现如下

//创建单链表(头插法)
void CreateListHead(LinkList *L, int n)
{
    LinkList p; int i;
    //srand(time(0));       //初始化随机数种子
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;     //建立一个带头结点的单链表
    for(i = 0; i < n; i++)
    {
        p = (LinkList)malloc(sizeof(Node));      //生成新的节点
        p->data = rand() % 100 + 1;     //生成100以内的随机数
        p->next = (*L)->next;
        (*L)->next = p;     //插入到表头
    }
}

3.2 单链表的创建(尾插法)

咱们再来看看尾插法是怎么操作的呢?尾插法啊!就是将新节点插入到单链表的尾巴处,那么?我们如何得知哪里是单链表的尾巴处呢?这里我们放置一个“小兵”,这个小兵啥都不干,就是帮咱们记录“单链表的尾巴”在哪里,找到尾巴了,把新节点插入到尾巴后面,此时咱们的小兵指向的不在是单链表的尾巴了,我们需要让小兵往后挪一个节点(更新小兵的指向),这样小兵又再次的指向单链表的尾巴了!依次反复即可!当你插入完了以后,别忘了,让你的小兵指向空哈(不然他要搞事情的啊!);具体的实现代码如下哈!

//创建单链表(尾插法)
void CreateListTail(LinkList *L, int n)
{
    LinkList p, r; int i;
    //srand(time(0));     //初始化随机数种子
    *L = (LinkList)malloc(sizeof(Node));        //L为整个线性表
    r = *L;     //r为指向尾部的节点
    for(i = 0; i < n; i++)
    {
        p = (LinkList)malloc(sizeof(Node));     //生成新的节点
        p->data = rand() % 100 + 1;     //随机生成100以内的数字
        r->next = p;        //将表尾终端节点的指针指向新节点
        r = p;      //将当前的新节点定义为表尾终端节点
    }

    r->next = NULL;     //表示当前链表结束
}

3.3 有序单链表的创建

这个有序单链表的创建,我是用的顺序存储结构数据来存储节点的数据的,我们只需要将上面的function修改一丢丢即可使用啊!也就是多加一个数组形参即可!具体的实现代码如下:

//创建特定有序链表(头插法)
void CreateOrderListH(LinkList *L, int n, int array[])
{
    LinkList p; 
    int i;
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    for(i = 0; i < n; i++)
    {
        p = (LinkList)malloc(sizeof(Node));
        p->data = array[i];
        p->next = (*L)->next;
        (*L)->next = p;     //插到表头
    }
}
//创建特定有序链表(尾插法)
void CreateOrderListT(LinkList *L, int n, int array[])
{
    LinkList r,p;
    int i;
    (*L) = (LinkList)malloc(sizeof(Node));
    r = (*L);
    for(i = 0; i < n; i++)
    {
        p = (LinkList)malloc(sizeof(Node));
        p->data = array[i];
        r->next = p;
        r = p;
    }
    r->next = NULL;
}

3.4 单链表的遍历 

单链表的遍历还是蛮简单的!你要是不会,99%是因为你没有搞懂指针哦!那就好好看看指针再来看哈!代码如下:

//遍历单链表
void DisplayList(LinkList L)
{
    LinkList p;     //声明一节点
    p = L->next;    //让p指向L的第一个节点
    for(;p != NULL; p = p->next)
    {
        printf("%d ",p->data);
    }
    printf("\n");
}

是不是so easy!呢?所以说,你要是看不懂,那就好好的敲敲脑袋瓜!面壁思过去(hhhhhh)

3.5 在单链表中查找元素

这个也比较简单,菜哥就不讲了!直接贴代码!看不懂的都去给我面壁思过哈!

//查询特定节点元素
Status Find_List_Node(LinkList L, ElemType data)
{
    LinkList p; int j;
    p = L->next;
    for(j = 1; p != NULL; p = p->next, j++)
    {
        if(data == p->data)
        {
            break;
        }
        else
            continue;
    }
    p->next = NULL;
    return j;
}

3.6 在单链表中的指定位置后插入元素

        在单链表中的指定位置后面插入元素,这个操作不难,大概思路就是这样的,遍历链表,搜寻到对应位置的节点,用个“保姆”指针存入插入的data,并将“保姆”指针的指针域存入特定位置节点后继的地址,然后再将特定位置节点的指针域存入“保姆”指针的地址即可!具体操作代码如下:

//在特定位置插入节点
void InsertListElem(LinkList *L, int n, ElemType data)
{
    LinkList me, pre; int i = 1;
    pre = (*L)->next;
    if(pre == NULL)
        printf("LinkList is NULL!\n");
    else
    {
        for(; i < n; i++)
        {
            pre = pre->next;
        }
        me = (LinkList)malloc(sizeof(Node));
        me->data = data;
        me->next = pre->next;
        pre->next = me;
    }
}

3.7  在单链表中删除特定元素

       在单链表中删除特定元素,这个不难,大概思路就两步,第一是在链表中遍历,找到特定元素对应的节点,二是把该节点从链表中踢出去,并且要释放该节点的内存空间(不然会出现内存溢出的问题哦!),好啦!废话不多说,直接上代码哈!

//在单链表中删除指定元素
Status DeleteListElem(LinkList *L, ElemType data)
{
    LinkList me, bonne;
    me = (*L);
    while(me->next != NULL)
    {
        if(me->data == data)
        {
            bonne->next = me->next;
            free(me);
            return OK;
        }
        else
        {
            bonne = me;
            me = me->next;
        }
    }
    return ERROR;
}

3.8 两个有序单链表合并的问题 

        好好听讲哦!这个面试经常爱考的呦!大概的思路就是比较,往后挪,然后再比较,等谁先挪完了,基本上就可以结束了!(下面这个按照从小到大的顺序哈)

如图所示呢!L1,L2为两个有序的单链表(带头结点哦!),L3呢是我们新建的一个头结点,用来连接合并后的结果,首先比较L1的第一个节点的data与L2第一个节点的data,若L1的小于L2的 ,则将L3的头结点指向L1的第一个节点,同时将L3往后挪一个位置,然后再进行比较,比较到最后,谁先到结尾,那么就直接将没有到结尾的,把他后面的节点统统接到L3的尾巴后面即可!具体的操作代码如下:

//合并两个有序单链表
LinkList MergeTwoList(LinkList l1, LinkList l2)
{
    LinkList preHead = (LinkList)malloc(sizeof(Node));
    LinkList prev = preHead,list1 = l1->next, list2 = l2->next;
    while(list1 != NULL && list2 != NULL)
    {
        if(list1->data <list2->data)
        {
            prev->next = list1;
            list1 = list1->next;
        }
        else{
            prev->next = list2;
            list2 = list2->next;
        }

        prev = prev->next;
    }
    // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
    prev->next = list1 == NULL ? list2 : list1;

    return preHead->next;
}

总结

完了,讲完了,菜哥讲学结束!

对啦!博主是一个又菜又爱玩的人哦!大家喜欢的请多多点赞!同时文中有讲的不足之处,欢迎大家在底下评论,菜哥都会看的哦!

猜你喜欢

转载自blog.csdn.net/songjun007/article/details/114005551