什么是链表
链表(LinkList)也是一种线性表,是将一组零散的内存块串联起来,从而进行数据存储的数据结构。链表中的每一个内存块被称为结点Node。结点由两部分组成:数据域(info)——存放有效数据的,指针域(link)——存放后继结点的,也称为后继指针next。
单链表
每个结点都只有一个后继指针的链表称为单链表。单链表的第一个结点称为头结点(head),最后一个结点称为尾结点,单链表的尾结点指向一个空地址NULL。
单链表的插入
单链表的删除
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct D_LinkList
{
int val;
struct D_LinkList* next;
}LNode;
LNode* Create()
{
LNode* p = (LNode*)malloc(sizeof(LNode));
if (p == NULL)
{
printf("malloc error!\n");
return NULL;
}
p->next = NULL;
return p;
}
//尾插法
void Tail_plug(LNode* begin, int len)
{
LNode* head = begin;//带头结点,将链表头结点的地址传给begin
while (head->next != NULL)
{
head = head->next;
}
head = head->next = (LNode*)malloc(sizeof(LNode));
for (int i = 1; i <= len; i++)
{
head->val = i;
//重新申请空间
head = head->next = (LNode*)malloc(sizeof(LNode));
}
head->next = NULL;
}
//在任意位置插入数据
LNode* insert(LNode* head, int ind, int num) {
LNode ret, * p = &ret;
LNode* a = Create();
a->val = num;
a->next = NULL;
ret.next = head;
// 从【虚拟头节点】开始向后走 ind 步
while (ind--) p = p->next;
// 完成节点的插入操作
a->next = p->next;
p->next = a;
// 返回真正的链表头节点地址
return ret.next;
}
//删除任意位置的结点
LNode* delete(LNode* head, int ind)
{
LNode ret, * p = &ret;
ret.next = head;
while (ind--)
{
p = p->next;
}
p->next = p->next->next;
return ret.next;
}
void Print_LNode(LNode* PH)
{
LNode* p = PH->next;
while (p->next != NULL)
{
printf("data: %d\n", p->val);
p = p->next;
}
}
//链表的反转(其实就是头插法)
LNode* Reverse(LNode* begin)
{
LNode* L = (LNode*)malloc(sizeof(LNode)), *s;
L->next = NULL;
while (begin->next != NULL)
{
s = (LNode*)malloc(sizeof(LNode));
s->val = begin->val;
s->next = L->next;
L->next = s;
begin = begin->next;
}
return L;
}
int main()
{
int len;
printf("请定义链表的长度:");
scanf("%d", &len);
LNode* header = Create();
Tail_plug(header, len);
printf("最开始的链表:\n");
Print_LNode(header);
insert(header, 1, 110);
printf("将110插入到第1个位置后的链表:\n");
Print_LNode(header);
delete(header, 6);
printf("删除了第6个结点后的链表:\n");
Print_LNode(header);
header = Reverse(header);
printf("反转后的链表:\n");
Print_LNode(header);
free(header);
}
运行结果:
关于上个【数组】笔记末尾留下的两个小问题:
1、为什么数组的下标都是从0开始的?
两个原因:
①因为数组的首地址是数组第1个元素储存空间的起始位置,若用下标0标记第一个元素则通过寻址公式计算时,可直接使用下标计算,即a[0]_address = base_address + 0 * data_type_size。若用下标1标记第1个元素则通过寻址公式计算地址时,需要将下标值减1再计算,即a[1]_address = base_address + (1 - 1)* data_type_size,这样每次寻址计算都多了一步减法操作,增加了性能开销。
②历史原因,C语言设计者用 0 开始计数数组下标,之后的Java、JavaScript等高级语言都效仿C语言,目的是为了减少C程序员学习Java等其他高级语言的成本。
2、参照一维数组的寻址公式,你能给出二维数组的寻址公式吗?
假设二维数组的的维度为m * n,则(i < n, j < m)a[i][j]_address = base_address + (i * n + j ) * data_type_size。