手撕单链表(单向,不循环,不带头结点)的基本操作

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary-walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互赞互关一下,看到必回
👑👑👑💎👑👑👑

 目录:

前言:对于单链表的基本操作重在考验大家对C语言指针的底子

一:传值传参区别

二:尾插

三:头插

四:尾删

五:头删

六:指定数据的查找

七:指定位置之前的删除

八:指定位置之后的删除

九:任意位置之前的插入

十:任意位置之后的插入

结语


 一:传值传参区别

这里就拿一个比较经典的问题来引入吧!

   写一个函数实现2个数 的交换

 

对于刚刚接触编程的铁子们,对这个结果 可能存在很大的疑惑

不慌不忙,接下来我慢慢给大家解释 

int a = 1, b = 2;
    int* p = &a;
    *p = 3;

想必大家对这个代码应该不会很陌生吧。

此时我们对指针p进行解引用拿到的就是变量 a 

也就是说,此时我们通过借助指针实现了对a   的改变

同理,这里我在调用Swap( )这个函数的时候,是不是进行传地址就可以实现对2个数的交换?

话不多说,接下来我们代码实现

 

 是滴,此时确实实现了2个数的交换

分析:

1)传参的本质:形参是对实参的一份临时拷贝,对形参的临时修改不会影响实参

2)所以说:当需要对变量进行改变的时候,我们就需要传对应的地址就可以

如何理解“ 传对应的地址”

比如说:

      改变int 类型的变量,这时就需要传int*的指针(地址)    

     改变int *类型的变量,这时就需要传int**的指针(地址)    

     改变结构体类型的变量,这时就需要传结构体的指针(地址)

 二:尾插

分析:

1)首先为要插入进来的数据开辟结点

2)链表不为空的时候:注意此时要改变的是结构体

         首先 先找到尾结点( 链表最后一个结点的next为空)

         其次进行尾插

3)链表为空:注意此时改变的是结构体类型的指针(头节点为空)

         直接进行插入即可

草图如下:

对非空的链表插入前:

 插入后:

   接下来可是重头戏,好好看,一不仔细,就错失了,那可就不好理解了,避免这个“瓜”没有吃到,反而懊恼不已

 1)开辟结点为插入的数据:
因为之后插入需要频繁开辟结点,所以这里写成了一个函数
SLNode* BuyNode(DataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return NULL;
	}
	// 对开辟的结点进行初始化
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
2)找尾结点:这里就依次遍历即可

注意啦:看看这样写对不???

SLNode* ptail = phead;
    while (ptail)
    {
        ptail = ptail->next;
    }
    ptail->next = newnode;

NO,NO,NO

乍一看,看不出啥问题,这就是“码图”结合了

这里以逻辑结构(人类的思维,为了形象化的理解)来解释

逻辑图:

 

   当ptail这个指针指向3这个结点的时候,是不为空的,所以就继续进入我的循环里,此时的尾结点就变成了NULL这个对应当结点,下面在进行插入自然也是不能把新的结点和我原来链表进行有效的连接起来

找尾结点的正确代码:

 while (ptail->next)
    {
        ptail = ptail->next;
    }
    ptail->next = newnode;

 3)当链表为空的时候我们发现以上代码不可取:

因为此时实参plist  传给我的phead这个形参就是一份临时拷贝,我对phead(结构体指针)改变不影响plist(结构体指针)的变化

有了前面那个传值传参的引入,想必大家此时应该有了见解了吧

没错!

就是实参传结构体指针的地址,形参用二级指针

 对应完整代码:

void SLPushBack(SLNode* *phead, DataType x)
{
	/*
	1:开辟结点
	2:判读是否为空的链表
	3:非空:找到尾结点;此时改变的是结构体,需要传结构体的地址
	4:为空:直接插入:  因为改变的是结构体类型的指针,所有需要传结构体类型的指针的地址,涉及到二级指针
	*/
	SLNode* newnode = BuyNode(x);
	if (*phead == NULL)  //为空
	{
		newnode->next = *phead;// 对*phead解引用 就是plist这个实参
		*phead = newnode;
		return;
	}
	//非空
	/*  找尾结点:err
	SLNode* ptail = phead;
	while (ptail)
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;

	*/
	SLNode* ptail = *phead;
	while (ptail->next)  //找尾结点
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;
	/*
	当链表为空的时候,以上代码有问题
	为空的时候需要对头节点进行改变,注意头节点是结构体类型指针所以需要传地址
	*/

}

 三:头插

