单链表创建、单链表打印、一级和二级指针做参数、尾插、头插、尾删、头删、查找指定数值位置、指定位置之前插入、指定位置删除、指定位置之后插入、指定位置之后删除等的介绍

前言

单链表创建、单链表打印、一级和二级指针做参数、尾插、头插、尾删、头删、查找指定数值位置、指定位置之前插入、指定位置删除、指定位置之后插入、指定位置之后删除等的介绍


链表的图示

在这里插入图片描述

一、 单链表节点创建

单链表节点的创建

typedef int SLTDataType;

// 链表节点类型的创建
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;
  • data的类型使用typedef进行重命名,方便修改data的类型
  • 节点的第二个成员为 下一个节点的指针(地址)。

二、 单链表的打印

单链表的打印定义

// 单链表的打印
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;

	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");

}
  • phead是一个指针变量,他存放的是单链表第一个节点的地址,通过 -> 可以访问第一个节点的成员。
  • 将phead存放到变量cur中,防止可能再次使用无法找到phead。
  • 判断若cur不为NULL,则打印当前cur的data。并且将cur置为cur->next(下一个节点的地址)。
  • 判断若cur为NULL,跳出循环。打印NULL。

三、 一级和二级指针做参数

  • 对于一个整形变量,我们若要通过函数来改变这个变量的值。
  • 直接参数是无法实现的,因为形参是实参的一份临时拷贝。如下:
void func(int n)
{
	n = 1;
}

int main()
{
	int a = 0;
	
	func(a);
	printf("%d ", a);// 0
}
  • 若通过函数改变一个整形变量的数值,可以通过传指针(地址),在函数中解引用的方式来实现。如下:
void func1(int* n)
{
	*n = 1;
}

int main()
{
	int a = 0;
	
	func1(&a);
	printf("%d ", a);// 1
}
  • 同理可得,若通过函数改变一个一级指针变量,传入一个一级指针是无法实现的,如下:
void func2(int* n)
{
	n = (int*)malloc(sizeof(int));
}

int main()
{
	int* pa = NULL;
	func2(pa);
	printf("%p", pa);// 0000000000000000
}
  • 同理可得,若要通过函数改变一个一级指针变量,需要传入一个二级指针。
  • 在函数中对二级指针进行解引用来改变一级指针变量。如下:
void func3(int** n)
{
	*n = (int*)malloc(sizeof(int));
}

int main()
{
	int* pa = NULL;
	func3(&pa);
	printf("%p", pa);// 000002733CFA7BC0
}
  • 图解

在这里插入图片描述

四、单链表尾插

单链表尾插定义

  • 由上述一二级指针的关系,可得
  • 若初始状态链表为空(NULL)则尾插需要改变传入的第一个节点的地址。所以需要传入二级指针。
  • 若初始状态链表不为空,则找尾,并进行尾插。
// 创建新节点并初始化的函数
SLTNode* BuyNewNode(SLTDataType x)
{
	// 创建新的节点
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("BuyNewNode()::malloc()");
		return;
	}
	// 初始新节点的数据
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

// 尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyNewNode(x);
	
	// 判断是否单链表头节点地址为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		// 找尾
		SLTNode* tail = *pphead;

		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}


单链表尾插测试

void SLTest01()
{
	SLTNode* plist = NULL;

	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);
}

效果如下:
在这里插入图片描述

五、单链表头插

单链表头插定义

  • 头插同理也需要传入二级指针。
  • 新节点的next指针指向原来的头节点
  • 头节点又重新指向新的节点。
// 创建新节点并初始化的函数
SLTNode* BuyNewNode(SLTDataType x)
{
	// 创建新的节点
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("BuyNewNode()::malloc()");
		return;
	}
	// 初始新节点的数据
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

// 头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	// 创建新的节点
	SLTNode* newnode = BuyNewNode(x);
	
	newnode->next = *pphead;
	*pphead = newnode;
}

单链表头插测试

void SLTest02()
{
	SLTNode* plist = NULL;
	// 头插
	SLTPushFront(&plist, 1);
	SLTPrint(plist);
	SLTPushFront(&plist, 2);
	SLTPrint(plist);
	SLTPushFront(&plist, 3);
	SLTPrint(plist);
	SLTPushFront(&plist, 4);
	SLTPrint(plist);
}

效果如下:
在这里插入图片描述

六、单链表尾删

单链表尾删定义

  • 找到单链表最后一个节点以及最后一个节点的前一个节点
  • free释放最后一个节点。
  • 并将最后一个节点的前一个节点置为空(NULL)。
// 尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead != NULL);

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		SLTNode* prev = NULL;
		// 找尾
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}

		free(tail);
		tail = NULL;

		prev->next = NULL;
	}
}

单链表尾删测试

void SLTest03()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 尾删
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	
}

效果如下:
在这里插入图片描述

五、单链表头删

单链表头删定义

  • 先将链表第二个节点的地址存储
  • 然后释放第一个节点。
  • 最后将链表第一个节点的指着指向存储的第二个节点地址。
// 头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead != NULL);

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tmp = (*pphead)->next;
		free(*pphead);
		*pphead = tmp;
	}
}

单链表头删测试

void SLTest04()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 头删
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	/*SLTPopFront(&plist);
	SLTPrint(plist);*/

}

效果如下:
在这里插入图片描述

六、单链表查找指定数值位置

