初阶数据结构:链表(二)

目录

 一、前言

二、带头双向循环链表

1.带头双向循环链表的结构

(1)什么是带头?

  (2)什么是双向呢?

(3)那什么是循环呢?

2.带头双向循环链表的实现

(1)节点结构

(2)创建链表的头节点,也即哨兵节点

(3)创建其他节点和链表打印

(4)链表尾插和尾删功能的实现

(5)链表的头插和头删

(6)链表的查找

(7)双链表在pos位置前后插入和删除pos位置功能


一、前言

          在上一篇博客中,我们实现的是单链表。我们知道链表有8种结构,由单向和双向、有头和无头、循环和非循环组合而成。单链表就是无头单向非循环链表。它是一种结构简单的链表,通常不会用来单独作为存储数据用,实际中更多的是作为其它数据结构的子结构存在,如哈希桶、图的邻接等等。单链表虽然在头插、头删方面很方便,但在尾插和尾删又比不过顺序表。那么有没有一种链表,在头插头删和尾插尾删上都很方便呢?

         当然有,那就是链表中的“王者”------带头双向循环链表。它的结构非常复杂,但效率极高。让我们来看看带头双向循环链表的结构吧!

二、带头双向循环链表

1.带头双向循环链表的结构

(1)什么是带头?

指链表中没有存储数据的节点时,头指针仍然指向一个节点,这个节点不存储数据,只起到站位的作用,其后才是链表的实际数据节点,因此它也被成为哨兵节点。

它的作用是什么呢?在单链表中,我们在改变头指针的链接对象时,需要使用二级指针。有了哨兵节点,我们就不需要二级指针。在执行插入、删除等操作时,也不需要对链表是否为空或是否为最后一个节点进行特殊判断,从而使代码更加简洁和统一,也让我们不至于被绕晕。

  (2)什么是双向呢?

看图就可以明白。

节点结构体指针域中有两个指针。一个指向上一个节点,一个指向下一个节点。如此就可以由一个链表中的一个节点位置得到所有节点的位置。

(3)那什么是循环呢?

循环链表则是将尾节点的后继指针指向头结点,而头结点的前驱指针指向尾节点,从而形成一个闭环。这样的设计使得从链表的任何一个节点开始都可以很方便地遍历整个链表,无论是向前还是向后。也即:

循环链表的哨兵节点的头指针指向尾节点,尾节点的next指针指向哨兵节点。

不要看带头双向循环链表的结构很复杂,就认为它的实现也很难,正因为结构如此,它的实现也避开了需多难题。相比于单链表的实现,它反而简单。

2.带头双向循环链表的实现

(1)节点结构

为了实现双向,那么作为节点的结构体就需要有两个指针和存储数据的位置。代码如下:

typedef int type;
typedef struct ListNde
{
	struct ListNde* next;//指向下一个节点
	struct ListNde* head;//指向上一个节点
	type data;//存储数据
}ListNode;

(2)创建链表的头节点,也即哨兵节点

先看代码:

ListNode* ListCreate()
{
	//动态申请一个结构体空间
	ListNode* head = (ListNode*)malloc(sizeof(ListNode));
	if (head == NULL)
	{
		perror("ListCreate::malloc");
		return NULL;
	}
	//使节点不存储数据
	head->data = NULL;
	//因为作为头节点存在,因此在没有其他节点时,需要让
	//前指针和后指针都指向自己
	head->head = head;
	head->next = head;
	//如果不返回,那就需要传二级指针来使头指针和哨兵节点链接
	return head;
}

为什么哨兵节点的前指针和后指针都要指向自己。看图:

(3)创建其他节点和链表打印

创建其他节点和之前单链表创建节点一样,代码如下:

ListNode* BuyListNode()
{
	ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));
	if (ptr == NULL)
	{
		perror("BuyListNode::malloc");
		return NULL;
	}
	ptr->head = NULL;
	ptr->next = NULL;
	return ptr;
}

