从0开始创建单链表

前言

       这次我来为大家讲解链表,首先我们来理解一下什么是单链表,我们可以将单链表想象成火车

每一节车厢装着货物和连接下一个车厢的链子,单链表也是如此,它是将一个又一个的数据封装到节点上,节点里不仅包含着数据,还又指向下一个节点的指针。

因此单链表的结构体表示如下:

typedef int SLTDataType;

typedef struct SListNode
{
	int data;
	struct SListNode* next;
}SListNode;

所以单链表的特点是:

在物理结构上不一定是线性的
在逻辑结构上是线性的

我们通常会将单链表抽象成如下图所示: 这样会方便我们接下来的思考和代码的实现。

单链表代码实现

打印

我们先来写打印代码,这个代码也方便我们后期的测试。

打印单链表,我们需要遍历单链表的所有数据,这个函数的形参传一级指针就可以了,因为打印并不需要改变头指针。

void SListPrint(SListNode* phead)
{
	SListNode* ptail = phead;
	while (ptail)
	{
		printf("%d->", ptail->data);
		ptail = ptail->next;
	}
	printf("NULL\n");
}

这里定义一个变量ptail,是为了不想改变phead,说不定哪天要在这个函数再次使用phead,所以我定义了一个ptail来进行遍历,以便后面想使用phead的时候,可以快速增加代码

创建新节点

鉴于插入数据都需要创建一个新节点,为了方便后面的头插,尾插等各种插入,于是我们来封装一个函数来创建新节点:

SListNode* CreatNewnode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

尾插

尾插需要在单链表的尾部插入一个新的数据,画图理解一下:

需要我们找到单链表原先的尾节点,将尾节点的next指向newnode,于是我们很快就会写出如下的代码:

void SListPushBack(SListNode** pphead, SLTDataType x)//尾插
{
	assert(pphead);

	SListNode* newnode = CreatNewnode(x);

	//找尾节点
	SListNode* pcur = *pphead;
	while (pcur->next)
	{
		pcur = pcur->next;
	}
	pcur->next = newnode;
}

由于上面的代码是基于链表不为空这一种情况实现的,这时候我们还要思考,如果链表为空的时候,上面的代码还能实现吗?

我们来走一下代码,如果链表为空,pcur==NULL,pcur->next一定会报错,对空指针是不能解引用的,所以上面的代码无法处理链表为空的情况,我们就需要单独进行处理!!!

这个尾插代码应该是这样的:

void SListPushBack(SListNode** pphead, SLTDataType x)//尾插
{
	assert(pphead);

	SListNode* newnode = CreatNewnode(x);

	//如果链表为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾节点
		SListNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
	}
}

所以当链表为空的时候,我们需要改变头指针的指向,所以传二级指针!!!

头插

头插需要我们在单链表的最前面插入一个数据,我们画图来理解一下:

头插很显然要改变头指针,所以形参要影响实参需要传二级指针。

这时候是先newnode指向原先的第一个节点,再改变phead?还是先改变phead再将newnode 指向原先的第一个节点?

在不创建另外一个变量的时候,我们要找到原先第一个节点只能通过phead->next,如果先改变了phead 的话,我们就找不到原先的第一个节点了。
所以需要将新节点的next指向原先的第一个节点,然后我们将头指针改变,指向新节点。

void SListPushFront(SListNode** pphead, SLTDataType x)//头插
{
	assert(pphead);

	SListNode* newnode = CreatNewnode(x);

	newnode->next = *pphead;
	*pphead = newnode;
}

这个时候,由于上面的代码还是基于链表不为空的条件下进行的,所以我们还要考虑链表为空的情况,走一下代码,*pphead == NULL,newnode->next = NULL,*phead = newnode ,很显然这个代码也能处理好链表为空的情况,于是我们不需要任何的改动。

尾删

尾删指删除单链表最后一个节点。画图理解一下:

这时候我们需要找到尾节点和尾节点的前一个节点,将尾节点释放掉,改变现在的尾节点的next指向,置为NULL。