分析:

1) 首先为插入数据开辟结点

2)因为此时改变的是结构体指针(plist),所以需要传入结构体指针的地址

void SLPushFront(SLNode** phead, DataType x)
{
	/*
	1:为x开辟结点
	2:更新头节点
	3:因为改变的是结构体类型的指针,所有需要传结构体类型的指针的地址
	传参的本质是:拷贝:形参是对实参的一份临时拷贝,对形参的修改不会影响我实参的变化
	*/
	SLNode* newnode = BuyNode(x);
	newnode->next = *phead;// 对*phead解引用 就是plist这个实参
	*phead = newnode;
}

四:尾删

分析:

1)首先判断链表是否为空;为空不需删除

2)其次:判断链表是否为一个结点;因为此时改变的头节点(结构体指针);那就涉及到了传结构体指针的地址

3)最后就是多个结点的情况:

      先找尾结点

       删除尾结点

 1)先从正常情况说起(多个结点)

 找尾结点:这里需要找到尾结点的前一个结点,避免free(ptail)时找不到新的尾结点

 尾删后:

SLNode* ptail = *phead;
	SLNode* pre =* phead;
	while (ptail->next)  //找尾结点
	{
		pre = ptail;//保存尾结点的前一个结点
		ptail = ptail->next;
	}
	free(ptail);
	ptail = pre;//尾结点更新
	ptail->next = NULL;//不要忘了置空
2)只有一个结点

注意这里需要传入结构体指针的地址

if ((*phead)->next == NULL)  //一个结点,注意*与->优先级
	{
		free(*phead);
		*phead = NULL;
		return;
	}
 3)判空

直接暴力检查即可:

assert(*phead);

 对应完整代码:

void SLPopBack(SLNode** phead)
{
	/*
	1:判断是否为空
	2:判断是否为一个结点:因为此时改变的是头节点(结构体指针)
	3:找到尾结点,此时尾结点的前一个结点成为新的结点
	4:  *phead 就是头指针  plist
	*/
	//为空:
	assert(*phead);
	
	if ((*phead)->next == NULL)  //一个结点,注意*与->优先级
	{
		free(*phead);
		*phead = NULL;
		return;
	}
	// 非空
	SLNode* ptail = *phead;
	SLNode* pre =* phead;
	while (ptail->next)  //找尾结点
	{
		pre = ptail;//保存尾结点的前一个结点
		ptail = ptail->next;
	}
	free(ptail);
	ptail = pre;//尾结点更新
	ptail->next = NULL;//不要忘了置空


}

五:头删

相信有了前面的尾删,我们对头删那便是轻轻松拿捏了

1)判空

2)非空

 1)判空

assert(*phead);  //直接暴力检查

2)非空:  删除头节点之前需要保存一下

对应完整代码:

void SLPopFront(SLNode** phead)
{
	/*
	1:判是否为空 
	2:非空:删除头节点之前需要保存一下第二个结点
	*/
	assert(*phead);//为空
	SLNode* psec = (*phead)->next;//保存第二个结点
	free(*phead);
	*phead = psec;//更新


}
六:指定数据查找

1:若是当前数据存在,则返回对应的结点;否则返回NULL

2:依次遍历

相信有了前面的基础,我们对这个区区查找的代码轻轻松拿下

SLNode* SLFind(SLNode* phead, DataType x)
{
	/*
	若是找到返回该节点
	循环遍历
	*/
	SLNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
			return pcur;//返回节点
		else
			pcur = pcur->next;//更新
	}
	return NULL;
}
七:指定位置之前的删除 

分析:

假设对pos这个位置之前的进行删除

1:pos若是为头节点,则不需要删除

2:pos为第二个结点,其实就是进行头删的操作,注意此时改变的是头节点(结构体指针),所以需要传二级指针

3:正常情况:找到pos前一个结点

 1:pos为头节点

直接暴力断言,就像当你作业还没有写完,但你依然再玩游戏此时你的父亲突然过来问你,作业写完了吗,你回答到:没有。你父亲直接就是一顿说,此时你就乖乖去写作业了

assert(*phead != pos);

2:pos 为第二个结点

if ((*phead)->next == pos)//pos为第二个结点
    {
        free(*phead);
        *phead = pos;//pos是新的头节点
    }

3:正常情况
SLNode* pre = *phead;
		while (pre->next->next != pos)
		{
			pre = pre->next;
		}
		free(pre->next);
		pre->next = pos;

