双链表实现,增 删 改 查(基础详细版)

news2024/10/1 9:40:08

0.在开始之前建议先跟着思路,走一遍,调试部分我就不放了主要写的是实现思路。当然最后也会把源码附上。

1. 带头双向循环链表(简称:双向链表)

  1. 双向循环带头链表:
    1. 红色的指向正的 最后一个节点指向头结点
    2. 绿色的指向反的 从最后一个开始遍历,直到头结点
    3. 头结点里存储着指向尾节点的和头结点的
    4. 比如:phead->next = pcur ; phead->prev = ptail;
  2. 每个节点的当前节点都保存着指向下一个节点的地址指向上一个节点的地址
  3. 代码的基本结构
typedef int LTDataType;
 typedef struct ListNode
 {
    struct ListNode* next; //指针保存下⼀个节点的地址
    struct ListNode* prev; //指针保存前⼀个节点的地
    LTDataType data;
 }LTNode;
  1. 单链表和双链表为NULL时的两种不一样的情况
    1. 单链表:第一个直接置为NULL
    2. 双链表: 只有一个带头链表,里面不存储任何有效的数据,双向链表不能置为NULL,要让他指向自己实现自循环

1.1. 单链表和双链表与指针的关系

  1. 双链表的本质,就是不会对头节点改变,而单链表头插对头节点改变了,所以要二级指针
  2. 区别就是要不要对(单链表和双链表)的头指针改变
  3. 举个例子:对于单链表的头指针,有些地方也可以不用传二级指针

        就比如尾插时不用对头指针改变,而头插就需要对头指针改变了

  1. 已知传过去的是创建好有空间的节点,此时尾插不需要二级指针
  2. 总结概括:改变单链表头指针指向就要传二级地址。双链表不要改变头节点

2. 代码实现

  1. 在双链表中不用改变头结点,要改变的是结构体内前后指针的指向。和改变头结点的指向完全是两个概念
  2. 在实现也是要分三个文件的,DouList.c  DouList.h  test.c 。功能的实现,函数和类型的声明,测试文件
  3. 这里讲的是核心部分,也就是功能的实现

2.1. 双链表基本结构

  1. prev指向上一个节点,next指向下一个节点
typedef int DLDataType;
typedef struct ListNode
{
	DLDataType data;
	struct ListNode* prev;//指向上一个节点
	struct ListNode* next;//指向下一个节点
}DLNode;//链表节点

2.1.1. 初始化

//第二种初始化方法为了保证接口的一致性,让传递的值都是一级指针
DLNode* DLInit()
{
	DLNode* node = DLBuyNode(-1);
	return node;
}

DLNode* DLInit()
{
	DLNode* node = DLBuyNode(-1);
	return node;
}
//test.c 通过接收返回值的方式
void DListTest1()
{
	DLNode* plist = NULL;//哨兵位
	plist = DLInit();
}

2.1.2. 空间申请

  1. 空间申请部分很简单,就是创建新的节点,然后赋值上需要插入的值
  2. 接着让新创建的节点实现自循环,因为这个是双链表
//新空间
DLNode* DLBuyNode(DLDataType x)
{
    DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
    if (newnode == NULL)
    {
        perror("malloc nwe");
        exit(-1);//推出整个程序
    }
    newnode->data = x;
    newnode->next = newnode->prev = newnode;//申请新节点,需要自循环
    return newnode;
}

2.2. 尾插

  1. 不管是头插还是尾插都不需要改变头节点的位置
  2. 头节点既不能删除也不能改变,没必要传二级指针
  3. 头节点不能为NULL,因为这是双链表
  4. phead->prev->next = newnode;必须写在前面,不然会找不到尾节点
void DLPushBack(DLNode* phead, DLDataType x)
{
	assert(phead);
	DLNode* newnode = DLBuyNode(x);
	//phead phead->prev newnode
	newnode->prev = phead->prev;//phead->prev 是头节点指向的上一个节点也就是尾节点
	newnode->next = phead;

	phead->prev->next = newnode;//尾节点指向的下一个节点
	phead->prev = newnode;
}

2.2.1. 空间申请

  1. 空间申请部分很简单,就是创建新的节点,然后赋值上需要插入的值
  2. 接着让新创建的节点实现自循环,因为这个是双链表
