【数据结构】——单链表

news2025/7/21 10:13:49

目录

1.链表

1.1 链表的概念及结构

1.2 链表的分类

1. 单向或者双向

 2. 带头或者不带头

 3. 循环或者非循环        

  1.3实现一个单链表(无头单项非循环链表增删查改的实现)      

1.链表结构的创建

2.创建一个节点

3.创建一个链表

4.打印链表

5.链表尾插

6.链表尾删

7.链表头插

8.链表头删

9.找链表节点

10.在pos位置后面插入一个节点x

11.在pos位置前面插入一个节点x

12.在pos位置删除后面的一个节点x

13.删除pos位置处的节点

2.完整单链表实现代码

2.1头文件代码(SList.h)

2.2函数定义代码(SList.c)

2.3测试代码(test.c)


问题1.为什么需要链表结构?

那先看看已有的顺序表的缺陷吧,顺序表空间不够的话,需要扩容

扩容是需要代价的,(尤其是异地扩容),消耗的连续空间更多,而且可能会造成空间浪费

扩容一般是扩充一倍,此时空间的大小就可能造成浪费,而且对于数组的增删查改需要挪动整个数组,效率低下。

由此就引出了链表结构

1.链表

1.1 链表的概念及结构

概念:链表是一种 物理存储结构上非连续、非顺序的存储结构 数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
我的理解1:
顺序表的本质是一个结构,里面存的是整体数据本身
链表的本质也是一个结构,里面存的是一个链表节点的数据和下一个链接节点的地址,这样在空间上不连续,但是在逻辑上,上一个节点可以通过下一个节点的地址找到下一个节点,拿到下一个节点存的数据,
(当然通过合理的存放地址,也可以从下一个节点访问到上一个节点,这里我们不做深究,先弄懂最基本的链式的原理)

 PS:

1.由上图可知,链式结构在逻辑上是连续的,但是在物理上不一定连续

2.现实中的节点一般都是在堆(realloc malloc)上申请出来的

3.从堆上申请的空间是按照一定的策略分配的,两次申请的空间,可能连续,也可能不连续。

1.2 链表的分类

实际中链表的结构非常多样,只要具有链式逻辑的结构都可以纳入链表范畴

此文就以以下情况组合起来的8种链表结构探究:

1. 单向或者双向

 2. 带头或者不带头

 3. 循环或者非循环        

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

  1.3实现一个单链表(无头单项非循环链表增删查改的实现)      

1.链表结构的创建

//链表结构创建
typedef int SLTDataType;//链表所存数据的类型
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;//下个同类型结构体的地址,以便于找到下一个节点
}SLTNode;

PS:

链表结构创建的误区如下:

2.创建一个节点

//申请一个节点
SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

测试代码如下:

void TestSList0()
{
	//创建一个个没有联系的节点
	SLTNode* n1 = BuySLTNode(1);
	SLTNode* n2 = BuySLTNode(2);
	SLTNode* n3 = BuySLTNode(3);
	SLTNode* n4 = BuySLTNode(4);
	//前一个节点里面的next指针存的是下一个节点的地址
	//因此可以达到通过第一个节点就(像链条一样)能找到后面所有节点的效果
	//此种数据存储的方式为----单链表
	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;
	//这样链接节点太废代码
	//可以将链接节点封装成一个函数
}

 分析如下:

 但是用这种方法把节点链接起来太低效了。

3.创建一个链表

//创建一个链表,链表里面有n个节点
SLTNode* CreateSList(int n)
{
	SLTNode* phead = NULL;//链表头
	SLTNode* ptail = NULL;//链表尾
	int x = 0;
	for (int i = 0; i < n; ++i)
	{
		//scanf("%d", &x);
		//SLTNode* newnode = BuySLTNode(x);
		SLTNode* newnode = BuySLTNode(i);
		//链接节点
		if (phead == NULL)
		{
			ptail = phead = newnode;
		}
		else
		{
            //next存的下一个节点的地址
            //下一个节点成为新的尾
			ptail->next = newnode;
			ptail = newnode;
		}
	}

	//ptail->next = NULL;

	return phead;
}