对应完整代码: 

void SLEarseBefore(SLNode** phead, SLNode* pos)
{
	/*
	1:pos为头节点是不可以删除的
	2:pos为第二个结点,此时要删除的是头节点,改变的是结构体指针(phead),需要二级指针
	3:正常情况,找到pos前一个结点
	*/
	assert(pos != *phead);//保证pos不为头节点
	if ((*phead)->next == pos)//pos为第二个结点
	{
		free(*phead);
		*phead = pos;//pos是新的头节点
	}
	else
	{
		SLNode* pre = *phead;
		while (pre->next->next != pos)
		{
			pre = pre->next;
		}
		free(pre->next);
		pre->next = pos;
	}
}
八:指定位置之后的删除

分析:假设要删除的位置是pos

    1:pos为最后一个结点;没有必要删除
    2:pos不为最后一个结点

对应代码:

void SLEarseAfter(SLNode** phead, SLNode* pos)
{
	/*
	1:pos为最后一个结点;没有必要删除
	2:pos不为最后一个结点
	*/
	assert(pos->next != NULL);//暴力判断是否为最后一个
	SLNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}
九:任意位置之前的插入

假设任意位置为pos

1:pos为头节点:可以借助头插的函数进行,注意此时改变的是头节点(结构体指针),要传结构体指针的地址

2:pos不是头节点:需要找到pos的前一个结点

3:为要插入的数据开辟结点

 对应代码:

void SLInsertBefore(SLNode** phead, SLNode* pos, DataType x)
{
	/*
	*1:开辟结点
	2:找到pos前面的结点(pos不是头节点)
	3:pos是头节点此时变成头插
	*/
	SLNode* newnode = BuyNode(x);
	if (pos == *phead)
	{
		SLPushFront(phead, x);
		return;
	}
	else
	{
		SLNode* pre = *phead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		//插入
		pre->next = newnode;
		newnode->next = pos;
	}
}
十:任意位置之后的插入

分析:假设此位置是pos

1:开辟结点

2:保存pos后面的那个结点SLNode* p =  pos->next

3: 直接插入

void SLInsertAfter(SLNode* phead, SLNode* pos, DataType x)
{
	/*
	1:开辟结点
	2:保存一下pos后面的那结点(否则会连不上)
	3:直接插入
	*/
	SLNode* newnode = BuyNode(x);
	SLNode* p = pos->next;//保存pos后的结点
	//插入
	pos->next = newnode;
	newnode->next = p;
}

 整个单链表完整代码:

SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DataType;
typedef struct SListNode
{
	DataType data;//数据域
	struct SListNode* next;//指针域
}SLNode;

void SLPrint(SLNode* phead);
void SLPushFront(SLNode** phead, DataType x);
void SLPushBack(SLNode** phead, DataType x);
void SLPopBack(SLNode** phead);
void SLPopFront(SLNode** phead); 
SLNode* SLFind(SLNode* phead, DataType x);//对指定数据进行查找
void SLModify(SLNode* phead, SLNode*pos,DataType x);

void SLInsertBefore(SLNode** phead, SLNode* pos, DataType x);//在指定数据之前插入
void SLInsertAfter(SLNode* phead, SLNode* pos, DataType x);//在指定数据之前插入

void SLEarseBefore(SLNode** phead, SLNode* pos);//任意位置之前的删除
void SLEarseAfter(SLNode** phead, SLNode* pos);//任意位置之后的删除


SList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

