简介
以顺序结构进行数据存储时,它的特点就是可以用一组任意的存储单元存储数据元素,这组存储单元可以是连续的,也可以是不连续的,这些数据可以存在内存未被占用的任意位置。它也是有缺点的,就是在插入和删除时需要移动大量的元素,需要耗费一点时间。
以下是它的一个示意图
想要创建这样的数据结构,首先需要使用结构体先定义一个节点:
typedef struct node
{
int data; // 数据
struct node *next; // 指向下一个节点的地址
}node_t;
然后需要使用结构体定义单链表:
typedef struct list
{
struct node *head; // 头部
struct node *tail; // 尾部
}list_t;
相关函数
- 首先需要定义一个静态函数用于创建节点
static node_t *create_node(int data)
{
node_t *pnew = malloc(sizeof(node_t)); // 分配内存空间
pnew->data = data; // 分配数据
pnew->next = NULL; // 默认指向空
return pnew; // 返回地址
}
- 初始化函数
void list_init(list_t *plist)
{
plist->head = create_node(0); // 为头部创建一个节点
plist->tail = create_node(0); // 为尾部创建一个节点
plist->head->next = plist->tail; // 头部的next指向尾部
plist->tail->next = NULL; // 尾部的next指向NULL
}
- 释放单链表的函数
void list_deinit(list_t *plist)
{
node_t *pnode = plist->head; // 将pnode指向头部
// 当pnode非空时,进行循环,说明还有数据
while(pnode)
{
node_t *ptmp = pnode->next; // 备份pnode的next
free(pnode); // 释放pnode
pnode = ptmp; // 将之前pnode的next指向现在的pnode
}
}
- 遍历单链表
void list_travel(list_t *plist)
{
// 遍历: 首先将pnode指向head,直到pnode指向tail结束,pnode每次往后移一位
for(node_t *pnode = plist->head; pnode != pnode->tail; pnode = pnode->next)
{
// 三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
if(pmid != pnode->tail)
printf("%d ", pmid->data);
}
printf("\n");
}
- 按照顺序添加数据到单链表中
void list_add(list_t *plist, int data)
{
// 1. 创建一个新的节点
node_t *pnew = create_node(data);
// 2. 遍历链表
for(node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 2.1 三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 2.2 当找到比data大的数据,就插入到它后面,或者找到最后都没找到比它大的,就插到最后
if(pmid->data > pnew->data || pmid == plist->tail)
{
pfirst->next = pnew;
pnew->next = pmid;
break;
}
}
}
- 前插函数(将数据插到最前面)
void list_add_first(list_t *plist, int data)
{
// 1. 创建一个新的节点
node_t *pnew = create_node(data);
// 2. 备份头部的next
node_t *ptmp = plist->head->next;
// 3. 将头部的next指向新节点
plist->head->next = pnew;
// 4. 将新节点的next指向之前头部指向next
pnew->next = ptmp;
}
- 后插函数(将数据插到最后面)
void list_add_last(list_t *plist, int data)
{
// 1. 创建一个新的节点
node_t *pnew = create_node(data);
// 2. 遍历链表
for(node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 2.1 三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 2.2 当pmid执行plist->tail时,插入
if(pmid == plist->tail)
{
pfirst->next = pnew;
pnew->next = pmid;
break;
}
}
}
- 删除指定数据所在的节点
void list_del(list_t *plist, int data)
{
// 1. 遍历链表
for(node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 1.1 三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 1.2 当找到数据相等的,并且此数据不是尾节点,因为尾节点是初始化定义的
if(data == pmid->data && pmid != plist->tail)
{
pfirst->next = plast;
free(pmid); // 此时pmid就是要删除的那个节点
break;
}
}
}
示例代码
创建三个文件: list.c、list.h、main.c,实现上面的相关函数
list.c
#include "list.h"
// 定义分配节点内存函数
static node_t *create_node(int data)
{
node_t *pnew = malloc(sizeof(node_t));
pnew->data = data;
pnew->next = NULL;
return pnew;
}
// 初始化
void list_init(list_t *plist)
{
// 1. 给首尾分配内存
plist->head = create_node(0);
plist->tail = create_node(0);
// 2. 头指向尾
plist->head->next = plist->tail;
// 3. 尾指向空
plist->tail->next = NULL;
}
// 释放
void list_deinit(list_t *plist)
{
// 1. 取到单链表的头部
node_t *pnode = plist->head;
// 2. 当头部不为空的时候,说明还有数据,继续循环
while (pnode)
{
// 2.1 备份pnode->next
node_t *ptmp = pnode->next;
// 2.2 释放pnode
free(pnode);
// 2.3 重新指定pnode是ptmp
pnode = ptmp;
}
}
// 遍历数据单链表
void list_travel(list_t *plist)
{
for (node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 创建三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 判断pmid是有效节点
if (pmid != plist->tail)
{
printf("%d ", pmid->data);
}
}
printf("\n");
}
// 按顺序添加数据到单链表中
void list_add(list_t *plist, int data)
{
// 1. 创建新的节点
node_t *pnew = create_node(data);
// 2. 遍历单链表
for (node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 2.1 创建三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 2.2 判断当找到pmid的数据大于等于data或者找到最后都没找到比data大的,就插到最后
if (pmid->data >= pnew->data || pmid == plist->tail)
{
// 2.2.1 放在pmid后面,pfirst的前面
pfirst->next = pnew;
pnew->next = pmid;
break;
}
}
}
// 前插函数
void list_add_first(list_t *plist, int data)
{
// 1. 创建新的节点
node_t *pnew = create_node(data);
// 2. 备份头部的next
node_t *ptmp = plist->head->next;
// 3. 将头部的next指向新的节点
plist->head->next = pnew;
// 4. 将新的节点next指向之前头部的next
pnew->next = ptmp;
}
// 后插函数
void list_add_last(list_t *plist, int data)
{
// 1. 创建新的节点
node_t *pnew = create_node(data);
// 2. 遍历节点,找到tail前面的节点
for (node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 2.1 创建三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 2.2 当pmid==tail时,说明pfirst是tail前面的节点
if (pmid == plist->tail)
{
// 2.2.1 将pfirst的next指向pnew
pfirst->next = pnew;
// 2.2.2 将pnew的next执行tail(pmid)
pnew->next = pmid;
break;
}
}
}
// 删除指定数字所在所在的节点
void list_del(list_t *plist, int data)
{
// 1. 遍历单链表
for (node_t *pnode = plist->head; pnode != plist->tail; pnode = pnode->next)
{
// 1.1 三个游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
// 1.2 当找到数据相等的,并且此数据不是尾节点,因为尾节点是初始化定义的
if (data == pmid->data && pmid != plist->tail)
{
// 1.2.1 将pfirst的next指向plast
pfirst->next = plast;
// 1.2.2 释放pmid
free(pmid);
break;
}
}
}
list.h
声明单链表的相关函数和定义节点和单链表
#ifndef __LIST_H
#define __LIST_H
#include <stdio.h>
#include <stdlib.h>
// 定义节点
typedef struct node
{
int data;
struct node *next;
} node_t;
// 声明单链表的结构体
typedef struct list
{
node_t *head; // 保存头节点的地址
node_t *tail; // 保存尾节点的地址
} list_t;
extern void list_init(list_t *plist);
extern void list_deinit(list_t *plist);
extern void list_travel(list_t *plist);
extern void list_add(list_t *plist, int data);
extern void list_add_first(list_t *plist, int data);
extern void list_add_last(list_t *plist, int data);
extern void list_del(list_t *plist, int data);
#endif
main.c
主函数使用单链表
#include "list.h"
int main(void)
{
// 1. 创建单链表
list_t list;
// 2. 初始化单链表
list_init(&list);
// 3. 插入三个数据10 30 20
printf("插入三个数据10 30 20,结果应该排好顺序的: ");
list_add(&list, 10);
list_add(&list, 30);
list_add(&list, 20);
// 4. 循环遍历输出单链表
list_travel(&list);
// 5. 在头部插入一个15
printf("在头部插入15: ");
list_add_first(&list, 15);
// 6.遍历输出单链表
list_travel(&list);
// 7. 在尾部插入一个1
printf("在尾部插入一个1: ");
list_add_last(&list, 1);
// 8. 遍历输出
list_travel(&list);
// 9. 删除一个20
printf("删除一个20: ");
list_del(&list, 20);
// 10. 遍历输出
list_travel(&list);
// 11. 释放整个链表
list_deinit(&list);
return 0;
}