前提知识
- 结构体
- typedef
- 指针
- 面向对象(后续)
基本概念
链表,linked list
一种数据结构,结构体+指针
1 | //这是一个声明的内容,不是具体的 |
结构体名称 | 结构体元素 | 指针 |
---|---|---|
Button(也是该结构体的地址) | 多个属性 | 新的Button结构体地址(即下一个按键结构体) |
地址 | 数据 | 下一个的地址 |
相当于第一个记住第二个的地址,第二个记住第三个的地址,一直到最后一个,一般指向空NULL,这就是单链表。
如果最后一个指向第一个,则是循环链表;
如果每个结构体既指向前一个结构体,又指向后一个结构体,就是双向链表;
如果第一个与最后一个互相指向,则是双向循环链表。
另外,每个结构体有了新的名字,节点Node;第一个节点叫头节点,最后一个叫尾节点。
这里只记录单链表。
结构体指针与结构体
1 | struct Button* next; //定义一个结构体指针next,它是该结构体的地址 |
简化定义
typedef 起别名,简化结构体的定义
1 | typedef struct Button { |
链表操作
单链表
单链表=头节点+n个节点组成
- 头节点:没有数据,只有指向下一个节点的指针
- 尾节点:指针为空NULL
定义
1
2
3
4typedef struct __LNode{
int data;
__LNode *next;
}LNode, *LinkList;初始化
1
2
3
4
5
6
7
8
9
10
11
12
13int InitLL(LinkList *L)//L是个二级指针,函数可以修改调用者传入的指针变量的值。
{
//(*L) 表示通过二级指针解引用,获取调用者传入的指针变量的值。
(*L) = (LinkList)malloc(sizeof(LNode));//动态分配一块内存,大小为 LNode 类型的大小,并强转为(LinkList)
if ((*L) == NULL)
{
return -1; // 表示内存分配失败
}
//(*L) 是头结点的地址
(*L)->next = NULL;// (*L)->next表示访问头结点的next指针,使其设为NULL
return 0;
}调用方式
1
2LinkList L;
InitLL(&L); // L 现在指向一个头结点,头结点的 next 指针为 NULL
创建链表
头插法
O(1)
1
2
3
4
5
6
7
8
9void InsertHead(LinkList *L, int data)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode)); // 创建新节点
newNode->data = data; // 设置新节点的数据
newNode->next = (*L)->next; // 新节点指向当前头结点的下一个节点
(*L)->next = newNode; // 更新头结点的 next 指针
}尾插法
O(n)
1
2
3
4
5
6
7
8
9
10
11
12
13void InsertTail(LinkList *L, int data)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode)); // 创建新节点
newNode->data = data; // 设置新节点的数据
newNode->next = NULL; // 新节点的 next 指针指向 NULL
LNode* tail = *L; // 从头结点开始
while (tail->next != NULL) // 遍历链表,找到最后一个节点
{
tail = tail->next;
}
tail->next = newNode; // 将新节点插入到链表尾部
}O(1)
1
2
3
4
5
6
7
8
9
10
11
12
13void InsertTailOptimized(LinkList *L, LNode** tail, int data)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode)); // 创建新节点
newNode->data = data; // 设置新节点的数据
newNode->next = NULL; // 新节点的 next 指针指向 NULL
if (*tail == NULL) { // 如果链表为空
(*L)->next = newNode; // 新节点成为第一个节点
} else {
(*tail)->next = newNode; // 将新节点插入到尾部
}
*tail = newNode; // 更新尾指针
}
插入元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//p为原链表,elem表示新数据元素,add表示新元素要插入的位置
link * insertElem(link * p, int elem, int add)
{
link * temp = p;//创建临时结点temp
//首先找到要插入位置的上一个结点
for (int i = 1; i < add; i++)
{
temp = temp->next;
if (temp == NULL)
{
printf("插入位置无效\n");
return p;
}
}
//创建插入结点c
link * c = (link*)malloc(sizeof(link));
c->elem = elem;
//向链表中插入结点
c->next = temp->next;
temp->next = c;
return p;
}删除元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//p为原链表,add为要删除元素的值
link * delElem(link * p, int add)
{
link * temp = p; //遍历到被删除结点的上一个结点
for (int i = 1; i < add; i++)
{
temp = temp->next;
if (temp->next == NULL)
{
printf("没有该结点\n");
return p;
}
}
link * del = temp->next;//单独设置一个指针指向被删除结点,以防丢失
temp->next = temp->next->next;//删除某个结点的方法就是更改前一个结点的指针域
free(del);//手动释放该结点,防止内存泄漏
return p;
}查找元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//p为原链表,elem表示被查找元素、
int selectElem(link * p,int elem)
{
//新建一个指针t,初始化为头指针 p
link * t=p;
int i=1;
//由于头节点的存在,因此while中的判断为t->next
while (t->next)
{
t=t->next;
if (t->elem==elem)
{
return i;
}
i++;
}
//程序执行至此处,表示查找失败
return -1;
}
示例代码
源码
1 |
|
运行结果:
1 | 初始化链表为: |