4.打印链表

//打印链表
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		//printf("[%d|%p]->", cur->data, cur->next);
		printf("%d->", cur->data);
		cur = cur->next;//节点往链表后走
	}
	printf("NULL\n");
}

测试如下:

void TestSList1()
{
	SLTNode* plist = CreateSList(10);
	SLTPrint(plist);
}

int main()
{
	TestSList1();
	//int a = 0, b = 1;
	//Swap1(&a, &b);

	//int* ptr1 = &a, *ptr2 = &b;
	//Swap2(&ptr1, &ptr2);

	return 0;
}

如图:

5.链表尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	//尾插的时候没有节点
	if (*pphead == NULL)
	{ 
		*pphead = newnode;
	}
	//尾插的时候已经有节点
	else
	{
		SLTNode* tail = *pphead;
		// 找尾
		while (tail->next)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

总结:

6.链表尾删

void SLTPopBack(SLTNode** pphead)
{
	// 暴力的检查
	assert(*pphead);

	// 温柔的检查
	//if (*pphead == NULL)
	//	return;

	//只有一个节点的尾删
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//多个节点的尾插
	else
	{
		//写法1
		/*SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next)
		{
		prev = tail;
		tail = tail->next->next;
		}

		free(tail);
		prev->next = NULL;*/
		//写法2
		//其实tail->next相当于写法1的prev,tail->next->next相当于代码1中的tail->next->next
		//也即是说两次指向的next可以代替掉代码1的prev
		SLTNode* tail = *pphead;
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

分析:

1.多个节点的尾删

 2.一个节点的尾删

 只有一个节点的话,就直接free掉就行了

7.链表头插

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

分析:

只需要创建一个节点指向原来的第一个节点

并且把指向原来头节点的指针指向新的节点就行 

8.链表头删

void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);

	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

PS:

单链表最大的优势是头插和头删,这两个很快,但是尾插和尾删慢(因为要遍历链表)

9.找链表节点

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

分析:找到就返回结点的地址,没找到就继续往下找。

10.在pos位置后面插入一个节点x

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	/*if (pos == NULL)
	{
		return;
	}*/

	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

注意    

newnode->next = pos->next;

pos->next = newnode;

这两句代码的位置,如果顺序想法,会导致死循环。

11.在pos位置前面插入一个节点x

// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);

	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

有两种情况

 第一种情况是头插

第二种情况是非头插

12.在pos位置删除后面的一个节点x

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);

	if (pos->next == NULL)//删最后一个
	{
		return;
	}
	else
	{
		// 错的
		/*free(pos->next);
		pos->next = pos->next->next;*/

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

13.删除pos位置处的节点

// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(*pphead);

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

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

		//pos = NULL;
	}
}

分析:

第一种情况pos节点是头节点

第二种情况pos节点是非头节点

2.完整单链表实现代码

2.1头文件代码(SList.h)

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


//链表结构创建
typedef int SLTDataType;//链表所存数据的类型
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;//下个同类型结构体的地址,以便于找到下一个节点
}SLTNode;

链表结构创建(错误的创建方法)
//typedef int SLTDataType;//链表所存数据的类型
//typedef struct SListNode
//{
//	SLTDataType data;
//	//struct SListNode* next;
//	SLTNode* next;
//}SLTNode;

//创建和初始化一个节点,节点存的数据是x
SLTNode* BuySLTNode(SLTDataType x);
//创建一个链表,链表里面有n个节点
SLTNode* CreateSList(int n);
//打印链表,链表的起始地址是phead
void SLTPrint(SLTNode* phead);
//单链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//单链表尾删
void SLTPopBack(SLTNode** pphead);
//单链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//单链表头删
void SLTPopFront(SLTNode** pphead);

SLTNode* SListFind(SLTNode* plist, SLTDataType x);

// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SLTEraseAfter(SLTNode* pos);

// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//单链表的释放
void SLTDestroy(SLTNode** pphead)