所以我们需要两个临时变量,一个来遍历链表找到尾节点,一个来保存上一个节点!!!

这时我们来写代码:

void SListPopBack(SListNode** pphead)//尾删
{
	assert(pphead && *pphead);//不能传NULL,链表也不能为空

	//找尾节点
	SListNode* pcur = *pphead;
	SListNode* prev = *pphead;

	while (pcur->next)
	{
            prev = pcur;
            pcur = pcur->next;
	}
	free(pcur);
	pcur = NULL;
	prev->next = NULL;

}

这里我们需要断言一下,就是链表不能为空,链表为空,删什么?

上面的代码是基于链表至少又两个节点的情况下,如果链表只有一个节点呢?也就是头指针指向的就是你要尾删的节点,走一下代码:*pphead == NULL,pcur = prev =NULL,此时while(pcur->next),对空指针解引用,直接报错,既然如此,我们就写多一点代码来专门处理只有一个节点的情况。

void SListPopBack(SListNode** pphead)//尾删
{
	assert(pphead && *pphead);//不能传NULL,链表也不能为空
	//链表只有一个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找尾节点
		SListNode* pcur = *pphead;
		SListNode* prev = *pphead;

		while (pcur->next)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		free(pcur);
		pcur = NULL;
		prev->next = NULL;
	}
}

由于可能会出现只有一个节点的情况,删除的话也需要改变头指针,所以传二级指针。

头删

头删指删除第一个节点。画图理解一下:

我们需要改变头指针的指向,所以必须传二级指针!!!