链表打印和单链表打印大至一样,循环打印即可,但与单链表打印不同的是,它需要有一个循环终止条件,单纯的不为空可不行,带头双向循环链表可没有为空。而且因为哨兵节点没有存储数据,因此要避免打印哨兵节点。代码如下:

void ListPrint(ListNode* plist)
{
	//不打印哨兵节点,哨兵节点不存储任何数据
	ListNode* ptr = plist->next;
	//以这个代表哨兵节点
	printf("<head>");
	//循环打印
	while (ptr)
	{
		printf("<%d>", ptr->data);
		//因为带头双向循环链表尾节点和哨兵节点是相链接的
		//所以需要一个条件来作为循环的终止条件
		if (ptr->next == plist)
		{
			//ptr指向尾节点时,ptr->next指向哨兵节点,退出循环
			break;
		}
		ptr = ptr->next;
	}
}

(4)链表尾插和尾删功能的实现

先看图:

我们想要使新节点和链表链接最好带顺序的去进行指针的互换,不然容易漏掉,或者换错。我们先让尾节点的后指针指向新节点,新节点的前指针指向尾节点,新节点的后指针指向哨兵节点,哨兵节点的前指针指向新节点,这样按顺序来,既不容易漏掉,也具有逻辑美,更容易理解。代码如下

void ListPushBack(ListNode* plist)
{
	//断言判断plist不为空,因为有哨兵节点存在,那么plist
	//传过来的必定不为空
	assert(plist);
	ListNode* ptr = plist;
	//由ptr->head得到尾节点
	ListNode* tail = ptr->head;
	//申请一个新的节点
	ListNode* NewNode = BuyListNode();
	printf("请输入要尾插的数字\n");
	scanf("%d", &NewNode->data);
	//尾插:尾节点的next指向新节点
	tail->next = NewNode;
	//新节点的前指针指向尾节点
	NewNode->head = tail;
	//新节点的后指针指向哨兵节点
	NewNode->next = ptr;
	//哨兵节点的前指针指向新节点
	ptr->head = NewNode;
}

链表的尾删也很简单,只需按上述步骤逆着来然后释放要删除的空间就行了。代码如下:

void ListPopBack(ListNode* plist)
{
	assert(plist);
	ListNode* ptr = plist;
	ListNode* tail = ptr->head;
	ListNode* tail1 = tail->head;
	tail1->next = ptr;
	ptr->head = tail1;
	free(tail);
	printf("删除成功\n");
}

(5)链表的头插和头删

链表的头插和头删和链表的尾插尾删差不多,只不过这里的头插和头删是在哨兵节点的下一个节点,不是让你删或者插在哨兵节点后面。并且,在所有带头链表的删除功能里一定不能删除哨兵节点。那会出现野指针的,程序运行也会不安全。代码如下:

// 双向链表头插
void ListPushFront(ListNode* plist)
{
	assert(plist);
	ListNode* ptr = plist;
	ListNode* newnode = BuyListNode();
	ListNode* ptrnext = ptr->next;
	ptr->next = newnode;
	newnode->head = ptr;
	newnode->next = ptrnext;
	ptrnext->head = newnode;
	printf("请输入头插数字\n");
	scanf("%d", &newnode->data);
	printf("插入成功\n");
}
// 双向链表头删
void ListPopFront(ListNode* plist)
{
	assert(plist);
	ListNode* ptr = plist;
	ListNode* ptrnext2= ptr->next->next;
	free(ptr->next);
	ptr->next = ptrnext2;
	ptrnext2->head = ptr;
	printf("删除成功\n");
}

(6)链表的查找

ListNode* ListFind(ListNode* plist)
{
	assert(plist);
	int a;
	printf("请输入要查找的数字\n");
	scanf("%d", &a);
	ListNode* ptr = plist->next;
	while (ptr)
	{
		//查找到直接返回
		if (ptr->data == a)
		{
			printf("查找成功\n");
			return ptr;
		}
		//一定要有这个条件,防止查找不到陷入死循环
		if (ptr->next == plist)
		{
			printf("查找失败,未找到\n");
			return NULL;
		}
		ptr = ptr->next;
	}
}

