数据结构——线性表的单链表存储(优缺点及代码实现:C语言)

链式存储

优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针

两部分组成(数据域 + 指针域)
逻辑上相邻的数据元素在物理上不一定相邻
访问只能通过头指针进入链表 并通过每个元素指针域顺序查找其他元素节点(顺序存取法)

综上:该存储方式适合频繁插入删除 场景,不适合频繁查找访问数据

代码实现(C语言)

定义链表

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

// 定义链表节点数据类型
typedef int DataType;

// 定义链表节点结构
typedef struct Node
{
    DataType data;
    struct Node *next;
} LinkNode, *LinkList;   //这两个都是结构体的别名,
//LinkNode *p 代表定义一个指针类型的节点p(强调节点),也可以表示为 LinkList p;(强调是一个单链表),为了可读性

初始化

LinkList InitList()
{
    LinkList L;				
    L = (LinkNode *)malloc(sizeof(LinkNode));    //内存分配节点  c++ 是 L=new LinkNode;
    if (L == NULL)
    {
        printf("内存分配失败");
        exit(1);
    }
    L->next = NULL;
    return L;
}

使用头插法建立单链表

时间复杂度O(n)
头插法是逆序插入,在头节点后,次新节点前插入新节点

void CreateList_H(LinkList L, int n)		//n代表节点数
{
	int i;					//当前节点序号
    LinkNode *s;
    for (i = 0; i < n; i++)            //循环建立、插入 n个节点
    {
        s = (LinkNode *)malloc(sizeof(LinkNode));
        if (s == NULL)
        {
            printf("内存分配失败");
            exit(1);
        }
        printf("请输入第 %d 个元素值: ", i + 1);    //节点序号是从0开始的
        scanf("%d", &s->data);    //输入的值传给节点的data域
        s->next = L->next;		  //节点插入在头指针的下一个,也就是其他节点之前
        L->next = s;			  //连接起来	
    }
}

尾插法建立单链表

时间复杂度O(n)
顺序插入,定义一个尾指针,循环在尾指针的下一个插入新节点,最后再将最后一个节点的next域设空。

void CreateList_R(LinkList L, int n)
{
    LinkNode *s, *r;  //s是新节点的指针,r是尾指针
	int i;
    r = L;
    for (i = 0; i < n; i++)   //循环建立、插入 n个节点
    {
        s = (LinkNode *)malloc(sizeof(LinkNode));
        if (s == NULL)
        {
            printf("内存分配失败");
            exit(1);
        }
        printf("请输入第 %d 个元素值: ", i + 1);  
        scanf("%d", &s->data);  
        r->next = s;  //在最后一个节点后面插入新节点s
        r = s;		//尾指针r移动到 新插入的节点s处
    }
    r->next = NULL;		//循环结束后最后一个节点的next域指向空
}

ps:如果不懂头插法和尾插法如何实现的可以看下这个博主画的动图,很生动形象:
链接: 单链表之头插法和尾插法(动图+图解)

单链表判空

int IsEmpty(LinkList L)
{
	return L->next ==NULL;  //若为真返回1 表示空表,为假返回0
}

获取表长

定义一个计数器,遍历一遍链表,计数器++;
时间复杂度O(n)

int GetLength(LinkList L)
{
	int length = 0;
	LinkNode *p = L->next;  //定义一个指针指向第一个节点
	while(p)            //与while(p!= NULL)等价
	{
		length++;
		p = p->next;  
	}
	return length;   //返回表长
}

按值查找

循环链表,从第一个元素向后一个个进行值的比对
时间复杂度O(n)

int SearchByValue(LinkList L, DataType key)
{
    int index = 1;
    LinkNode *p = L->next;
    while (p)		//与while(p!= NULL)等价
    {
        if (p->data == key)
        {
            return index;
        }
        p = p->next;
        index++;
    }
    return 0;    //如果没找到与key相匹配的元素 返回0
}

按位查找

时间复杂度O(n)
建一个计数器,遍历链表,计数器++直到和位置相等。最后返回查找元素的值

DataType SearchByPosition(LinkList L, int i)
{
    LinkNode *p = L->next;
    int j = 1;
    while (p != NULL && j < i)
    {
        p = p->next;
        j++;
    }
    if (p == NULL || j > i)
    {
        printf("未找到第 %d 个节点,可能链表为空或位置不正确", i);
        exit(1);
    }
    return p->data;
}

插入元素

第一步:先找到所查位置的前一个节点
第二步: 元素分配节点并赋值
第三步: 将节点插入进去,先插“尾巴” 后连“头”.
插入操作时间复杂度O(1),查找O(n)