2.2函数定义代码(SList.c)

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

//申请一个节点
SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

//创建一个链表,链表里面有n个节点
SLTNode* CreateSList(int n)
{
	SLTNode* phead = NULL;//链表头
	SLTNode* ptail = NULL;//链表尾
	int x = 0;
	for (int i = 0; i < n; ++i)
	{
		//scanf("%d", &x);
		//SLTNode* newnode = BuySLTNode(x);
		SLTNode* newnode = BuySLTNode(i);
		//链接节点
		if (phead == NULL)
		{
			ptail = phead = newnode;
		}
		else
		{
			ptail->next = newnode;
			ptail = newnode;
		}
	}

	//ptail->next = NULL;

	return phead;
}

//打印链表
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		//printf("[%d|%p]->", cur->data, cur->next);
		printf("%d->", cur->data);
		cur = cur->next;//节点往链表后走
	}
	printf("NULL\n");
}

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	//尾插的时候没有节点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//尾插的时候已经有节点
	else
	{
		SLTNode* tail = *pphead;
		// 找尾
		while (tail->next)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

//错误的尾删
//void SLTPopBack(SLTNode* phead)
//{
//	SLTNode* tail = phead;
//	while (tail->next)
//	{
//		tail = tail->next;
//	}
//	free(tail);
//	tail = NULL;
//}

void SLTPopBack(SLTNode** pphead)
{
	// 暴力的检查
	assert(*pphead);

	// 温柔的检查
	//if (*pphead == NULL)
	//	return;

	//只有一个节点的尾删
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//多个节点的尾插
	else
	{
		//写法1
		/*SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next)
		{
		prev = tail;
		tail = tail->next->next;
		}

		free(tail);
		prev->next = NULL;*/
		//写法2
		//其实tail->next相当于写法1的prev,tail->next->next相当于代码1中的tail->next->next
		//也即是说两次指向的next可以代替掉代码1的prev
		SLTNode* tail = *pphead;
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);

	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	/*if (pos == NULL)
	{
		return;
	}*/

	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);

	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);

	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		// 错的
		/*free(pos->next);
		pos->next = pos->next->next;*/

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

// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(*pphead);

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

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

		//pos = NULL;
	}
}

void SLTDestroy(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;
}

2.3测试代码(test.c)


#include "SList.h"


void TestSList0()
{
	//创建一个个没有联系的节点
	SLTNode* n1 = BuySLTNode(1);
	SLTNode* n2 = BuySLTNode(2);
	SLTNode* n3 = BuySLTNode(3);
	SLTNode* n4 = BuySLTNode(4);
	//前一个节点里面的next指针存的是下一个节点的地址
	//因此可以达到通过第一个节点就(像链条一样)能找到后面所有节点的效果
	//此种数据存储的方式为----单链表
	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;
	//这样链接节点太废代码
	//可以将链接节点封装成一个函数
}

void TestSList1()
{
	SLTNode* plist = CreateSList(10);
	SLTPrint(plist);
}

void TestSList2()
{
	SLTNode* plist = CreateSList(5);
	SLTPushBack(&plist, 100);
	SLTPushBack(&plist, 200);
	SLTPushBack(&plist, 300);
	SLTPrint(plist);
}

void TestSList3()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 100);
	SLTPushBack(&plist, 200);
	SLTPushBack(&plist, 300);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);

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

void TestSList4()
{
	SLTNode* plist = NULL;
	SLTPushFront(&plist, 100);
	SLTPushFront(&plist, 200);
	SLTPushFront(&plist, 300);
	SLTPushFront(&plist, 400);

	SLTPrint(plist);

	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
}

void Swap1(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void Swap2(int** pp1, int** pp2)
{
	int* tmp = *pp1;
	*pp1 = *pp2;
	*pp2 = tmp;
}

void TestSList5()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPushBack(&plist, 5);
	SLTPrint(plist);

	SLTNode* p = SLTFind(plist, 3);
	SLTInsertAfter(p, 30);

	//p = SLTFind(plist, 300);
	//SLTInsertAfter(p, 30);

	p = SLTFind(plist, 2);
	SLTInsert(&plist, p, 20);

	/*if (p)
	{
		SLTInsertAfter(p, 30);
		printf("ҵ\n");
	}
	else
	{
		printf("Ҳ\n");
	}*/

	SLTPrint(plist);
}

