链表的完整讲解

链表

特点:内存不连续,通过指针进行连接

解决: 长度固定的问题,插入删除麻烦的问题

1)逻辑结构:线性结构

2)存储结构:链式存储

3)操作:增删改查

2.1单向链表

链表结构体:

struct node_t

{

int data;

struct node_t *next;

};

2.1.1分类

1)有头单向链表

存在一个头节点,数据域无效,指针域有效

遍历有头单向链表:

#include <stdio.h>

struct node_t
{
    int data;
    struct node_t *next;
};

int main(int argc, char const *argv[])
{
    // 1.定义节点并初始化
    struct node_t a = {1, NULL};
    struct node_t b = {2, NULL};
    struct node_t c = {3, NULL};

    // 2.将节点进行连接
    a.next = &b;
    b.next = &c;

    // 3.定义一个头节点,指向第一个节点
    struct node_t s;
    s.next = &a;

    // 4.定义头指针指向头节点
    struct node_t *h = &s;

    // 5.遍历有头单向链表,方法1:
    // while (h->next != NULL)
    // {
    //     h = h->next;
    //     printf("%d ", h->data);
    // }

    //方法2:
    h=h->next;
    while (h != NULL)
    {
        printf("%d ",h->data);
        h=h->next;
    }
    
    printf("\n");
    return 0;
}

2)无头单向链表

所有节点的指针域和数据域都有效

遍历无头单向链表:

#include <stdio.h>

struct node_t
{
    int data;
    struct node_t * next;
};

int main(int argc, char const *argv[])
{
    //1.定义节点并初始化
    struct node_t a = {1,NULL};
    struct node_t b = {2,NULL};
    struct node_t c = {3,NULL};

    //2.将节点进行连接
    a.next = &b;
    b.next = &c;

    //3.定义头指针指向第一个节点
    struct node_t *h = &a;

    //4.遍历无头单向链表
    while(h!=NULL)
    {
        printf("%d ",h->data);
        h = h->next;
    }
    printf("\n");
    return 0;
}

2.1.2操作函数

linklist.h

#ifndef _LINKLIST_H_
#define _LINKLIST_H_

#include<stdio.h>
#include<stdlib.h>

typedef int datatype;
typedef struct node_t
{
	datatype data;//数据域
	struct node_t *next;//指针域,指向自身结构体的指针
}link_node_t,*link_list_t;

//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList();
//2.向单向链表的指定位置插入数据
//p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p,int post, datatype data);
//3.遍历单向链表
void ShowLinkList(link_node_t *p);
//4.求单向链表长度的函数
int LengthLinkList(link_node_t *p);
//5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post);
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p);
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data);
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data);
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data);
//10.转置链表
void ReverseLinkList(link_node_t *p);
//11.清空单向链表
void ClearLinkList(link_node_t *p);
#endif

1. 创建一个空的(有头)单向链表

2.向post位置插入一个数据

1、 先遍历找到要插入节点的前一个节点,假设这个节点为A;A的下一个节点为B;将C插入A与B之间;

2、 先让C的指针域指向B;

3、 再让A的指针域指向C;

注意:顺序不可以调换

3)删除指定位置的数据

1、先遍历找到要删除节点的前一个节点,假设为A;

2、找一个临时指针指向要删除的节点;

3、 将A的指针域指向删除节点的下一个节点;

4、 释放被删除节点

4)单向链表的转置

转置的思想:

(1) 将头节点与当前链表断开,断开前保存下头节点的下一个节点,保证后面链表能找得到,定义一个q保存头节点的下一个节点,断开后前面相当于一个空的链表,后面是一个无头的单向链表

(2) 遍历无头链表的所有节点,将每一个节点当做新节点插入空链表头节点的下一个节点(每次插入的头节点的下一个节点位置)

linklist.c

#include "linklist.h"

// 1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList()
{
    // 1.开辟一个结构体大小的空间存放头节点
    link_list_t p = (link_list_t)malloc(sizeof(link_node_t));
    if (p == NULL)
    {
        perror("CreateEpLinkList err");
        return NULL;
    }
    // 2.初始化头节点
    p->next = NULL;

    return p;
}
// 4.求单向链表长度的函数
int LengthLinkList(link_node_t *p)
{
    int len = 0;
    while (p->next != NULL)
    {
        len++;
        p = p->next;
    }
    return len;
}