int InsertElem(LinkList L, int i, DataType key)
{
    LinkNode *p, *s;
    int j = 0;
    p = L;           //新节点可以插入在第一个节点之前
    while (p && j < i - 1)  //找到需要插入位置的前一个节点
    {
        p = p->next;
        j++;
    }
    if (!p || j > i - 1)  //!p 和 p ==NULL 等价
    {
        printf("未找到第 %d 个节点,可能链表为空或位置不正确", i - 1);
        return 0;
    }
    s = (LinkNode *)malloc(sizeof(LinkNode));
    if (s == NULL)
    {
        printf("内存分配失败,无法插入新节点");
        return 0;
    }
    s->data = key;  //新节点的数据域 赋值
    s->next = p->next;   //插入先插“尾巴”
    p->next = s;		 //后插“头”
    return 1;
}

删除元素

第一步:先找到所查位置的前一个节点
第二步: 断开连接
第三步:将要删节点的值取出来,释放节点内存
删除操作时间复杂度O(1),查找O(n)

int DelElem(LinkList L, int i)
{
    LinkNode *p, *q;
    int j = 0;
    DataType x;  //要删除元素的值
    p = L;
    while (p->next && j < i - 1)  //删除不能删最后一个的下一个,增加可以增加到最后一个的下一个
    {
        p = p->next;
        ++j;
    }
    if (!(p->next) || j > i - 1)
    {
        return 0;
    }
    q = p->next;    //把要删除的节点“拎”出来 给q
    p->next = q->next;   //要删除的节点断开连接
    x = q->data;     //将删除节点的值 赋给x
    free(q);      //释放节点内存
    return 1;
}

清空链表

时间复杂度O(n)
有一个中间指针用来存储下一个节点的地址,循环赋值 释放;

void ClearList(LinkList L)
{
	LinkNode *p=L->next;
	LinkNode *q;
	while(p)
	{
		q=p->next;
		free(p);
		p=q;
	}
	L->next =NULL;   
}

ps:清空和销毁的区别是,清空之后是个表,还有头节点,只不过头节点指向空,销毁之后就没表了也没头节点.

销毁链表

时间复杂度O(n)

void DestroyList(LinkList L)
{
	LinkNode *p=L;
	while(p)
	{
		L=L->next;
		free(p);
		p=L;
	}
}

打印链表

时间复杂度O(n)

void DispList(LinkList L)
{
    LinkNode *p = L->next;
    while (p != NULL)
    {
        printf("%5d", p->data);  //输出链表中的值
        p = p->next;
    }
}

完整示例(C语言实现)

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

// 定义链表节点数据类型
typedef int DataType;

// 定义链表节点结构
typedef struct Node
{
    DataType data;
    struct Node *next;
} LinkNode, *LinkList;

// 初始化链表
LinkList InitList()
{
    LinkList L;
    L = (LinkNode *)malloc(sizeof(LinkNode));
    if (L == NULL)
    {
        printf("内存分配失败");
        exit(1);
    }
    L->next = NULL;
    return L;
}

// 使用头插法创建链表
void CreateList_H(LinkList L, int n)
{
	int i;
    LinkNode *s;
    for (i = 0; i < n; i++)
    {
        s = (LinkNode *)malloc(sizeof(LinkNode));
        if (s == NULL)
        {
            printf("内存分配失败");
            exit(1);
        }
        printf("请输入第 %d 个元素值: ", i + 1);
        scanf("%d", &s->data);
        s->next = L->next;
        L->next = s;
    }
}

// 使用尾插法创建链表
void CreateList_R(LinkList L, int n)
{
    LinkNode *s, *r;
	int i;
    DataType x;
    r = L;
    for (i = 0; i < n; i++)
    {
        s = (LinkNode *)malloc(sizeof(LinkNode));
        if (s == NULL)
        {
            printf("内存分配失败");
            exit(1);
        }
        printf("请输入第 %d 个元素值: ", i + 1);
        scanf("%d", &x);
        s->data = x;
        r->next = s;
        r = s;
    }
    r->next = NULL;
}

// 检查链表是否为空
int IsEmpty(LinkList L)
{
    return L->next == NULL;
}

// 获取链表长度
int GetLength(LinkList L)
{
    int length = 0;
    LinkNode *p = L->next;
    while (p != NULL)
    {
        length++;
        p = p->next;
    }
    return length;
}

// 按值查找元素
int SearchByValue(LinkList L, DataType key)
{
    int index = 1;
    LinkNode *p = L->next;
    while (p != NULL)
    {
        if (p->data == key)
        {
            return index;
        }
        p = p->next;
        index++;
    }
    return 0;
}

// 按位置查找元素
DataType SearchByPosition(LinkList L, int i)
{
    LinkNode *p = L->next;
    int j = 1;
    while (p != NULL && j < i)
    {
        p = p->next;
        j++;
    }
    if (p == NULL || j > i)
    {
        printf("未找到第 %d 个节点,可能链表为空或位置不正确", i);
        exit(1);
    }
    return p->data;
}