void SLPrint(SLNode* phead)
{
	SLNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;//更新
	}
	printf("NULL\n");
}
SLNode* BuyNode(DataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return NULL;
	}
	// 对开辟的结点进行初始化
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SLPushFront(SLNode** phead, DataType x)
{
	/*
	1:为x开辟结点
	2:更新头节点
	3:因为改变的是结构体类型的指针,所有需要传结构体类型的指针的地址
	传参的本质是:拷贝:形参是对实参的一份临时拷贝,对形参的修改不会影响我实参的变化
	*/
	SLNode* newnode = BuyNode(x);
	newnode->next = *phead;// 对*phead解引用 就是plist这个实参
	*phead = newnode;
}
void SLPushBack(SLNode* *phead, DataType x)
{
	/*
	1:开辟结点
	2:判读是否为空的链表
	3:非空:找到尾结点;此时改变的是结构体,需要传结构体的地址
	4:为空:直接插入:  因为改变的是结构体类型的指针,所有需要传结构体类型的指针的地址,涉及到二级指针
	*/
	SLNode* newnode = BuyNode(x);
	if (*phead == NULL)  //为空
	{
		newnode->next = *phead;// 对*phead解引用 就是plist这个实参
		*phead = newnode;
		return;
	}
	//非空
	/*  找尾结点:err
	SLNode* ptail = phead;
	while (ptail)
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;

	*/
	SLNode* ptail = *phead;
	while (ptail->next)  //找尾结点
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;
	/*
	当链表为空的时候,以上代码有问题
	为空的时候需要对头节点进行改变,注意头节点是结构体类型指针所以需要传地址
	*/

}
void SLPopBack(SLNode** phead)
{
	/*
	1:判断是否为空
	2:判断是否为一个结点:因为此时改变的是头节点(结构体指针)
	3:找到尾结点,此时尾结点的前一个结点成为新的结点
	4:  *phead 就是头指针  plist
	*/
	//为空:
	assert(*phead);
	
	if ((*phead)->next == NULL)  //一个结点,注意*与->优先级
	{
		free(*phead);
		*phead = NULL;
		return;
	}
	// 非空
	SLNode* ptail = *phead;
	SLNode* pre =* phead;
	while (ptail->next)  //找尾结点
	{
		pre = ptail;//保存尾结点的前一个结点
		ptail = ptail->next;
	}
	free(ptail);
	ptail = pre;//尾结点更新
	ptail->next = NULL;//不用忘了置空
	
	//对一个与多个节点的操作可以合并
	//只要找到倒数第二个结点就可以
	/*SLNode* ptail = *phead;

	while (ptail->next->next)
	{
		ptail = ptail->next;
	}
	free(ptail->next);
	ptail->next = NULL;*/

}
void SLPopFront(SLNode** phead)
{
	/*
	1:判是否为空 
	2:非空:删除头节点之前需要保存一下第二个结点
	*/
	assert(*phead);//为空
	SLNode* psec = (*phead)->next;//保存第二个结点
	free(*phead);
	*phead = psec;//更新
}
SLNode* SLFind(SLNode* phead, DataType x)
{
	/*
	若是找到返回该节点
	循环遍历
	*/
	SLNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
			return pcur;//返回节点
		else
			pcur = pcur->next;//更新
	}
	return NULL;
}
void SLInsertBefore(SLNode** phead, SLNode* pos, DataType x)
{
	/*
	*1:开辟结点
	2:找到pos前面的结点(pos不是头节点)
	3:pos是头节点此时变成头插
	*/
	SLNode* newnode = BuyNode(x);
	if (pos == *phead)
	{
		SLPushFront(phead, x);
		return;
	}
	else
	{
		SLNode* pre = *phead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		//插入
		pre->next = newnode;
		newnode->next = pos;
	}
}
void SLInsertAfter(SLNode* phead, SLNode* pos, DataType x)
{
	/*
	1:开辟结点
	2:保存一下pos后面的那结点(否则会连不上)
	3:直接插入
	*/
	SLNode* newnode = BuyNode(x);
	SLNode* p = pos->next;//保存pos后的结点
	//插入
	pos->next = newnode;
	newnode->next = p;
}
void SLEarseBefore(SLNode** phead, SLNode* pos)
{
	/*
	1:pos为头节点是不可以删除的
	2:pos为第二个结点,此时要删除的是头节点,改变的是结构体指针(phead),需要二级指针
	3:正常情况,找到pos前一个结点
	*/
	assert(pos != *phead);//保证pos不为头节点
	if ((*phead)->next == pos)//pos为第二个结点
	{
		free(*phead);
		*phead = pos;//pos是新的头节点
	}
	else
	{
		SLNode* pre = *phead;
		while (pre->next->next != pos)
		{
			pre = pre->next;
		}
		free(pre->next);
		pre->next = pos;
		SLNode* pre = *phead;
		while (pre->next->next != pos)
		{
			pre = pre->next;
		}
		free(pre->next);
		pre->next = pos;
	}
}
void SLEarseAfter(SLNode** phead, SLNode* pos)
{
	/*
	1:pos为最后一个结点;没有必要删除
	2:pos不为最后一个结点
	*/
	assert(pos->next != NULL);//暴力判断是否为最后一个
	SLNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}
void SLModify(SLNode* phead, SLNode* pos, DataType x)
{
	assert(phead);
	pos->data = x;

}


test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