(7)双链表在pos位置前后插入和删除pos位置功能

这些和头插头删没有多大区别,且双链表在pos位置前插入比单链表简单。代码如下:

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos)
{
	assert(pos);
	ListNode* front = pos->head;
	ListNode* ptr = pos;
	ListNode* newnode = BuyListNode();
	front->next = newnode;
	newnode->head = front;
	newnode->next = ptr;
	ptr->head = newnode;
	printf("请输入要在pos前插入的数\n");
	scanf("%d", &newnode->data);
	printf("插入成功\n");
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* front = pos->head;
	ListNode* posnext = pos->next;
	free(pos);
	front->next = posnext;
	posnext->head = front;
	printf("删除成功\n");
}
// 双向链表在pos位置之后插入
void ListInsertAfter(ListNode* pos)
{
	assert(pos);
	ListNode* posnext = pos->next;
	ListNode* newnode = BuyListNode();
	ListNode* ptr = pos;
	ptr->next = newnode;
	newnode->head = ptr;
	newnode->next = posnext;
	posnext->head = newnode;
	printf("请输入要插入的数字\n");
	scanf("%d", &newnode->data);
}

这样一个带头双向循环链表也就完成了。单链表和带头双向循环链表虽然是两个极端,但当我们可以自主实现后,其他的链表结构我们也可以信手拈来。

全部代码如下:

listnode.h:

#pragma once
#pragma warning(disable : 4996)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int type;
typedef struct ListNde
{
	struct ListNde* next;//指向下一个节点
	struct ListNde* head;//指向上一个节点
	type data;//存储数据
}ListNode;
// 创建链表的头节点,也即哨兵节点.
ListNode* ListCreate();
//创建其他节点
ListNode* BuyListNode();
// 双向链表销毁
void ListDestory(ListNode* plist);
// 双向链表打印
void ListPrint(ListNode* plist);
// 双向链表尾插
void ListPushBack(ListNode* plist);
// 双向链表尾删
void ListPopBack(ListNode* plist);
// 双向链表头插
void ListPushFront(ListNode* plist);
// 双向链表头删
void ListPopFront(ListNode* plist);
// 双向链表查找
ListNode* ListFind(ListNode* plist);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
// 双向链表在pos位置之后插入
void ListInsertAfter(ListNode* pos);

Flistnode.c:

#include"listnode.h"
ListNode* ListCreate()
{
	//动态申请一个结构体空间
	ListNode* head = (ListNode*)malloc(sizeof(ListNode));
	if (head == NULL)
	{
		perror("ListCreate::malloc");
		return NULL;
	}
	//使节点不存储数据
	head->data = NULL;
	//因为作为头节点存在,因此在没有其他节点时,需要让
	//前指针和后指针都指向自己
	head->head = head;
	head->next = head;
	//如果不返回,那就需要传二级指针来使头指针和哨兵节点链接
	return head;
}

ListNode* BuyListNode()
{
	ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));
	if (ptr == NULL)
	{
		perror("BuyListNode::malloc");
		return NULL;
	}
	ptr->head = NULL;
	ptr->next = NULL;
	return ptr;
}
void ListDestory(ListNode* plist)
{
	//assert(plist);
	ListNode* ptr = plist->next;
	while (ptr)
	{
		ListNode* ptrnext = ptr->next;
		free(ptr);
		if (ptrnext == plist)
		{
			break;
		}
		ptr = ptrnext;
	}
	free(plist);
	printf("销毁成功\n");
}
void ListPrint(ListNode* plist)
{
	//不打印哨兵节点,哨兵节点不存储任何数据
	ListNode* ptr = plist->next;
	//以这个代表哨兵节点
	printf("<head>");
	//循环打印
	while (ptr)
	{
		printf("<%d>", ptr->data);
		//因为带头双向循环链表尾节点和哨兵节点是相链接的
		//所以需要一个条件来作为循环的终止条件
		if (ptr->next == plist)
		{
			//ptr指向尾节点时,ptr->next指向哨兵节点,退出循环
			break;
		}
		ptr = ptr->next;
	}
}