// 插入元素
int InsertElem(LinkList L, int i, DataType key)
{
    LinkNode *p, *s;
    int j = 0;
    p = L;
    while (p && j < i - 1)
    {
        p = p->next;
        j++;
    }
    if (!p || j > i - 1)
    {
        printf("未找到第 %d 个节点,可能链表为空或位置不正确", i - 1);
        return 0;
    }
    s = (LinkNode *)malloc(sizeof(LinkNode));
    if (s == NULL)
    {
        printf("内存分配失败,无法插入新节点");
        return 0;
    }
    s->data = key;
    s->next = p->next;
    p->next = s;
    return 1;
}

// 删除元素
int DelElem(LinkList L, int i)
{
    LinkNode *p, *q;
    int j = 0;
    DataType x;
    p = L;
    while (p->next && j < i - 1)
    {
        p = p->next;
        ++j;
    }
    if (!(p->next) || j > i - 1)
    {
        return 0;
    }
    q = p->next;
    p->next = q->next;
    x = q->data;
    free(q);
    return 1;
}
//清空链表
void ClearList(LinkList L)
{
	LinkNode *p=L->next;
	LinkNode *q;
	while(p)
	{
		q=p->next;
		free(p);
		p=q;
	}
	L->next =NULL;
}
//销毁链表
void DestoryList(LinkList L)
{
	LinkNode *p=L;
	while(p)
	{
		L=L->next;
		free(p);
		p=L;
	}
}

// 打印链表
void DispList(LinkList L)
{
    LinkNode *p = L->next;
    while (p != NULL)
    {
        printf("%5d", p->data);
        p = p->next;
    }
}

// 菜单函数
void MenuLine()
{
    printf("\n                  线性表子系统");
    printf("\n =================================================");
    printf("\n|               1 - 初始化链表               |");
    printf("\n|               2 - 头插法创建链表           |");
    printf("\n|               3 - 尾插法创建链表           |");
    printf("\n|               4 - 检查链表是否为空         |");
    printf("\n|               5 - 获取链表长度             |");
    printf("\n|               6 - 按值查找元素             |");
    printf("\n|               7 - 按位置查找元素           |");
    printf("\n|               8 - 插入元素                 |");
    printf("\n|               9 - 删除元素                 |");
	printf("\n|               10 -清空元素                 |");
	printf("\n|               11 -销毁列表                 |");
	printf("\n|               12- 打印列表                 |");
    printf("\n|               0 - 返回                     |");
    printf("\n =================================================");
    printf("\n请输入菜单编号 (0-9): ");
}

int main()
{
    LinkList L = NULL;
    DataType x;
    int i, n;
    char ch1, ch2;
    ch1 = 'y';
    while (ch1 == 'y' || ch1 == 'Y')
    {
        MenuLine();
        scanf(" %c", &ch2);
        getchar();
        switch (ch2)
        {
        case '1':
            L = InitList();
            printf("链表已初始化");
            break;
        case '2':
            printf("请输入要创建的线性表长度: ");
            scanf("%d", &n);
            CreateList_H(L, n);
            printf("使用头插法创建的链表:\n");
            DispList(L);
            break;
        case '3':
            printf("请输入要创建的线性表长度: ");
            scanf("%d", &n);
            CreateList_R(L, n);
            printf("使用尾插法创建的链表:\n");
            DispList(L);
            break;
        case '4':
            if (IsEmpty(L))
            {
                printf("链表为空");
            }
            else
            {
                printf("链表非空");
            }
            break;
        case '5':
            printf("链表长度为: %d", GetLength(L));
            break;
        case '6':
            printf("请输入要查找的元素值: ");
            scanf("%d", &x);
            i = SearchByValue(L, x);
            if (i == 0)
            {
                printf("未找到该元素");
            }
            else
            {
                printf("元素值 %d 在第 %d 个位置", x, i);
            }
            break;
        case '7':
            printf("请输入要查找的元素位置: ");
            scanf("%d", &i);
            x = SearchByPosition(L, i);
            printf("第 %d 个元素值为: %d", i, x);
            break;
        case '8':
            printf("请输入要插入的元素位置和值: ");
            scanf("%d %d", &i, &x);
            if (InsertElem(L, i, x))
            {
                printf("插入成功");
            }
            else
            {
                printf("插入失败");
            }
            break;
        case '9':
            printf("请输入要删除的元素位置: ");
            scanf("%d", &i);
            if (DelElem(L, i))
            {
                printf("删除成功");
            }
            else
            {
                printf("删除失败");
            }
            break;
		case '10':
		    ClearList(L);
			printf("链表已清空");
    		break;
		case '11':
			DestroyList(L);
			printf("链表已销毁");
			L = NULL; // 将链表指针置为NULL
			break;
		case '12':
            printf("当前链表内容为: ");
            DispList(L);
            break;
        case '0':
            return 0;
        default:
            printf("无效菜单选项");
            break;
        }
    }
    return 0;
}

结语

敬请各位大佬批评指正 日后也会持续更新数据结构相关内容,互相学习😊😊
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值