数据结构之--链表(C语言实现),极力推荐!!

数组结构之----- 链表

1. 链表基本概念

1.1 什么是链表

  • 链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序存储)。
  • 数据域用来存储数据,指针域用于建立与下一个结点的联系。
  • 建立链表时无需预先知道数据总量的,可以随机的分配空间,可以高效的在链表中的任意位置实时插入或删除数据。
  • 链表的开销,主要是访问顺序性和组织链的空间损失

在这里插入图片描述

1.2 数组和链表的区别

数组:
  • 一次性分配一块连续的存储区域。
  • 随机访问元素效率高
  • 需要分配一块连续的存储区域(很大区域,有可能分配失败)
  • 删除和插入某个元素效率低
链表:
  • 无需一次性分配一块连续的存储区域,只需分配n块节点存储区域,通过指针建立关系;
  • 不需要一块连续的存储区域
  • 删除和插入某个元素效率高
  • 随机访问元素效率低

1.3 链表节点

  • 链表的节点类型实际上是结构体变量,此结构体包含数据域指针域
    • 数据域用来存储数据;
    • 指针域用于建立与下一个结点的联系,当此节点为尾节点时,指针域的值为NULL;

在这里插入图片描述

2. 链表的分类

2.1 静态链表

所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“静态链表”

示例
struct linkNode {
    int No;
    char name[64];
    struct linkNode *next;
};

void initNodes() {
    struct linkNode linkNode1 = {1, "node1", NULL};
    struct linkNode linkNode2 = {2, "node2", NULL};
    struct linkNode linkNode3 = {3, "node3", NULL};
    struct linkNode linkNode4 = {4, "node4", NULL};
    linkNode1.next = &linkNode2;
    linkNode2.next = &linkNode3;
    linkNode3.next = &linkNode4;
    //遍历链表
    struct linkNode *p = &linkNode1;
    while (p != NULL) {
        printf("N0=%d name=%s\n", p->No, p->name);
        p = p->next;
    }
    //N0=1 name=node1
    //N0=2 name=node2
    //N0=3 name=node3
}

void initNodesInHeap() {
    //堆区创建节点
    struct linkNode *node1 = malloc(sizeof(struct linkNode));
    struct linkNode *node2 = malloc(sizeof(struct linkNode));
    struct linkNode *node3 = malloc(sizeof(struct linkNode));
    struct linkNode *node4 = malloc(sizeof(struct linkNode));

    node1->No = 1;
    strcpy(node1->name,"node1");
    node1->next = node2;

    node2->No = 2;
    strcpy(node2->name,"node2");
    node2->next = node3;

    node3->No = 3;
    strcpy(node3->name,"node3");
    node3->next = node4;

    node4->No = 4;
    strcpy(node4->name,"node4");
    node4->next = NULL;

    //遍历
    struct linkNode *p = node1;
    while (p != NULL) {
        printf("N0=%d name=%s\n", p->No, p->name);
        p = p->next;
    }
    //释放内存
    free(node4);
    free(node3);
    free(node2);
    free(node1);
}

int main() {
    //initNodes();
    initNodesInHeap();
    return 0;
}

2.2 动态链表

动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系

示例
typedef struct Stu{
	int id;	//数据域
	char name[100];

	struct Stu *next; //指针域
}Stu;

void test(){
	//动态分配3个节点
	Stu *s1 = (Stu *)malloc(sizeof(Stu));
	s1->id = 1;
	strcpy(s1->name, "yuri");

	Stu *s2 = (Stu *)malloc(sizeof(Stu));
	s2->id = 2;
	strcpy(s2->name, "lily");

	Stu *s3 = (Stu *)malloc(sizeof(Stu));
	s3->id = 3;
	strcpy(s3->name, "lilei");

	//建立节点的关系
	s1->next = s2; //s1的next指针指向s2
	s2->next = s3;
	s3->next = NULL; //尾结点

	//遍历节点
	Stu *p = s1;
	while (p != NULL)
	{
		printf("id = %d, name = %s\n", p->id, p->name);

		//结点往后移动一位
		p = p->next; 
	}

	//释放节点空间
	p = s1;
	Stu *tmp = NULL;
	while (p != NULL)
	{
		tmp = p;
		p = p->next;

		free(tmp);
		tmp = NULL;
	}
}

2.3 带头链表

带头链表:固定一个节点作为头结点(数据域不保存有效数据),起一个标志位的作用,以后不管链表节点如果改变,此头结点固定不变

2.4 不带头链表

不带头链表:头结点不固定,根据实际需要变换头结点(如在原来头结点前插入新节点,然后,新节点重新作为链表的头结点)。

2.5 单向链表、双向链表、循环链表

单向链表

在这里插入图片描述

双向链表

在这里插入图片描述

循环链表

在这里插入图片描述

3. 链表基本操作(重点)

3.1 创建链表

struct linkNode {
    int No;
    char name[64];
    struct linkNode *next;
};

struct linkNode *headNode = NULL;

/**
 * @method 初始化头结点
 */
void initHeadNode() {
    headNode = malloc(sizeof(struct linkNode));
    headNode->No = 1;
    strcpy(headNode->name, "headNode");
    headNode->next = NULL;
    //printMsg();
}

/**
 * @method 创建新节点
 * @param no
 * @param name
 * @return
 */
struct linkNode *createNode(int no, char name[64]) {
    struct linkNode *newNode = malloc(sizeof(struct linkNode));
    newNode->No = no;
    strcpy(newNode->name, name);
    newNode->next = NULL;
    return newNode;
}

/**
 * @method 向链表的尾部添加节点
 * @param newNode
 */
void addNodeAtEnd(struct linkNode *newNode) {
    //找到末尾节点
    struct linkNode *curr = headNode;
    while (curr->next != NULL) {
        curr = curr->next;
    }
    curr->next = newNode;
}

3.2 遍历链表

void printMsg() {
    struct linkNode *p = headNode;
    while (p != NULL) {
        printf("NO=%d name=%s\n", p->No, p->name);
        p = p->next;
    }
}

3.3 链表插入

/**
 * @method 往链表中插入节点
 * @param newNode
 */
void insertNodeByNo(struct linkNode *newNode) {
    struct linkNode *curr = headNode;
    while (curr != NULL) {
        if (newNode->No > curr->No && newNode->No < curr->next->No) {
            break;
        }
        curr = curr->next;
    }
    newNode->next = curr->next;
    curr->next = newNode;
}

3.3 删除链表中的节点

/**
 * @method 删除节点数据
 * @param No
 */
void deleteNode(int No) {
    struct linkNode *curr = headNode;
    while (curr != NULL) {
        if (curr->No == No) {
            break;
        }
        curr = curr->next;
    }
    if (curr == NULL) {
        printf("no such node\n");
        return;
    } else {
        struct linkNode *p = curr;
        curr->No = curr->next->No;
        strcpy(curr->name, curr->next->name);
        p = curr->next;
        curr->next = curr->next->next;
        free(p);
    }
}

3.4 清空链表

/**
 * @method :清空链表
 */
void clearLinkedList() {
    if (headNode == NULL) {
        perror("no node in list");
        return;
    }
    struct linkNode *tmp = headNode->next;
    while (tmp != NULL) {
        struct linkNode *nextNode = tmp->next;
        free(tmp);
        tmp = nextNode;
    }
    headNode->next = NULL;
}

3.5 销毁链表

/**
 * @method 清空链表
 */
void destroyLinkedList() {
    if (headNode == NULL) {
        perror("no node in list");
        return;
    }
    //链表中有元素
    clearLinkedList();
    //清空头结点
    free(headNode);
    headNode = NULL;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值