void SListPopFront(SListNode** pphead)//头删
{
	assert(pphead && *pphead);//不能传NULL,链表也不能为空

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

老规矩,如果链表只有一个节点,走一下代码:SListNode->next = (*pphead) -> next =NULL; 释放掉第一个节点,*pphead = next = NULL,刚好可以处理这种情况,代码不需要修改。

查找

写查找代码也是方便我们后续的指定位置的插入删除作准备。

这个代码和简单,只需要遍历单链表。

SListNode* SListFind(SListNode* phead, SLTDataType x)
{
	SListNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

删除指定节点

我们画图理解一下:

我们需要改变两个个节点,pos前一个节点的next指向改成pos后一个节点。

void SListPopPos(SListNode** pphead, SListNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	SListNode* pcur = *pphead;
	while (pcur->next != pos)
	{
            pcur = pcur->next;
	}
	pcur->next = pos->next;

	free(pos);
	pos = NULL;
		
}

如果pos节点就是第一个节点(即pphead == pos),我们来走一下代码,在while循环中pcur->next 不会找到pos,因为pcur = *phead = pos,所以循环最后会导致pcur走到NULL,然后发生对空指针的解引用,所以我们需要单独处理这一种情况

void SListPopPos(SListNode** pphead, SListNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	if (pos == *pphead)
	{
		//调用头删
		SListPopFront(pphead);
	}
	else
	{
		SListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = pos->next;

		free(pos);
		pos = NULL;
	}
}

由于pos 就是头指针,所以pos的删除就是头删,我们可以调用我们之前写好的头删函数,无论你要不要调用头删函数,都要对头指针进行修改,所以要传二级指针(头指针的地址)。而pos不需要传二级指针是因为一般情况下我们不会再次使用pos,所以我们不需要改变pos的指向,也就不用传pos 的地址过去。

删除指定位置之前的数据

画图理解:

我们需要找到pos前两个节点,释放pos 前一个节点,改变pos前前节点的next 的指向,变为指向pos这个节点。

void SListPopPosFront(SListNode** pphead, SListNode* pos)
{
	assert(pphead && pos); 
	assert(pos != *pphead);
	
	SListNode* pcur = *pphead;
	SListNode* prev = *pphead;
	while (pcur->next != pos)
	{
            prev = pcur;
            pcur = pcur->next;
	}
	prev->next = pos;
	free(pcur);
	pcur = NULL;
		
}

我们要考虑一下如果pos前面只有一个节点的情况下,我们来走一下代码:当prev->next = pos(pphead = pos),释放pcur(也就是释放了pphead)。这时候头指针没了,你找不到后面的数据了!!!

所以我们单独处理一下这种情况。

void SListPopPosFront(SListNode** pphead, SListNode* pos)
{
	assert(pphead && pos); 
	assert(pos != *pphead);
	
	if ((*pphead)->next == pos)
	{
		//头删
		SListPopFront(pphead);
	}
	else
	{
		SListNode* pcur = *pphead;
		SListNode* prev = *pphead;
		while (pcur->next != pos)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		prev->next = pos;
		free(pcur);
		pcur = NULL;
	}
}

头删,需要改变头指针的指向,所以要用二级指针来接收头指针的地址。

删除指定位置之后的数据

画图理解:

由于我们可以通过pos 就可以找到pos后面的节点,所以我们直接传pos就可以了。

我们需要找到pos后后一个节点,然后改变pos 的next 指向指向后后一个节点,为了方便书写代码,我们可以定义一个临时变量del来保存要删除的节点。

这里要注意,指定位置之后必须要有一个节点,否则删什么?所以这里断言一下。

void SListPopPosAfter(SListNode* pos)
{
	assert(pos && pos->next);
	SListNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

我们来考虑一下,如果要删除的节点刚好是尾节点,我们来走一下代码:pos->next = del->next = NULL;free(del),没有问题,那就不用单独处理。

指定位置前插入

画图理解一下:

我们需要改变前一个节点,为了找到pos 的前一个结点,我们需要传入头指针对链表进行遍历。

void SListPushPosFront(SListNode** pphead, SListNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);

	SListNode* newnode = CreatNewnode(x);
	SListNode* pcur = *pphead;
	while (pcur->next != pos)
	{
		pcur = pcur->next;
	}
	newnode->next = pos;
	pcur->next = newnode;
}

如果pos就是第一个节点,上面的代码中while循环就无法找到pos前一个节点,所以我们要单独处理一下这种情况,这时可以调用一下我们写过的头插函数。

头插,需要改变头指针,所以传二级指针。

void SListPushPosFront(SListNode** pphead, SListNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);

	if (*pphead == pos)
	{
		//头插
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* newnode = CreatNewnode(x);
		SListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		newnode->next = pos;
		pcur->next = newnode;
	}
}

指定位置之后插入

画图理解一下:

这个代码我们可以通过pos找到pos后一个节点,所以不需要传入头指针。

void SListPushPosAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);

	SListNode* newnode = CreatNewnode(x);
	SListNode* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}

我们来考虑一下pos就是尾节点的情况,能不能走得通,走一下代码,next = pos->next = NULL,pos->next = newnode,newnode->next = next = NULL,可以,没有任何问题,就不需要改代码了。

销毁链表

销毁链表需要一个一个节点依次删除,所以要遍历链表。