// 2.向单向链表的指定位置插入数据
// p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p, int post, datatype data)
{
    // 1.容错处理
    if (post < 0 || post > LengthLinkList(p))
    {
        perror("InsertIntoPostLinkList err");
        return -1;
    }
    // 2.移动头指针,指向插入位置前一个节点
    for (int i = 0; i < post; i++)
        p = p->next;

    // 3.创建一个新节点,保存即将插入的数据
    link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
    if (pnew == NULL)
    {
        perror("pnew malloc err");
        return -1;
    }
    pnew->data = data;
    pnew->next = NULL;

    // 4.将新节点插入链表中(先连后面,再连前面)
    pnew->next = p->next;
    p->next = pnew;

    return 0;
}
// 6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p)
{
    return p->next == NULL;
}

// 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post)
{
    // 1.容错判断
    if (post < 0 || post >= LengthLinkList(p) || IsEpLinkList(p))
    {
        perror("DeletePostLinkList err");
        return -1;
    }
    // 2.移动头指针,让它指向被删除位置的前一个节点
    for (int i = 0; i < post; i++)
        p = p->next;

    // 3.进行删除操作
    // 1)定义一个指针pdel指向被删除节点
    link_list_t pdel = p->next;
    // 2)跨过被删除节点
    p->next = pdel->next;
    // 3)释放被删除节点
    free(pdel);
    pdel = NULL;
    return 0;
}

// 3.遍历单向链表
void ShowLinkList(link_node_t *p)
{
    while (p->next != NULL)
    {
        p = p->next;
        printf("%d ", p->data);
    }
    printf("\n----------------------\n");
}
// 7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data)
{
    // 1.容错判断
    if (post < 0 || post >= LengthLinkList(p) || IsEpLinkList(p))
    {
        perror("ChangePostLinkList err");
        return -1;
    }
    // 2.移动头指针,让它指向被修改的节点
    for(int i = 0;i<=post;i++)
        p=p->next;
    // 3.修改数据
    p->data=data;

    return 0;
}
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data)
{
    //1.定义一个变量记录查找的位置
    int post  = 0;

    //2.遍历有头单向链表
    while(p->next != NULL )
    {
        p=p->next;
        if(p->data == data)
            return post;
        post++;
    }
    return -1;//不存在
}
//9.删除单向链表中出现的指定数据data,代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data)
{
    //1.定义一个指针q,始终指向p的下一个节点
    link_list_t q = p->next;
    //2.用q遍历无头单向链表
    while(q != NULL)
    {
        if(q->data == data)//找到了要删除的节点
        {
            p->next = q->next;//跨过被删除节点
            free(q);//释放被删除节点
            q=p->next;//q重新指向p的下一个节点(新)
        }
        else  
        {
            p=p->next;//将头指针指向下一个节点
            q=p->next;//始终保证q指向p的下一个节点
        }
    }
    return 0;
}
//11.清空单向链表
void ClearLinkList(link_node_t *p)
{
    link_list_t pdel = NULL;
    while (p->next != NULL)
    {
        //(1)将pdel指向被删除节点即头节点下一个节点
        pdel = p->next;
        //(2)跨过被删除节点
        p->next = pdel->next;
        //(3)释放被删除节点
        free(pdel);
        pdel = NULL;
    } 
}

//10.转置链表
void ReverseLinkList(link_node_t *p)
{
    //临时保存q节点的下一个节点
    link_list_t temp = NULL;
    //1.断开前,保存头节点的下一个节点的地址(此时q相当于指向无头单向链表的头指针)
    link_list_t q  = p->next;
    //2.断开链表
    p->next = NULL;
    //3.遍历无头链表
    while (q != NULL)
    {
        //提前保存q的下一个节点
        temp=q->next;

        //先连后面再连前面(头插)
        q->next = p->next;
        p->next = q;

        //让q重新指向当前无头单向链表的第一个节点
        q = temp;
    }
    
}

main.c

#include"linklist.h"

int main(int argc, char const *argv[])
{
    link_list_t p =CreateEpLinkList();
    InsertIntoPostLinkList(p,0,1);
    InsertIntoPostLinkList(p,1,2);
    InsertIntoPostLinkList(p,2,3);
    ShowLinkList(p);//1 2 3 
    ReverseLinkList(p);
    ShowLinkList(p);//3 2 1
    printf("len is %d\n",LengthLinkList(p));//3
    DeletePostLinkList(p,0);
    ShowLinkList(p);// 2 1
    printf("%d post is %d\n",2,SearchDataLinkList(p,2));//2  0
    DeleteDataLinkList(p,2);
    ShowLinkList(p);//1
    DeletePostLinkList(p,0);
    if(IsEpLinkList(p))
        printf("IsEpLinkList\n");
    free(p);
    p = NULL;
    return 0;
}

Makefile

CC=gcc
CFLAGS=-c -g -o
OBJS=main.o linklist.o

linklist:$(OBJS)
	$(CC) $^ -o $@
%.o:%.c 
	$(CC) $(CFLAGS) $@ $<

.PHONY:clean
clean:
	rm *.o linklist -f

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值