//新空间
DLNode* DLBuyNode(DLDataType x)
{
    DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
    if (newnode == NULL)
    {
        perror("malloc nwe");
        exit(-1);//推出整个程序
    }
    newnode->data = x;
    newnode->next = newnode->prev = newnode;//申请新节点,需要自循环
    return newnode;
}
  1. 这张图是什么意思呢?意思是在头节点左边插入其实也是尾插,你可以想象一下,把链表想成环形

2.3. 打印链表

  1. 打印链表需要有两个注意的点,这个不同于单链表,结束条件不能为pcur != NULL;也不可以是pcur->next != NULL,打印到尾节点时会停下循环不进尾节点
  2. 而是pcur != phead; 头节点不存储有效数据,也就不用打印,拿这个作为结束条件再好不过
void DLPrint(DLNode* phead)
{
	DLNode* pcur = phead->next;//头节点指向下一个节点,也就是第一个有效节点
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;//遍历指向下一个节点
	}
	printf("\n");
}

2.4. 头插

  1. 尾插:在头节点的前面尾插,看上面的图newnode的位置意思是一样的
  2. 头插:在头节点的后面头插
  3. 方法的先后顺序是一定不能改变的
void DLPushFront(DLNode* phead,DLDataType x)
{
	assert(phead);//头节点不能为NULL
	DLNode* newnode = DLBuyNode(x);
	newnode->next = phead->next;
	newnode->prev = phead;

	//方法1
	//phead->next->prev = newnode;//第一个有效节点指向newnode
	//phead->next = newnode;//指向下一个节点

	//方法2
	phead->next = newnode;
	newnode->next->prev = newnode;
}
  1. 注意:要搞清楚头插和尾插时候的前后顺序

2.5. 尾删

  1. 如果不保存del节点会导致内存泄露
  2. 尾删和头删可以发现,不用在乎谁先指向谁
  3. 只要不满足phead为非NULL 和 phead->next != phead就会报错
void DLDelBack(DLNode* phead)
{
	//当phead->next != phead就正常执行
	assert(phead && phead->next != phead);//链表都为NULL你删什么?,如果是只剩一个头结点当然也不行
	DLNode* del = phead->prev;
	del->prev->next = phead;//删除的前一个节点指向phead
	phead->prev = del->prev;//头结点的前一个节点指向del->prev
	free(del);
	del = NULL;
}

2.6. 头删

  1. 头删很简单,就是先保存del节点
  2. 让phead->next指向del->next ,然后让del->next->prev= phead;
void DLDelFront(DLNode* phead)
{
	assert(phead && phead->next != phead);//如果保错就说明,要么为NULL,要么只有一个头结点
	DLNode* del = phead->next;//第一个有效节点
	phead->next = del->next;
	del->next->prev = phead;
	free(del);
	del = NULL;
}

2.7. 查找

  1. 查找很简单啦,这就不讲了,唯一和单链表不同的是结束条件,pcur != phead,当遍历到头节点的时候就结束
//查找
DLNode* DLFind(DLNode* phead, DLDataType x)
{
	assert(phead);
	DLNode* pcur = phead->next;//可能存在多次查找,其次记得是第一个有效节点
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
//test.c部分
	DLNode* find = DLFind(plist, 22);
	if (find == NULL)
		printf("没找到");
	else
		printf("找到了");

2.8. 在指定位置之前插入

  1. 两种情况

//在指定位置插入
void DLInsert(DLNode* pos, DLDataType x)
{
	assert(pos);//防止phead == NULL链表
	DLNode* newnode = DLBuyNode(x);
	//pos newnode pos->next
    newnode->next = pos->next;
	newnode->prev = pos;
    // 2,3点
	pos->next->prev = newnode;
	pos->next = newnode;
}

2.9. pos位置删除 

  1. pos位置删除,pos左右两边的节点手拉手就行

//在指定位置删除
void DLErase(DLNode* pos)
{
	assert(pos);//可能会缺少phead的检查
	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);//pos置为NULL并不影响find
	pos = NULL;
}