void SListDestroy(SListNode** pphead)
{
	SListNode* pcur = *pphead;
	while (pcur)
	{
		SListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

测试

每写完一个函数,我们就需要进行调试测试来看代码有没有问题,这时一个好的习惯,希望各位也能这样做。

一下是我自己的测试代码:我放在了test.c文件里(就是测试文件)

#include "SList.h"

void test1()
{
    SListNode* plist = NULL;

    //测试尾插
    //SListPushBack(&plist, 1);
    //SListPrint(plist);

    //SListPushBack(&plist, 2);
    //SListPrint(plist);

    //SListPushBack(&plist, 3);
    //SListPrint(plist);

    //测试头插
    SListPushFront(&plist, 10);
    SListPrint(plist);

    SListPushFront(&plist, 20);
    SListPrint(plist);

    SListPushFront(&plist, 30);
    SListPrint(plist);

    //测试尾删
  /*  SListPopBack(&plist);
    SListPrint(plist);

    SListPopBack(&plist);
    SListPrint(plist);

    SListPopBack(&plist);
    SListPrint(plist);*/

    //测试头删
    //SListPopFront(&plist);
    //SListPrint(plist);

    //SListPopFront(&plist);
    //SListPrint(plist);

    //SListPopFront(&plist);
    //SListPrint(plist);

    //测试查找
 /*   SListNode* find = NULL;
    find = SListFind(plist, 30);*/
 /*   if (find == NULL)
    {
        printf("找不到\n");
    }
    else
    {
        printf("找到了!%d\n", find->data);
    }*/
    //测试删除pos节点
    /*SListPopPos(&plist, find);
    SListPrint(plist);*/

    //删除指定位置之前的数据
    //SListPopPosFront(&plist, find);
    //SListPrint(plist);

    //删除指定位置之后的数据
   /* SListPopPosAfter(find);
    SListPrint(plist);*/

    //指定位置前插入
 /*   SListPushPosFront(&plist, find, 100);
    SListPrint(plist);*/

    //指定位置之后插入
  /*  SListPushPosAfter(find, 100);
    SListPrint(plist);*/

    SListDestroy(&plist);
    SListPrint(plist);
}

int main()
{
    test1();
    return 0;
}

分装函数

SList.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLTDataType;

typedef struct SListNode
{
	int data;
	struct SListNode* next;
}SListNode;

//打印
void SListPrint(SListNode* phead);

//插入
void SListPushBack(SListNode** pphead, SLTDataType x);//尾插
void SListPushFront(SListNode** pphead, SLTDataType x);//头插

//删除
void SListPopBack(SListNode** pphead);//尾删
void SListPopFront(SListNode** pphead);//头删

//查找
SListNode* SListFind(SListNode* phead, SLTDataType x);

//删除pos节点
void SListPopPos(SListNode** pphead, SListNode* pos);

//删除指定位置之前的数据
void SListPopPosFront(SListNode** pphead, SListNode* pos);

//删除指定位置之后的数据
void SListPopPosAfter(SListNode* pos);

//指定位置前插入
void SListPushPosFront(SListNode** pphead, SListNode* pos, SLTDataType x);

//指定位置之后插入
void SListPushPosAfter(SListNode* pos, SLTDataType x);

//销毁链表
void SListDestroy(SListNode** pphead);

SList.c

#include "SList.h"

//创建新节点
SListNode* CreatNewnode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

//打印
void SListPrint(SListNode* phead)
{
	SListNode* ptail = phead;
	while (ptail)
	{
		printf("%d->", ptail->data);
		ptail = ptail->next;
	}
	printf("NULL\n");
}

//插入
void SListPushBack(SListNode** pphead, SLTDataType x)//尾插
{
	assert(pphead);

	SListNode* newnode = CreatNewnode(x);

	//如果 *pphead==NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾节点
		SListNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
	}
}

void SListPushFront(SListNode** pphead, SLTDataType x)//头插
{
	assert(pphead);

	SListNode* newnode = CreatNewnode(x);

	newnode->next = *pphead;
	*pphead = newnode;
}

void SListPopBack(SListNode** pphead)//尾删
{
	assert(pphead && *pphead);//不能传NULL,链表也不能为空
	//链表只有一个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找尾节点
		SListNode* pcur = *pphead;
		SListNode* prev = *pphead;

		while (pcur->next)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		free(pcur);
		pcur = NULL;
		prev->next = NULL;
	}
}

void SListPopFront(SListNode** pphead)//头删
{
	assert(pphead && *pphead);//不能传NULL,链表也不能为空

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

//查找
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
	SListNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

//删除pos节点
void SListPopPos(SListNode** pphead, SListNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	if (pos == *pphead)
	{
		//调用头删
		SListPopFront(pphead);
	}
	else
	{
		SListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = pos->next;

		free(pos);
		pos = NULL;
	}
}

//删除指定位置之前的数据
void SListPopPosFront(SListNode** pphead, SListNode* pos)
{
	assert(pphead && pos); 
	assert(pos != *pphead);
	
	if ((*pphead)->next == pos)
	{
		//头删
		SListPopFront(pphead);
	}
	else
	{
		SListNode* pcur = *pphead;
		SListNode* prev = *pphead;
		while (pcur->next != pos)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		prev->next = pos;
		free(pcur);
		pcur = NULL;
	}
}

//删除指定位置之后的数据
void SListPopPosAfter(SListNode* pos)
{
	assert(pos && pos->next);
	SListNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

//指定位置前插入
void SListPushPosFront(SListNode** pphead, SListNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);

	if (*pphead == pos)
	{
		//头插
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* newnode = CreatNewnode(x);
		SListNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		newnode->next = pos;
		pcur->next = newnode;
	}
}

//指定位置之后插入
void SListPushPosAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);

	SListNode* newnode = CreatNewnode(x);
	SListNode* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}