void TestPush()
{
	SLNode* plist = NULL;
	//SLPushFront(&plist,1);
	//SLPushFront(&plist,2);
	//SLPushFront(&plist,3);
	//SLPrint(plist);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLPrint(plist);

}
void TestPop()
{
	SLNode* plist = NULL;
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLPushBack(&plist, 6);
	SLPushBack(&plist, 7);
	SLPrint(plist);
	SLNode* pos = SLFind(plist, 7);
	if(pos)//避免pos为空
		SLInsertAfter(plist, pos, 44);
	SLPrint(plist);

	/*SLPopBack(&plist);
	SLPrint(plist);

	SLPopBack(&plist);*/
	/*SLPopFront(&plist);
	SLPrint(plist);

	SLPopFront(&plist);
	SLPrint(plist);*/


}
void TestEarse()
{
	SLNode* plist = NULL;
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLPushBack(&plist, 6);
	SLPushBack(&plist, 7);
	SLPrint(plist);
	SLNode* pos = SLFind(plist, 7);
	if (pos)//避免pos为空
		//SLEarseAfter(&plist, pos);
		SLModify(plist, pos, 77);
	SLPrint(plist);
}

void Swap(int *x, int* y)
{
	int tmp = *x;//中间变量
	*x = *y;
	*y = tmp;
}
int main()
{
	TestEarse();
	
	return 0;
	/*
	总结:
	1:是指针不一定必须断言,是否断言取决于你的操作
	2:在函数外面改变变量,需要传地址,想改变谁,就传对应类型的地址
	3:对于链表这块一定注意自己要改变的是结构体还是结构体指针???因为这决定了传的地址类型不一样
	4:找
	*/
}

结语:

对于初学单链表的小白来讲(比如我本人,哈哈哈),这个理解起来确实不是那么顺手,其实重点在指针和结构体的掌握。当然自己也需要反复的体会其中的奥妙,

都看到这里了,屏幕前的你,咱一波关注走起呗,你的支持是我不懈的动力,蟹蟹

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

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

相关文章

鹅目标检测数据集VOC格式300张

鹅&#xff0c;一种家禽&#xff0c;以其独特的形态、生活习性和文化象征意义而备受人们喜爱。 鹅属于鸟纲、雁形目、鸭科&#xff0c;是一种大型水禽。它们的身体肥胖&#xff0c;羽毛洁白如雪&#xff0c;嘴部扁平且坚硬&#xff0c;脚部有蹼&#xff0c;适合在水中游动。 …

智能合约笔记

前言&#xff1a; 首先了解下为什么会出现智能合约&#xff0c;打个比方现在有两个人A和B打赌明天会不会下雨&#xff0c;每个人赌注100元&#xff0c;如果第二天下雨则A拿走200元&#xff0c;否则B拿走200元&#xff0c;这样就有一个问题&#xff0c;赌注要到第二天才能见效&…

RK3568驱动指南|第十篇 热插拔-第112章 热插拔简介

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

在IntelliJ IDEA上使用通义灵码(TONGYI Lingma)

参考链接&#xff1a; 通义灵码产品介绍_智能编码助手_AI编程_云效(Apsara Devops)-阿里云帮助中心 【IDEA如何使用通义灵码&#xff1f;】_idea 通义灵码-CSDN博客 1. 简介 1.1 定义 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提…

Android开发基础(一)

Android开发基础&#xff08;一&#xff09; 本篇主要是从Android系统架构理解Android开发。 Android系统架构 Android系统的架构采用了分层的架构&#xff0c;共分为五层&#xff0c;从高到低分别是Android应用层&#xff08;System Apps&#xff09;、Android应用框架层&a…

UML期末复习(带习题,选择题,判断题)(持续更新)

UML期末复习 UML简介UML模型图的构成UML事物UML包含4种事物&#xff1a;构件事物&#xff1a; UML模型的静态部分&#xff0c;描述概念或物理元素行为事物&#xff1a;UML模型图的动态部分&#xff0c;描述跨越空间和时间的行为分组事物&#xff1a; UML模型图的组织部分&#…

某查查请求头参数加密分析(含JS加密算法与Python爬虫源码)

文章目录 1. 写在前面2. 请求分析3. 断点分析4. 扣加密JS5. Python爬虫代码实现 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff…

Acrel-5000重点用能单位能耗在线监测系统的实际应用分析-安科瑞 蒋静