void ListPushBack(ListNode* plist)
{
	//断言判断plist不为空,因为有哨兵节点存在,那么plist
	//传过来的必定不为空
	assert(plist);
	ListNode* ptr = plist;
	//由ptr->head得到尾节点
	ListNode* tail = ptr->head;
	//申请一个新的节点
	ListNode* NewNode = BuyListNode();
	printf("请输入要尾插的数字\n");
	scanf("%d", &NewNode->data);
	//尾插:尾节点的next指向新节点
	tail->next = NewNode;
	//新节点的前指针指向尾节点
	NewNode->head = tail;
	//新节点的后指针指向哨兵节点
	NewNode->next = ptr;
	//哨兵节点的前指针指向新节点
	ptr->head = NewNode;
}

void ListPopBack(ListNode* plist)
{
	assert(plist);
	ListNode* ptr = plist;
	ListNode* tail = ptr->head;
	ListNode* tail1 = tail->head;
	tail1->next = ptr;
	ptr->head = tail1;
	free(tail);
	printf("删除成功\n");
}
// 双向链表头插
void ListPushFront(ListNode* plist)
{
	assert(plist);
	ListNode* ptr = plist;
	ListNode* newnode = BuyListNode();
	ListNode* ptrnext = ptr->next;
	ptr->next = newnode;
	newnode->head = ptr;
	newnode->next = ptrnext;
	ptrnext->head = newnode;
	printf("请输入头插数字\n");
	scanf("%d", &newnode->data);
	printf("插入成功\n");
}
// 双向链表头删
void ListPopFront(ListNode* plist)
{
	assert(plist);
	ListNode* ptr = plist;
	ListNode* ptrnext2= ptr->next->next;
	free(ptr->next);
	ptr->next = ptrnext2;
	ptrnext2->head = ptr;
	printf("删除成功\n");
}

ListNode* ListFind(ListNode* plist)
{
	assert(plist);
	int a;
	printf("请输入要查找的数字\n");
	scanf("%d", &a);
	ListNode* ptr = plist->next;
	while (ptr)
	{
		//查找到直接返回
		if (ptr->data == a)
		{
			printf("查找成功\n");
			return ptr;
		}
		//一定要有这个条件,防止查找不到陷入死循环
		if (ptr->next == plist)
		{
			printf("查找失败,未找到\n");
			return NULL;
		}
		ptr = ptr->next;
	}
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos)
{
	assert(pos);
	ListNode* front = pos->head;
	ListNode* ptr = pos;
	ListNode* newnode = BuyListNode();
	front->next = newnode;
	newnode->head = front;
	newnode->next = ptr;
	ptr->head = newnode;
	printf("请输入要在pos前插入的数\n");
	scanf("%d", &newnode->data);
	printf("插入成功\n");
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* front = pos->head;
	ListNode* posnext = pos->next;
	free(pos);
	front->next = posnext;
	posnext->head = front;
	printf("删除成功\n");
}
// 双向链表在pos位置之后插入
void ListInsertAfter(ListNode* pos)
{
	assert(pos);
	ListNode* posnext = pos->next;
	ListNode* newnode = BuyListNode();
	ListNode* ptr = pos;
	ptr->next = newnode;
	newnode->head = ptr;
	newnode->next = posnext;
	posnext->head = newnode;
	printf("请输入要插入的数字\n");
	scanf("%d", &newnode->data);
}


listnode.c:

#include"listnode.h"
ListNode* pplist = NULL;
int main()
{
	int a;
	pplist = ListCreate();
	ListNode* pos = NULL;
	do
	{
		printf("请输入数字");
		scanf("%d", &a);
		switch (a)
		{
		case 1:
			// 双向链表打印
			ListPrint(pplist);
			break;
		case 2:
			// 双向链表尾插
			ListPushBack(pplist);
			break;
		case 3:
			// 双向链表的头插
			ListPushFront(pplist);
			break;
		case 4:
			// 双向链表的尾删
			ListPopBack(pplist);
			break;
		case 5:
			// 双向链表头删
			ListPopFront(pplist);
			break;
		case 6:
			// 双向链表查找
			pos = ListFind(pplist);
			break;
		case 7:
			// 双向链表在pos位置之后插入x
			ListInsertAfter(pos);
			break;
		case 8:
			// 在pos的前面插入
			ListInsert(pos);
			break;
		case 9:
			// 删除pos位置
			ListErase(pos);
			break;
		case 0:
			//链表的销毁
			ListDestory(pplist);
			printf("退出\n");
			break;
		default:
			printf("输入数字错误,请重新输入\n");
			break;
		}
	} while (a);
     return 0;
}