//销毁链表
void SListDestroy(SListNode** pphead)
{
	SListNode* pcur = *pphead;
	while (pcur)
	{
		SListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

单链表完结撒花!!!

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

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

相关文章

NzN的数据结构--二叉树part2

上一章我们介绍了二叉树入门的一些内容&#xff0c;本章我们就要正式开始学习二叉树的实现方法&#xff0c;先三连后看是好习惯&#xff01;&#xff01;&#xff01; 目录 一、二叉树的顺序结构及实现 1. 二叉树的顺序结构 2. 堆的概念及结构 3. 堆的实现 3.1 堆的创建 …

90天玩转Python—10—基础知识篇:函数详解

90天玩转Python系列文章目录 90天玩转Python—01—基础知识篇:C站最全Python标准库总结 90天玩转Python--02--基础知识篇:初识Python与PyCharm 90天玩转Python—03—基础知识篇:Python和PyCharm(语言特点、学习方法、工具安装) 90天玩转Python—04—基础知识篇:Pytho…

循环单链表算法库

学习贺老师数据结构 数据结构之自建算法库——循环单链表_循环单链表 csdn-CSDN博客​​​​​​ 整理总结出的循环单链表算法库 v1.0 : 基本实现功能 v2.0(2024.4.6): 修复Delete_SpecificLocate_CyclicList()删除节点函数bug,添加验证删除节点是否超范围判断 目录 1.主要功能…

Blender2.83 下载地址及安装教程

Blender是一款开源的3D计算机图形软件&#xff0c;广泛应用于动画制作、游戏开发、建模、渲染等领域。它提供了一套强大的工具和功能&#xff0c;让用户能够进行三维建模、动画制作和视觉效果的创作。 Blender支持多种文件格式的导入和导出&#xff0c;使用户能够与其他软件进…

【Linux源码学习】(一)源码下载、解压说明

目录 一、Linux源码下载地址 二、使用7z解压 一、Linux源码下载地址 官网下载&#xff1a;The Linux Kernel Archives 我们可以到这个地址下载各种版本的压缩包&#xff1a;即上图对应的http网址。 Index of /pub/ 选择Linux 找内核代码 这里直接选择最新的6.x 然后往下翻&…

《猎灵online》游戏完整源码(源码+客户端+服务端+文档+工具),云盘下载

《猎灵》是一款由国内知名开发运营开发的大型3D魔幻网游&#xff0c;《猎灵》研发团队突破诸多瓶颈&#xff0c;首创“全自由无限制PK”&#xff0c;让玩家拒绝无意义等待&#xff0c;自由作战不受任何束缚&#xff0c;真正的实现想战就战&#xff0c;游戏创建了六界神魔乱斗的…

单调栈用法

文章目录 1. 单调栈1.1 理解单调栈&#xff08;模板&#xff09;1.2 每日温度1.3 子数组的最小值之和1.4 柱状图中最大的矩形1.5 最大矩形1.6 最大宽度坡1.7 去除重复字母 1. 单调栈 单调栈经典的用法&#xff1a; 对每个位置都求&#xff1a; 当前位置的左侧比当前位置的数…

UDP网络程序

上一章中&#xff0c;我们介绍了socket&#xff0c;以及TCP/UDP协议。这一章带大家实现几个UDP协议的网络服务。我们需要一个 服务端和一个客户端。 1.服务端实现 1.1socket函数 #include <sys/types.h> #include <sys/socket.h>int socket(int domain, in…

【JAVA基础篇教学】第六篇:Java异常处理

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第五篇&#xff1a; Java异常处理。 异常处理是Java编程中重要的一部分&#xff0c;它允许开发人员在程序运行时检测和处理各种错误情况&#xff0c;以保证程序的稳定性和可靠性。在Java中&#xff0c;异常被表示为对象&am…

【 书生·浦语大模型实战营】作业(三):“茴香豆” 搭建你的RAG 智能助理

【 书生浦语大模型实战营】学习笔记&#xff08;三&#xff09;&#xff1a;“茴香豆” 搭建你的RAG 智能助理作业 &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI…

JVM参数列表

-client :设置JVM使用client模式,特点启动较快(神机不明显(I5/8G/SSD)) -server :设置JVM使用server模式。64位JDK默认启动该模式 -agentlib:libname[options] :用于加载本地的lib -agentlib:hprof :用于获取JVM的运行情况 -agentpath:pathnamep[options] :加载制定路径的本…

PHP01——php快速入门 之 使用phpstudy快速搭建PHP环境

PHP01——php快速入门 之 使用phpstudy快速搭建PHP环境 0. 前言1. 下载小皮面板1.1 下载phpstudy&#xff08;小皮面板&#xff09;1.2 启动、简单访问1.2.1 启动Apache1.2.2 访问1.2.3 访问自定义文件或页面 2. 创建网站2.1 创建网站2.2 可能遇到的问题2.2.1 hosts权限问题&am…

极海APM32电机驱动板记录(二)

文章目录 1、解除写保护2、极海驱动板资源概述3、新建工程4、点灯5、嘀嗒定时器6、中断7、串口打印8、adc读取9、i2c尝试10、定时器测试11、电机驱动pwm测试 上一篇文章算是简单了解了一下极海的板子开发环境吧&#xff0c;结果前几天板子来了&#xff0c;然后发现一个大bug&am…

力扣题目 19:删除链表的倒数第N个节点 【python】

&#x1f464;作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a; 码上找工作http://t.csdnimg.cn/Q59WX 作者专栏每日更新&#xff1a; LeetCod…

Qt-绘制多边形、椭圆、多条直线

1、说明 所有的绘图操作是在绘图事件中进行。mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWi…

C++ 类和对象(一)

目录 0.前言 1.面向过程&面向对象 1.1面向过程编程&#xff08;PP&#xff09; 1.2面向对象编程&#xff08;OOP&#xff09; 1.3从C到C 2.类的引入 2.1C语言中的结构体 2.2C中类的引入 2.3结构体与类的区别 2.4为什么引入类 3.类的定义 3.1声明与定义不分离 …

【Java探索之旅】从输入输出到猜数字游戏

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、输入输出1.1 输出到控制台1.2 从键盘输入 二、猜数字游戏2.1 所需知识&#xff1a…

【动态规划】【01背包】Leetcode 1049. 最后一块石头的重量 II

【动态规划】【01背包】Leetcode 1049. 最后一块石头的重量 II 解法 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法 &#x1f612;: 我的代码实现> 动规五部曲 ✒️确定dp数组以及下标的含义 dp[j]表示容量为…

Learn SRP 01

学习链接&#xff1a;Custom Render Pipeline (catlikecoding.com) 使用Unity版本&#xff1a;Unity 2022.3.5f1 1.A new Render Pipeline 1.1Project Setup 创建一个默认的3D项目&#xff0c;项目打开后可以到默认的包管理器删掉所有不需要的包&#xff0c;我们只使用Unit…

陆面、生态、水文模拟与多源遥感数据同化

原文链接&#xff1a;陆面、生态、水文模拟与多源遥感数据同化https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601198&idx6&sn51b9b26b75c9df1f11dcb9a187878261&chksmfa820dc9cdf584df9ac3b997c767d63fef263d79d30238a6523db94f68aec621e1f91df85f6…