void TestSList6()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPushBack(&plist, 5);
	SLTPrint(plist);

	SLTNode* p = SLTFind(plist, 3);
	SLTEraseAfter(p);
	SLTPrint(plist);

	p = SLTFind(plist, 3);
	SLTErase(&plist, p);
	p = NULL;
	SLTPrint(plist);

	SLTDestroy(&plist);
	//野指针
	SLTPrint(plist);
}

int main()
{
	TestSList4();
	//int a = 0, b = 1;
	//Swap1(&a, &b);

	//int* ptr1 = &a, *ptr2 = &b;
	//Swap2(&ptr1, &ptr2);

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/7833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

大家好&#xff0c;又见面了。 在前面的几篇文章中&#xff0c;我们一起聊了下本地缓存的动手实现、本地缓存相关的规范等&#xff0c;也聊了下Google的Guava Cache的相关原理与使用方式。比较心急的小伙伴已经坐不住了&#xff0c;提到本地缓存&#xff0c;怎么能不提一下“地…

软考 - 程序语言设计

程序设计语言基本概述 程序设计语言是为了书写计算机程序而人为设计的符号语言&#xff0c;用于对计算过程进行 描述、组织和推导。 低级语言&#xff1a;机器语言&#xff08;计算机硬件只能识别0和1的指令序列&#xff09;&#xff0c;汇编语言。 高级语言&#xff1a;功能…

从http请求过程分析为何不同业务的http请求都可以使用默认的缺省端口80,8080等

问题: http上传请求时url地址中一般无显示指定端口号&#xff0c;这时会使用默认的80端口&#xff1b;但是可能不止一个业务需要用到http请求&#xff0c;技术上web服务端那边肯定无法根据业务逻辑的数据格式去分别解析区分它们&#xff1b;因为业务是事先无法预知的&#xff…

【Spring Cloud实战】Consul服务注册与发现

个人博客上有很多干货&#xff0c;欢迎访问&#xff1a;https://javaxiaobear.gitee.io/ 1、简介 https://www.consul.io/docs/intro Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation f…

Flink-经典案例WordCount快速上手以及安装部署

2 Flink快速上手 2.1 批处理api 经典案例WordCount public class BatchWordCount {public static void main(String[] args) throws Exception {//1.创建一个执行环境ExecutionEnvironment env ExecutionEnvironment.getExecutionEnvironment();//2.从文件中读取数据//得到…

[附源码]java毕业设计基于Web留学管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Linux下C++开发笔记--编译静态链接库和动态链接库

目录 1--前言 2--生成静态链接库 3--生成动态链接库 1--前言 承接上一篇Linux下C开发笔记&#xff08;g命令的使用笔记&#xff09;&#xff0c;依据教程记录学习笔记。 2--生成静态链接库 ①回顾项目结构&#xff1a; ​ ②汇编&#xff0c;生成swap.o文件 cd srcg sw…

基于simulink的牛鞭效应模型建模与仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果 一、理论基础 牛鞭效应&#xff0c;是经济学中的一个术语&#xff0c;它也被称为需求放大效应。牛鞭效应指的是当信息流从最终客户端传输到原始供应商时&…

9.行为建模(Behavioral modeling)

9.1行为模型概述 Verilog行为模型包含控制模拟和操纵先前描述的数据类型变量的过程语句。这些语句包含在程序中。每个过程都有一个与其关联的活动流。活动开始于initial和always语句。每个initial语句和每个always语句都会启动一个单独的活动流。所有活动流都是并发的&…

【机器学习】线性分类【上】广义线性模型

主要参考了B站UP主“shuhuai008”&#xff0c;包含自己的理解。 有任何的书写错误、排版错误、概念错误等&#xff0c;希望大家包含指正。 由于字数限制&#xff0c;分成两篇博客。 【机器学习】线性分类【上】广义线性模型 【机器学习】线性分类【下】经典线性分类算法 1. 线…

C语言实现线索化二叉树(先序、中序、后序)

》》如何用C语言构建一颗二叉树? 第一种方法: ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));A->data = { A };A->ltag = 0;A->rtag = 0;A->lchild = NULL;A->rchild = NULL;ThreadTree B = (ThreadTree)malloc(sizeof(ThreadNode));B->data =…

【python自动化】使用关键字驱动实现appium自动化

在写app自动化用例时&#xff0c;尝试用了关键字驱动的框架 记录一下自己对关键字驱动的理解&#xff1a; 1 关键字驱动指将用例步骤的操作封装为关键字&#xff0c;比如定位元素、点击元素、获取元素属性值、断言&#xff0c;这些都是操作关键字 2 在excel中按照用例执行过程&…

Java8方法引用和Lambda表达式实例源码+笔记分享

前言 Java8的lambda表达式&#xff0c;通过lambda表达式可以替代我们之前写的匿名内部类来实现接口。lambda表达式本质是一个匿名函数。 1、lambda表达式本质是一个匿名函数。 1 package com.demo.main;2 3 public class LambdaMain {4 5 public static void main(String[…

环辛炔衍生物DBCO-NH2,amine,Acid,NHS,Maleimide无铜点击反应

DBCO对叠氮化物具有非常高的反应选择性&#xff0c;可用于修饰生物分子&#xff0c;包括肽、蛋白质、酶、活细胞、整个生物体等。在生理温度和pH值范围内&#xff0c;DBCO基团不与胺或羟基反应&#xff0c;DBCO也与叠氮化物基团发生反应DBCO也称为ADIBO&#xff08;氮杂二苯并环…

2022.11.15-二分图专练

目录 50 years, 50 colors(HDU-1498) Uncle Toms Inherited Land*(HDU-1507) Matrix(HDU-2119) Arbiter(HDU-3118) [ZJOI2007]矩阵游戏(黑暗爆炸1059) Jimmy’s Assignment(HDU-1845) 50 years, 50 colors(HDU-1498) 原题链接:传送门 题意:一个n*n的矩阵中&#xff0c;…

第四章. Pandas进阶—数据格式化

第四章. Pandas进阶 4.1 数据格式化 1.设置小数位数(round函数) DataFrame.round(decimals0&#xff0c;*args&#xff0c;**kwargs)参数说明: decimals:用于设置保留的小数位数 args&#xff0c;kwargs:附加关键字的参数 返回值&#xff1a;返回DataFrame对象 1).示例&#…

HTML常用标签的使用

HTML常用标签的使用 文章目录HTML常用标签的使用1.排版标签1.1 标题标签&#xff08;h&#xff09;1.2 段落标签&#xff08;p&#xff09;1.3 换行标签&#xff08;br&#xff09;1.4 水平线标签&#xff08;hr&#xff09;2.文本格式化标签&#xff08;strong、ins、em、del&…

Vue(七)——Vue中的Ajax

目录 Vue脚手架配置代理 插槽 默认插槽 具名插槽 作用域插槽 Vue脚手架配置代理 本案例需要下载axios库&#xff1a;npm install axios 1.配置类方式(实现WebMvcConfigurer) 2.使用CrossOrigin注解 3.使用nginx反向代理解决跨域 4.Vue中配置代理服务器 代理服务器怎…

懒人的法宝——办公自动化!

没错&#xff01;办公自动化他来了&#xff01;果然&#xff0c;代码都是懒人发明出来的。接下来让我们一起来看看这个批改作业的自动化脚本吧&#xff01;学会了这种思想可以帮助我们高效解决许多重复性的工作&#xff0c;比如说批量修改文件的名称、类型、位置等等&#xff0…

【附源码】计算机毕业设计JAVA计算机系教师教研科研管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…