至此,链表就完了,大家可以去力扣或者牛客上找一些链表的题做一做来巩固一下。提个建议,如果在链表中,或者说在整个数据结构中,画图永远是你最好的伙伴。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java Web-Request与Response

在 Java Web 开发中&#xff0c;Request 和 Response 是两个非常重要的对象&#xff0c;用于在客户端和服务器之间进行请求和响应的处理&#xff0c;以下是详细介绍&#xff1a; Request&#xff08;请求对象&#xff09; Request继承体系 在 Java Web 开发中&#xff0c;通…

mysql 学习2 MYSQL数据模型,mysql内部可以创建多个数据库,一个数据库中有多个表;表是真正放数据的地方,关系型数据库 。

在第一章中安装 &#xff0c;启动mysql80 服务后&#xff0c;连接上了mysql&#xff0c;那么就要 使用 SQL语句来 操作mysql数据库了。那么在学习 SQL语言操作 mysql 数据库 之前&#xff0c;要对于 mysql数据模型有一个了解。 MYSQL数据模型 在下图中 客户端 将 SQL语言&…

微信小程序date picker的一些说明

微信小程序的picker是一个功能强大的组件&#xff0c;它可以是一个普通选择器&#xff0c;也可以是多项选择器&#xff0c;也可以是时间、日期、省市区选择器。 官方文档在这里 这里讲一下date picker的用法。 <view class"section"><view class"se…

【学习笔记】计算机网络(二)

第2章 物理层 文章目录 第2章 物理层2.1物理层的基本概念2.2 数据通信的基础知识2.2.1 数据通信系统的模型2.2.2 有关信道的几个基本概念2.2.3 信道的极限容量 2.3物理层下面的传输媒体2.3.1 导引型传输媒体2.3.2 非导引型传输媒体 2.4 信道复用技术2.4.1 频分复用、时分复用和…

总结8..

#include <stdio.h> // 定义结构体表示二叉树节点&#xff0c;包含左右子节点编号 struct node { int l; int r; } tree[100000]; // 全局变量记录二叉树最大深度&#xff0c;初始为0 int ans 0; // 深度优先搜索函数 // pos: 当前节点在数组中的位置&#xff0c…

多智能体中的理论与传统智能体理论有何异同?

多智能体系统与传统单智能体理论在多个方面存在异同&#xff0c;多智能体系统在理论上扩展了单智能体系统的研究范畴&#xff0c;强调智能体之间的交互和协作。随着人工智能、人机智能、人机环境系统智能的发展&#xff0c;多智能体系统在机器人群体、分布式计算、资源管理等领…

RKNN_C++版本-YOLOV5

1.背景 为了实现低延时&#xff0c;所以开始看看C版本的rknn的使用&#xff0c;确实有不足的地方&#xff0c;请指正&#xff08;代码借鉴了rk官方的仓库文件&#xff09;。 2.基本的操作流程 1.读取模型初始化 // 设置基本信息 // 在postprocess.h文件中定义&#xff0c;详见…

FlinkSql使用中rank/dense_rank函数报错空指针

问题描述 在flink1.16(甚至以前的版本)中&#xff0c;使用rank()或者dense_rank()进行排序时&#xff0c;某些场景会导致报错空指针NPE(NullPointerError) 报错内容如下 该报错没有行号/错误位置&#xff0c;无法排查 现状 目前已经确认为bug&#xff0c;根据github上的PR日…

csapp2.4节——浮点数

