单链表(支持增、删、反转)

什么是链表

链表(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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值