摘要&#xff1a;根据《重点用能节能办法》&#xff08;国家发展改革委等第七部委2018年15号令&#xff09;、《重点用能单位能耗在线监测系统推广建设工作方案》&#xff08;发改环资[2017]1711号&#xff09;和《关于加速推进重点用能单位能耗在线监测系统建设的通知》&#…

【踩坑】JDK1.8 AudioSystem 无法关闭流的问题

文章目录 一、前言二、开始狼人杀嫌疑人1&#xff1a;嫌疑人2&#xff1a; 三、复盘Jdk8原生bug解决方法和原理解析 一、前言 做了一个基于文字转语言的小接口&#xff0c;想删除本地wav文件来着&#xff0c;结果发现删除不了。 很明显被占用了&#xff0c;还是被Java占用了……

点击出现视频弹框

<VideoPlayer ref"video":size"{ width: 88%, height: 100% }" :videoSrc"currentVideo.url"></VideoPlayer>import VideoPlayer from /components/video-player.vue

MySQL之导入以及导出远程备份v

目录 一.navact数据导入导出 1.1 导入 1.2 导出 二. mysqldump命令导入导出数据 2.1 导入 2.2 导出 三.load data file进行数据导入导出&#xff08;只限于单表&#xff09; 3.1 导入 3.2 导出 四.远程连接 好啦就到这里了哦!!!希望帮到你哦!!! 一.navact数据导入导…

条款21:必须返回对象时,别妄想返回其引用

考虑一个表示有理数的类&#xff0c;其中包含一个计算两个有理数相乘的函数: class Rational { public:Rational(int numerator 0, int denominator 1) :n{ numerator }, d{ denominator }{} private:int n, d; // 分子和分母friend const Rational& operator*(const R…

Win11安装与卸载Oracle 19c数据库

一、官网下载安装包 进入官网&#xff0c;选择产品-Oracle DataBase&#xff0c;点击进入下载界面 官网 二、安装 将下载的压缩包进行解压&#xff0c;解压路径随意即可 1 双击exe文件开始安装 等待出现如下页面 2 选择所示&#xff0c;点击下一步 3 选择桌面类安装 4 创…

2000-2022各省、地级市风险投资(VC)数据

2000-2022各省、地级市风险投资&#xff08;VC&#xff09;数据 1、时间&#xff1a;2000-2022年 2、范围&#xff1a;350个地级市&#xff0c;34省 3、指标&#xff1a;包含投资机构层面的风险投资原始数据&#xff0c;汇总到省市层面的结果数据&#xff0c;具体指标如下&a…

走进shell

Linux系统启动时&#xff0c;会自动创建多个虚拟控制台。虚拟控制台是运行在Linux系统内存中的终端会话。 打开Linux控制台Terminal使用tty命令查看当前使用的虚拟控制台。 注&#xff1a;tty 表示电传打字机(teletypewriter) $ tty /dev/pts/0表示当前使用的是/dev/pts/0 虚拟…

Android 事件分发介绍

文章目录 一、目的二、环境三、相关概念3.1 事件分发 四、详细设计4.1应用布局4.1.1 应用布局结构4.1.2 LayoutInspector 4.2 关键View&方法4.2.1 相关View4.2.2 相关方法4.2.3 View与方法关系 4.3 事件分发概念图4.3.1 事件分发类图4.3.2 事件分发模型图 4.4 Activity组件…

vmlinux, System.map; cmake的find_package(Clang)产生的变量们; geogebra单位切向量(简单例子)

linux4.15.y内核中的函数个数 依赖关系: vmlinux, vmlinux.bin, bzImage cd /bal/linux-stable/ file vmlinux #vmlinux: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]b99bbd9dda1ec2751da246d4a7ae4e6fcf7d789b, not str…

渐进增强与优雅降级:提升用户体验的双重策略

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【人工智能】智能电网:未来能源的革命

未来能源的革命 智能电网革命的意义在于将电力行业从传统的集中式发电和集中式输配电模式转变为智能化、分布式、互动式的能源网络。 现在我们从以下方面详细认真的了解一下智能电网&#xff1a; 智能变电站&#xff0c;智能配电网&#xff0c;智能电能表&#xff0c;智能交互…

基于arcgis的遥感深度学习数据集制作

由于很多时候&#xff0c;我们在研究过程中往往需要根据实际情况使用自己的影像数据来提取目标物&#xff0c;如果没有合适的公开数据集的话&#xff0c;为了满足实际需要&#xff0c;我们就需要制作符合自己要求的数据集。 今天我们就根据实际情况来详细讲解如何利用arcgis&am…