目录 二进制小数 十进制小数转二进制小数 IEEE浮点表示 规格化表示 非规格化表示 特殊值 舍入 浮点运算 二进制小数 类比十进制中的小数&#xff0c;可定义出二进制小数 例如1010.0101 小数点后的权重从-1开始递减。 十进制小数转二进制小数 整数部分使用辗转相除…

【2024年华为OD机试】 (A卷,200分)- 开放日活动、取出尽量少的球(JavaScriptJava PythonC/C++)

一、问题描述 题目描述 某部门开展Family Day开放日活动,其中有个从桶里取球的游戏,游戏规则如下: 有N个容量一样的小桶等距排开。每个小桶默认装了数量不等的小球,记录在数组 bucketBallNums 中。游戏开始时,要求所有桶的小球总数不能超过 SUM。如果小球总数超过 SUM,…

《安富莱嵌入式周报》第349期:VSCode正式支持Matlab调试,DIY录音室级麦克风,开源流体吊坠,物联网在军工领域的应用,Unicode字符压缩解压

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; 《安富莱嵌入式周报》第349期&#xff1a;VSCode正式支持Matlab调试&#xff0c;DIY录音室级麦克风…

nacos(基于docker最详细安装)

1、什么是Spring Cloud Spring Cloud是一系列框架的集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用Spring Boot的开发风格做到一键启动和部署。…

李沐vscode配置+github管理+FFmpeg视频搬运+百度API添加翻译字幕

终端输入nvidia-smi查看cuda版本 我的是12.5&#xff0c;在网上没有找到12.5的torch&#xff0c;就安装12.1的。torch&#xff0c;torchvision&#xff0c;torchaudio版本以及python版本要对应 参考&#xff1a;https://blog.csdn.net/FengHanI/article/details/135116114 创…

Java Web-Tomcat Servlet

Web服务器-Tomcat Web服务器简介 Web 服务器是一种软件程序&#xff0c;它主要用于在网络上接收和处理客户端&#xff08;如浏览器&#xff09;发送的 HTTP 请求&#xff0c;并返回相应的网页内容或数据。以下是关于 Web 服务器的详细介绍&#xff1a; 功能 接收请求&#…

An OpenGL Toolbox

3.An OpenGL Toolbox 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 3.1 Vertex Arrays and Their Drawing Commands 顶点数组及其绘制命令&#xff1a;将几何数据存储在一个位置&#xff0c…

GCC之编译(8)AR打包命令

GCC之(8)AR二进制打包命令 Author: Once Day Date: 2025年1月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-C…

【miniconda】:langraph的windows构建

langraph需要python3.11 langraph强烈建议使用py3.11 默认是3.12 官方 下载仓库 下载老版本的python (后续发现新版miniconda也能安装老版本的python) 在这里

微信小程序中常见的 跳转方式 及其特点的表格总结(wx.navigateTo 适合需要返回上一页的场景)

文章目录 详细说明总结wx.navigateTo 的特点为什么 wx.navigateTo 最常用&#xff1f;其他跳转方式的使用频率总结 以下是微信小程序中常见的跳转方式及其特点的表格总结&#xff1a; 跳转方式API 方法特点适用场景wx.navigateTowx.navigateTo({ url: 路径 })保留当前页面&…

python3+TensorFlow 2.x(四)反向传播

目录 反向传播算法 反向传播算法基本步骤&#xff1a; 反向中的参数变化 总结 反向传播算法 反向传播算法&#xff08;Backpropagation&#xff09;是训练人工神经网络时使用的一个重要算法&#xff0c;它是通过计算梯度并优化神经网络的权重来最小化误差。反向传播算法的核…

深度学习 Pytorch 单层神经网络

神经网络是模仿人类大脑结构所构建的算法&#xff0c;在人脑里&#xff0c;我们有轴突连接神经元&#xff0c;在算法中&#xff0c;我们用圆表示神经元&#xff0c;用线表示神经元之间的连接&#xff0c;数据从神经网络的左侧输入&#xff0c;让神经元处理之后&#xff0c;从右…