查找指定数值位置函数定义

  • 遍历链表,判断每一个节点的data数据是否等于指定数值x
  • 若存在相等,则返回当前节点的地址。
  • 若没有相等,则返回空指针NULL。
// 查找指定数值位置
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);

	SLTNode* cur = phead;

	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;

}

查找指定数值位置函数测试

void SLTest05()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 查找指定数值2的位置
	SLTNode* ret = SLTFind(plist, 2);
	if (ret != NULL)
	{
		// 将数值为2的节点数据乘2
		ret->data *= 2;
		SLTPrint(plist);
	}
	else
	{
		printf("没找到!!!\n");
	}	
}

效果如下:
在这里插入图片描述

七、指定位置之前插入

指定位置之前插入函数定义

  • 若pos位置为第一个节点的地址,则直接调用头插
  • 若不是
  • 创建新的节点,找到pos节点之前的节点。
  • 将pos节点之前的节点的next指向新节点。
  • 新节点的next指向pos节点。
// 指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);



	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		// 创建新的节点
		SLTNode* newnode = BuyNewNode(x);

		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = newnode;
		newnode->next = pos;
	}
	
}

指定位置之前插入函数测试

void SLTest06()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 查找指定数值2的位置
	SLTNode* ret = SLTFind(plist, 2);
	
	SLTInsert(&plist, ret, 30);
	SLTPrint(plist);
}

效果如下:
在这里插入图片描述

八、指定位置删除

指定位置删除函数定义

  • 找到指定位置之前的节点(prev)。
  • 将prev的next指向指定位置(pos)的next节点。
/ 指定位置删除
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);

	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}

	prev->next = pos->next;
	free(pos);
}

指定位置删除函数测试

void SLTest07()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 查找指定数值2的位置
	SLTNode* ret = SLTFind(plist, 2);

	SLTErase(&plist, ret);

	SLTPrint(plist);
}

效果如下:
在这里插入图片描述

九、指定位置之后插入

指定位置之后插入函数定义

  • 若指定位置pos为最后一个节点,则直接调用尾插
  • 若pos不是最后一个节点
  • 新建一个节点
  • 将新节点的next指向pos的next节点。
  • 将pos的next指向新节点。
// 指定位置之后插入
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);


	if (pos == *pphead)
	{
		SLTPushBack(pphead, x);
	}
	else
	{
		// 创建新的节点
		SLTNode* newnode = BuyNewNode(x);


		newnode->next = pos->next;
		pos->next = newnode;
	}

}

指定位置之后插入函数测试

void SLTest08()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 查找指定数值2的位置
	SLTNode* ret = SLTFind(plist, 2);

	SLTInsertAfter(&plist, ret, 60);

	SLTPrint(plist);
}

效果如下:
在这里插入图片描述

十、指定位置之后删除

指定位置之后删除函数定义

  • 创建临时变量del存储pos的下一个节点。
  • 将pos的next指向del的next节点。
  • free释放del节点。
  • 将del置为NULL。
// 指定位置之后删除
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos && pos->next);


	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

指定位置之后删除函数测试

void SLTest09()
{
	SLTNode* plist = NULL;

	// 尾插
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	// 查找指定数值2的位置
	SLTNode* ret = SLTFind(plist, 2);

	SLTEraseAfter(&plist, ret);

	SLTPrint(plist);
}

效果如下:
在这里插入图片描述


总结

单链表创建、单链表打印、一级和二级指针做参数、尾插、头插、尾删、头删、查找指定数值位置、指定位置之前插入、指定位置删除、指定位置之后插入、指定位置之后删除等的介绍

  • 58
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A: 创建单链表的步骤如下: 1. 新建一个结点,其指针域为空。 2. 依次读入数据,新建一个结点,并将其插入到链表的部。 代码如下: ``` #include <stdio.h> #include <stdlib.h> typedef struct ListNode { int val; struct ListNode *next; } ListNode; ListNode* createList() { ListNode *head = (ListNode*)malloc(sizeof(ListNode)); head->next = NULL; // 初始化结点 int x; while (scanf("%d", &x) != EOF) { ListNode *newNode = (ListNode*)malloc(sizeof(ListNode)); newNode->val = x; newNode->next = head->next; head->next = newNode; } return head; } int main() { ListNode *head = createList(); head = head->next; // 链表结点包含无用信息,需要去掉 while (head != NULL) { printf("%d ", head->val); head = head->next; } return 0; } ``` 创建单链表的步骤如下: 1. 新建一个结点,其指针域为空。 2. 定义一个指针p指向结点。 3. 依次读入数据,每读入一个数据新建一个结点,并将其插入到链表的部。插入完成后,将p指向新建的结点。 代码如下: ``` ListNode* createList() { ListNode *head = (ListNode*)malloc(sizeof(ListNode)); head->next = NULL; // 初始化结点 ListNode *p = head; // 初始指向结点 int x; while (scanf("%d", &x) != EOF) { ListNode *newNode = (ListNode*)malloc(sizeof(ListNode)); newNode->val = x; newNode->next = NULL; // 将新结点的指针域设为空 // 将新结点插入到链表部 p->next = newNode; p = p->next; // p指向新结点 } return head; } ``` 其中,scanf("%d", &x) != EOF 表示当未到达文件结束符时循环读入整数。在Windows命令行下, 文件结束符可以通过按 Ctrl+Z 或 F6 来输入。在Linux系统下,文件结束符可以通过按 Ctrl+D 来输入。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值