3. 初始化的方式

  1. 为了保证接口的一致性
  2. 假如说这些方法,这个方法要传一级指针,那个方法要穿二级指针,总是不能统一,在后续开发中不会大大增加你的开发难度吗?
//新空间
DLNode* DLBuyNode(DLDataType x)
{
	DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
	if (newnode == NULL)
	{
		perror("malloc nwe");
		exit(-1);//推出整个程序
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;//申请新节点,需要自循环
	return newnode;
}
//第一种,双链表的初始化
//void DLInit(DLNode** pphead)//只传参不返回值,因为可以对实参直接改变
//{
//	*pphead = DLBuyNode(-1);
//}

//第二种初始化方法为了保证接口的一致性,让传递的值都是一级指针
DLNode* DLInit()
{
	DLNode* node = DLBuyNode(-1);
	return node;
}
void DListTest1()
{
    //第一种
    //DLNode* plist = NULL;//哨兵位
	//DLInit(&plist);//对头节点改变需要二级指针

	//第二种初始化方式
	DLNode* plist = DLInit();
}

4. 链表的销毁

  1. 形参是一级指针,传参的时候也是一级指针,这是传值调用了。但是free这个函数只管释放掉动态开辟的空间
  2. 假如用第一点的方式free掉头节点,并置为NULL,会导致实参这边指针变量没有置为NULL,为什么呢?
  3. 因为形参的改变没有影响实参(这个传的是一级指针),而有了两个变量名,虽然空间释放了,但是实参没受影响,所以也要置为NULL。
  4. 为什么不传二级指针直接改变实参头指针的指向呢,因为要保持接口的一致性
//销毁链表
void DLDesTroy(DLNode* phead)
{
	assert(phead);//此时链表可以为NULL
	DLNode* pcur = phead->next;
	while (pcur != phead)
	{
		DLNode* node = pcur->next;//保存后一个节点
		free(pcur);
		pcur = node;//把后一个节点给到pcur
	}
	free(phead);
	phead = NULL;//两个名字都有访问这块空间的权限,但是这个函数里的名字,并不会影响传过来之前的变量名
}
  1. free,原本意思对动态开辟的内存进行释放,函数内和实参这边的函数都有访问权限
  2. 可以理解为,两个地方都可以对这个块空间释放,但是有两个变量名所以,实参函数这边的指针就变成了野指针
  3. 而且是传值调用,如果想对实参函数这边的指针改变就需要二级指针了,而free只管free掉动态开辟的空间
//test.c
void DListTest1()
{
	DLNode* plist = NULL;//哨兵位
	DLInit(&plist);//对头节点改变需要二级指针

	//测试尾插
	DLPushBack(plist, 1);
	DLPushBack(plist, 2);
	DLPushBack(plist, 3);

	//DLPushFront(plist, 11);
	//DLPushFront(plist, 22);
	//DLPushFront(plist, 33);
	DLPrint(plist);
	//链表销毁
	DLDesTroy(plist);//销毁完后是需要置为NULL的
	plist = NULL;
}
  1. 当然也是可以到实参这边free掉函数的,可以看下边两个例子
  1. 再举个例子
  2. 这边我们创建一个单独销毁的函数叫des,如果传二级指针是可以直接对plist这个头节点释放并且置为NULL的,确确实实可以影响到plist这个实参
  3. 下面是函数调试图,可以发现函数调用传的二级指针,拿到了plist的地址确确实实对plist实参进行了空间的释放

void des(DLNode** del)
{
	free(*del);//拿到了plist的地址,对plist的指向改变
	*del = NULL;
}
void DListTest1()
{
	DLNode* plist = NULL;//哨兵位
	DLInit(&plist);//对头节点改变需要二级指针
	//测试尾插
	DLPushBack(plist, 1);
	DLPushBack(plist, 2);
	DLPushBack(plist, 3);
	DLPrint(plist);
	//链表销毁
	DLDesTroy(plist);
	des(&plist);//这边我们创建一个单独销毁的函数
}
int main()
{
	DListTest1();
	return 0;
}

5.源码

5.1调试部分test.c

#include "DList.h"
void DListTest1()
{
	//DLNode* plist = NULL;//哨兵位
	//DLInit(&plist);//对头节点改变需要二级指针

	//第二种初始化方式
	DLNode* plist = DLInit();

	测试尾插
	//DLPushBack(plist, 1);
	//DLPushBack(plist, 2);
	//DLPushBack(plist, 3);
	//测试头插
	DLPushFront(plist, 11);
	DLPrint(plist);
	DLPushFront(plist, 22);
	DLPrint(plist); 
	DLPushFront(plist, 33);
	DLPrint(plist);

	//测试尾删
	//DLDelBack(plist);
	//DLPrint(plist);
	//DLDelBack(plist);
	//DLPrint(plist);
	//DLDelBack(plist);
	//DLPrint(plist);
	//DLDelBack(plist);
	//DLPrint(plist);

	//测试头删
	//DLDelFront(plist);
	//DLPrint(plist);
	//DLDelFront(plist);
	//DLPrint(plist);
	//DLDelFront(plist);
	//DLPrint(plist);
	DLNode* find = DLFind(plist, 11);// 33 22 11
	if (find == NULL)
		printf("没找到\n");
	else
		printf("找到了\n");
	
	//在pos节点后插入
	DLInsert(find, 99);// 33 22 11 99
	DLPrint(plist);

	//删除pos节点
	DLErase(find);
	find = NULL;
	DLPrint(plist);
}
int main()
{
	DListTest1();
	return 0;
}   

5.2方法实现 DList.c

#include "DList.h"
//新空间
DLNode* DLBuyNode(DLDataType x)
{
	DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
	if (newnode == NULL)
	{
		perror("malloc nwe");
		exit(-1);//推出整个程序
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;//申请新节点,需要自循环
	return newnode;
}

//双链表的初始化
//void DLInit(DLNode** pphead)//只传参不返回值,因为可以对实参直接改变
//{
//	*pphead = DLBuyNode(-1);
//}

//第二种初始化方法为了保证接口的一致性,让传递的值都是一级指针
DLNode* DLInit()
{
	DLNode* node = DLBuyNode(-1);
	return node;
}

//打印链表
void DLPrint(DLNode* phead)
{
	DLNode* pcur = phead->next;//头节点指向下一个节点,也就是第一个有效节点
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

//尾插
void DLPushBack(DLNode* phead, DLDataType x)
{
	assert(phead);
	DLNode* newnode = DLBuyNode(x);
	//phead phead->prev newnode
	newnode->prev = phead->prev;//phead->prev 是头节点指向的上一个节点也就是尾节点
	newnode->next = phead;


	phead->prev->next = newnode;//尾节点指向下一个节点
	phead->prev = newnode;
}

//头插
void DLPushFront(DLNode* phead, DLDataType x)
{
	assert(phead);//头节点不能为NULL
	DLNode* newnode = DLBuyNode(x);
	newnode->next = phead->next;
	newnode->prev = phead;

	//方法1
	//phead->next->prev = newnode;//第一个有效节点指向newnode
	//phead->next = newnode;//指向下一个节点

	//方法2
	phead->next = newnode;
	newnode->next->prev = newnode;
}

//尾删
void DLDelBack(DLNode* phead)
{
	//当phead->next != phead就正常执行
	assert(phead && phead->next != phead);//链表都为NULL你删什么?,如果是只剩一个头结点当然也不行
	DLNode* del = phead->prev;
	del->prev->next = phead;//删除的前一个节点指向phead
	phead->prev = del->prev;//头结点的前一个节点指向del->prev
	free(del);
	del = NULL;
}

//头删
void DLDelFront(DLNode* phead)
{
	assert(phead && phead->next != phead);//如果保错就说明,要么为NULL,要么只有一个头结点
	DLNode* del = phead->next;//第一个有效节点
	phead->next = del->next;
	del->next->prev = phead;
	free(del);
	del = NULL;
}

//查找
DLNode* DLFind(DLNode* phead, DLDataType x)
{
	assert(phead);
	DLNode* pcur = phead->next;//可能存在多次查找,其次记得是第一个有效节点
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

//在指定位置插入
void DLInsert(DLNode* pos, DLDataType x)
{
	assert(pos);//防止phead == NULL链表
	DLNode* newnode = DLBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	newnode->prev = pos;

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

//在指定位置删除
void DLErase(DLNode* pos)
{
	assert(pos);//可能会缺少phead的检查
	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);//pos置为NULL并不影响find
	pos = NULL;
}

5.3声明函数和类型的部分 DList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int DLDataType;
typedef struct ListNode
{
	DLDataType data;
	struct ListNode* prev;//指向前一个节点
	struct ListNode* next;//指向下一个节点
}DLNode;//链表节点

//双链表初始化
//void DLInit(DLNode** pphead);

//第二种初始化方式
DLNode* DLInit();

//尾插
void DLPushBack(DLNode* phead, DLDataType x);

//头插
void DLPushFront(DLNode* phead, DLDataType x);

//尾删
void DLDelBack(DLNode* phead);

//头删
void DLDelFront(DLNode* phead);

//查找
DLNode* DLFind(DLNode* phead, DLDataType x);

//在指定位置插入
void DLInsert(DLNode* pos, DLDataType x);

//指定位置删除
void DLErase(DLNode* pos);

总结:

这里只算是基础部分,当然最主要的是学会如何使用手里的工具

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

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

相关文章

Rust-01 Hello Rust 10分钟上手编写第一个Rust程序 背景介绍 发展历史 环境配置 升级打怪的必经之路

背景介绍 Rust 是一种多范式、通用的编程语言&#xff0c;强调性能、类型安全和并发性。它通过一个称为“借用检查器”的机制在编译时追踪所有引用的对象生命周期&#xff0c;以强制实现内存安全&#xff0c;即确保所有引用都指向有效的内存&#xff0c;而不需要垃圾收集器。 …

浏览器工作原理与实践--性能分析工具:如何分析Performance中的Main指标

节我们介绍了如何使用Performance&#xff0c;而且我们还提到了性能指标面板中的Main指标&#xff0c;它详细地记录了渲染主线程上的任务执行记录&#xff0c;通过分析Main指标&#xff0c;我们就能够定位到页面中所存在的性能问题&#xff0c;本节&#xff0c;我们就来介绍如何…

如何修改支付宝号?日赚300+,纯撸信息差!

最近更新的内容中&#xff0c;很多都是给大家讲到的“信息差”。但是&#xff0c;真正能理解信息差&#xff0c;并且使用信息差赚钱的&#xff0c;有多少&#xff1f; 包括前几天给朋友们分享的软件项目&#xff0c;靠信息差月入3万&#xff0c;直接复制粘贴搞定&#xff01;和…

java可盈保险合同管理系统的设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的可盈保险合同管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Spring Boot的…

在【laravel框架】学习中遇到的常见的问题以及解决方法

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

net模块

建立TCP的链接 1 发送消息的服务 2 接收消息 2 建立http的链接让浏览器进行访问 import net from netconst html <h1>TCP</h1>const respinseHeaders [HTTP/1.1 200 OK,Content-Type:text/html,Content-Length: html.length,\r\n,html]const http net.create…

计算机系列之大话原码、补码、反码、移码

5、大话原码、补码、反码、移码 原码 最高位 低位&#xff08;7位二进制数&#xff09;&#xff0c; 最高位 0 表示正数&#xff0c;1表示负数 低位即该数字的二进制数 7 的原码 00000111&#xff0c; -7 的原码 10000111&#xff0c; 0000111 为 7 的二进制数&#xff…

VulnHub靶机 DC-5 打靶 渗透测试详情过程

VulnHub靶机 DC-5 打靶 详细渗透测试过程 目录 VulnHub靶机 DC-5 打靶 详细渗透测试过程一、将靶机导入到虚拟机当中二、渗透流程主机发现端口扫描目录爆破文件包含getshell反弹shell提权 一、将靶机导入到虚拟机当中 靶机地址&#xff1a; https://download.vulnhub.com/dc/…

Linux之C编程入门

目录 第1关&#xff1a;第一个C程序 任务描述 相关知识 编译C程序 编程要求 答案及其步骤&#xff1a; 第2关&#xff1a;Linux编译C程序 任务描述 相关知识 gcc编译器使用方法 编程要求 答案及其步骤&#xff1a; 第3关&#xff1a;Linux之静态库编写 任务描述 相关知识 生成…

【听劝】别盲目备考NPDP,思维导图助你高效学习

还在为如何高效学习NPDP而苦恼吗&#xff1f; 今天给大家分享NPDP认证考试必备的学习资料&#xff1a;思维导图。 &#xff08;内容来自圣略NPDP资深讲师整理&#xff09; 详细梳理了课本内容&#xff0c;保存到手机学习&#xff0c;非常方便&#xff01; 思维导图会陆续更…

web自动化系列-selenium的3种弹框操作(十二)

在进行功能测试时 &#xff0c;经常会遇到出现各种的弹出的提示 &#xff0c;比如删除数据给出提示 、做某个操作时也会弹框给出一些友好提示 &#xff0c;因为这些弹框都是做web操作时的一些常用组件 &#xff0c;所以&#xff0c;selenium就不得不支持这些组件 。 1.弹框介绍…

Python基础04-操作系统中的文件与目录操作

在与操作系统交互时&#xff0c;我们经常需要执行文件和目录的操作。Python提供了丰富的库来帮助我们完成这些任务。以下是一些常见的操作&#xff0c;以及如何使用Python代码来实现它们。 1. 导航文件路径 在不同的操作系统中&#xff0c;文件路径的格式可能不同。Python的o…

WAF攻防-漏洞发现协议代理池GobyAwvsXray

知识点 1、Http/s&Sock5协议 2、Awvs&Xray&Goby代理 3、Proxifier进程代理使用 4、Safedog&BT&Aliyun防护在漏洞发现中&#xff0c;WAF会对三个方向进行过滤拦截&#xff1a; 1、速度频率问题&#xff08;代理池解决&#xff09; 2、工具的指纹被识别&am…

【Leetcode笔记】236.二叉树的最近公共祖先

文章目录 题目要求ACM运行结果 题目要求 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能…

Zabbix监控Oracle归档日志空间

1、oracle查看归档日志空间的sql语句 select sum(PERCENT_SPACE_USED) from v$recovery_area_usage; 2、交互式查看oracle归档日志空间的命令&#xff0c;可以手动执行一下&#xff0c;注意要用oracle用户 sqlplus -S "/ as sysdba" << EOF select sum(PER…

python使用uiautomator2操作真机(vivo Y30)

环境&#xff1a; python3.8.10&#xff0c;vivo Y30(8G,128g)&#xff0c;版本android 10。 之前写过&#xff1a; python使用uiautomator2操作真机&#xff08;荣耀10青春版&#xff09;_python真机操作-CSDN博客 python使用uiautomator2操作真机&#xff08;oppo a9x&a…

CompletableFuture编排异步线程

CompletableFuture 是 Java 8 引入的一种新的 Future&#xff0c;设计目的是为了编写非阻塞的异步代码。 传统异步编程方式 传统异步编程方式获得异步任务值&#xff0c;首先我们得通过future task &#xff0c;然后创建一个实现callable内部类&#xff0c;或者通过lambda的表…

JAVA学习笔记31(IO流)

1.IO流 1.文件流 ​ *文件在程序中是以流的形式来操作的 2.常用文件操作 1.创建文件对象 1.new File(String pathname) //根据路径构建一个File对象 main() {}public void create01() {String filePath "e:\\news1.txt";File filePath new File(filePath);tr…

人人都会给视频换脸_出色的AI换脸软件离线版你可以把视频换上明星脸

网盘下载 简单几步骤: 1、找个人脸照片&#xff0c;正面高清 2、找个视频&#xff0c;最好是单人的视频&#xff0c;或者只有一个女的&#xff0c;这样可以按照条件换脸 3、点击开始&#xff0c;等待完成即可&#xff08;显卡勾选显卡&#xff0c;显卡不行选择CPU&#xff09;…

最新win11配置cuda以及cudnn补丁教程

1、首先使用指令 nvidia-smi 查看电脑支持的**最高cuda**版本&#xff0c;例如&#xff1a;本机 12.2 2、进入CUDA下载cuda安装包 https://developer.nvidia.com/cuda-toolkit-archive 2、点击上方绿色的链接&#xff0c;按照图中序号选择的即可&#xff0c;最后点击下载。 …