数据结构与算法 - 堆

1. 建堆

Floyd建堆算法作者(也是之前龟兔赛跑判环作者):

  • ①找到最后一个非叶子节点
  • ②从后往前,对每个节点执行下潜

一些规律

  • 一棵满二叉树节点个数为2^h - 1,如下例中高度h = 3节点数是2 ^ 3 - 1 = 7
  • 非叶子节点范围为[0, size/2 - 1]

算法时间复杂度分析

下面看交换次数的推导:设节点高度为3

本层节点数高度下潜最多交换次数(高度-1)
4567 这层410
23这层221
1这层132

每一层的交换次数为:节点个数 * 此节点交换次数,总的交换次数为

推导出 2^h - h - 1,其中2^h ≈ n,h ≈ log_2(n),因此时间复杂度为O(n)

算法描述:

  • heapify建立大根堆
  • 将堆顶与堆底交换(最大元素被交换到堆底),缩小并下潜调整堆
  • 重复第二步直至堆里剩一个元素

 大根堆:

package com.itheima.datastructure.Heap;import java.util.Arrays;/*** 大顶堆*/
public class MaxHeap {int[] array;int size;public MaxHeap(int capacity) {this.array = new int[capacity];}public boolean isEmpty() {return size == 0;}public boolean isFull() {return size == array.length;}/*** 获取堆顶元素** @return 堆顶元素*/public int peek() {if(isEmpty()) {return -1;}return array[0];}/*** 删除堆顶元素** @return 堆顶元素*/public int poll() {if(isEmpty()) {return -1;}int top = array[0];swap(0, size - 1);size--;down(0);return top;}/*** 删除指定索引处元素** @param index 索引* @return 被删除元素*/public int poll(int index) {if(isEmpty()) {return -1;}int deleted = array[index];up(Integer.MAX_VALUE, index);poll();return deleted;}/*** 替换堆顶元素** @param replaced 新元素*/public void replace(int replaced) {array[0] = replaced;down(0);}/*** 堆的尾部添加元素** @param offered 新元素* @return 是否添加成功*/public boolean offer(int offered) {if(isFull()) {return false;}up(offered, size);size++;return true;}// 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶private void up(int offered, int index) {int child = index;while (child > 0) {int parent = (child - 1) / 2;if (offered > array[parent]) {array[child] = array[parent];} else {break;}child = parent;}array[child] = offered;}public MaxHeap(int[] array) {this.array = array;this.size = array.length;heapify();}// 建堆private void heapify() {// 如何找到最后这个非叶子节点  size / 2 - 1for (int i = size / 2 - 1; i >= 0; i--) {down(i);}}// 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大private void down(int parent) {int left = parent * 2 + 1;int right = left + 1;int max = parent;if (left < size && array[left] > array[max]) {max = left;}if (right < size && array[right] > array[max]) {max = right;}if (max != parent) { // 找到了更大的孩子swap(max, parent);down(max);}}// 交换两个索引处的元素private void swap(int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}public static void main(String[] args) {int[] array = {2, 3, 1, 7, 6, 4, 5};MaxHeap heap = new MaxHeap(array);System.out.println(Arrays.toString(heap.array));while (heap.size > 1) {heap.swap(0, heap.size - 1);heap.size--;heap.down(0);}System.out.println(Arrays.toString(heap.array));}
}

小根堆:

package com.itheima.datastructure.heap;public class MinHeap {int[] array;int size;public MinHeap(int capacity) {this.array = new int[capacity];}public boolean isFull() {return size == array.length;}/*** 获取堆顶元素** @return 堆顶元素*/public int peek() {return array[0];}/*** 删除堆顶元素** @return 堆顶元素*/public int poll() {int top = array[0];swap(0, size - 1);size--;down(0);return top;}/*** 删除指定索引处元素** @param index 索引* @return 被删除元素*/public int poll(int index) {int deleted = array[index];up(Integer.MIN_VALUE, index);poll();return deleted;}/*** 替换堆顶元素** @param replaced 新元素*/public void replace(int replaced) {array[0] = replaced;down(0);}/*** 堆的尾部添加元素** @param offered 新元素* @return 是否添加成功*/public boolean offer(int offered) {if (size == array.length) {return false;}up(offered, size);size++;return true;}// 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶private void up(int offered, int index) {int child = index;while (child > 0) {int parent = (child - 1) / 2;if (offered < array[parent]) {array[child] = array[parent];} else {break;}child = parent;}array[child] = offered;}public MinHeap(int[] array) {this.array = array;this.size = array.length;heapify();}// 建堆private void heapify() {// 如何找到最后这个非叶子节点  size / 2 - 1for (int i = size / 2 - 1; i >= 0; i--) {down(i);}}// 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大private void down(int parent) {int left = parent * 2 + 1;int right = left + 1;int min = parent;if (left < size && array[left] < array[min]) {min = left;}if (right < size && array[right] < array[min]) {min = right;}if (min != parent) { // 找到了更大的孩子swap(min, parent);down(min);}}// 交换两个索引处的元素private void swap(int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}
}

2. 习题

2.1 堆排序

package com.itheima.datastructure.heap;import java.util.Arrays;public class E01HeapSort {public static void sort(int[] array) {// 1. 建堆int size = array.length;heapify(array, size);while (size > 1) {// 2. 将堆顶元素交换至堆底swap(array, 0, size - 1);// 堆大小减一size--;// 重新调整堆down(array, 0, size);}}// 建堆private static void heapify(int[] array, int size) {for (int i = size / 2 - 1; i >= 0; i--) {down(array, i, size);}}// 下潜private static void down(int[] array, int parent, int size) {int left = parent * 2 + 1;int right = left + 1;int max = parent;if (left < size && array[left] > array[max]) {max = left;}if (right < size && array[right] > array[max]) {max = right;}if (max != parent) { // 找到了更大的孩子swap(array, max, parent);down(array, max, size);}}// 交换private static void swap(int[] array, int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}public static void main(String[] args) {int[] array = {2, 3, 1, 7, 6, 4, 5};sort(array);System.out.println(Arrays.toString(array));}
}

2.2 数组中第K大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4

解法一:小根堆

class Solution {static class MinHeap {int[] array;int size;public MinHeap(int capacity) {array = new int[capacity];}public int peek() {return array[0];}public boolean offer(int offered) {if (size == array.length) {return false;}up(offered);size++;return true;}public void replace(int replaced) {array[0] = replaced;down(0);}private void up(int offered) {int child = size;while (child > 0) {int parent = (child - 1) >> 1;if (offered < array[parent]) {array[child] = array[parent];} else {break;}child = parent;}array[child] = offered;}private void down(int parent) {int left = (parent << 1) + 1;int right = left + 1;int min = parent;if (left < size && array[left] < array[min]) {min = left;}if (right < size && array[right] < array[min]) {min = right;}if (min != parent) {swap(min, parent);down(min);}}// 交换两个索引处的元素private void swap(int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}}public int findKthLargest(int[] nums, int k) {MinHeap heap = new MinHeap(k);// 先将k个元素入堆for (int i = 0; i < k; i++) {heap.offer(nums[i]);}// 将剩余的元素与堆顶元素比较,如果比堆顶元素大则替换。比较完成后,堆顶元素即为第k大的元素(小根堆)for (int i = k; i < nums.length; i++) {if (nums[i] > heap.peek()) {heap.replace(nums[i]);}}return heap.peek();}
}

解法二:优先级队列

class Solution {public int findKthLargest(int[] nums, int k) {PriorityQueue<Integer> minHeap = new PriorityQueue<>();  // 默认底层实现是小根堆for (int num : nums) {minHeap.offer(num);if(minHeap.size() > k) {minHeap.poll();}}return minHeap.peek();}
}

2.3 数据流的中的第K大元素

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

请实现 KthLargest 类:

  • KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
  • int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

示例:

输入:
["KthLargest", "add", "add", "add", "add", "add"]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3);   // return 4
kthLargest.add(5);   // return 5
kthLargest.add(10);  // return 5
kthLargest.add(9);   // return 8
kthLargest.add(4);   // return 8

提示:

  • 1 <= k <= 10^4
  • 0 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • -10^4 <= val <= 10^4
  • 最多调用 add 方法 10^4 次
  • 题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素

解法一:小根堆

class KthLargest {static class MinHeap {int[] array;int size;public MinHeap(int capacity) {array = new int[capacity];}public boolean isFull() {return size == array.length;}public int peek() {return array[0];}public boolean offer(int offered) {if (size == array.length) {return false;}up(offered);size++;return true;}public void replace(int replaced) {array[0] = replaced;down(0);}private void up(int offered) {int child = size;while (child > 0) {int parent = (child - 1) >> 1;if (offered < array[parent]) {array[child] = array[parent];} else {break;}child = parent;}array[child] = offered;}private void down(int parent) {int left = (parent << 1) + 1;int right = left + 1;int min = parent;if (left < size && array[left] < array[min]) {min = left;}if (right < size && array[right] < array[min]) {min = right;}if (min != parent) {swap(min, parent);down(min);}}// 交换两个索引处的元素private void swap(int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}}private MinHeap heap;public KthLargest(int k, int[] nums) {heap = new MinHeap(k);for(int i = 0; i < nums.length; i++) {add(nums[i]);}}public int add(int val) {if(!heap.isFull()) {heap.offer(val);} else if(val > heap.peek()) {heap.replace(val);}return heap.peek();}
}/*** Your KthLargest object will be instantiated and called as such:* KthLargest obj = new KthLargest(k, nums);* int param_1 = obj.add(val);*/

2.4 数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3 。
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。

  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -10^5 <= num <= 10^5
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 10^4 次调用 addNum 和 findMedian

解法一:大根堆 + 小根堆

解题思路:为了保持两边数据量的平衡:

  • 两边个数一样时,左边个数加一;两边个数不一样时,右边个数加一;
  • 左边个数加一时,应该先将新增元素加入右边,再挑 右边最小元素 加入左边;
  • 右边个数加一时,应该先将新增元素加入左边,再挑 左边最大元素 加入右边;
class MedianFinder {public static class Heap {int[] array;int size;boolean max;public int size() {return size;}public Heap(int capacity, boolean max) {this.array = new int[capacity];this.max = max;}/*** 获取堆顶元素** @return 堆顶元素*/public int peek() {return array[0];}/*** 删除堆顶元素** @return 堆顶元素*/public int poll() {int top = array[0];swap(0, size - 1);size--;down(0);return top;}/*** 删除指定索引处元素** @param index 索引* @return 被删除元素*/public int poll(int index) {int deleted = array[index];swap(index, size - 1);size--;down(index);return deleted;}/*** 替换堆顶元素** @param replaced 新元素*/public void replace(int replaced) {array[0] = replaced;down(0);}/*** 堆的尾部添加元素** @param offered 新元素*/public void offer(int offered) {if (size == array.length) {grow();}up(offered);size++;}/*** 扩容*/private void grow() {int capacity = size + (size >> 1);int[] newArray = new int[capacity];System.arraycopy(array, 0, newArray, 0, size);array = newArray;}// 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶private void up(int offered) {int child = size;while (child > 0) {int parent = (child - 1) / 2;boolean cmp = max ? offered > array[parent] : offered < array[parent];if (cmp) {array[child] = array[parent];} else {break;}child = parent;}array[child] = offered;}public Heap(int[] array, boolean max) {this.array = array;this.size = array.length;this.max = max;heapify();}// 建堆private void heapify() {// 如何找到最后这个非叶子节点 size / 2 - 1for (int i = size / 2 - 1; i >= 0; i--) {down(i);}}// 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大private void down(int parent) {int left = parent * 2 + 1;int right = left + 1;int min = parent;if (left < size && (max ? array[left] > array[min] : array[left] < array[min])) {min = left;}if (right < size && (max ? array[right] > array[min] : array[right] < array[min])) {min = right;}if (min != parent) { // 找到了更大的孩子swap(min, parent);down(min);}}// 交换两个索引处的元素private void swap(int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}}private Heap left = new Heap(10, false);  // 大根堆private Heap right = new Heap(10, true);  // 小根堆public MedianFinder() {}public void addNum(int num) {// 两边个数一样时,左边个数加一。 左边个数加一时,应该先将新增元素加入右边,再挑 右边最小元素 加入左边if(left.size() == right.size()) {  right.offer(num);left.offer(right.poll());} else {  // 两边个数不一样时,右边个数加一。 右边个数加一时,应该先将新增元素加入左边,再挑 左边最大元素 加入右边left.offer(num);right.offer(left.poll());}}public double findMedian() {if(left.size() == right.size()) {return (left.peek() + right.peek()) / 2.0;} else {  // 不等的情况下,是左边多一个元素return left.peek();}}
}/*** Your MedianFinder object will be instantiated and called as such:* MedianFinder obj = new MedianFinder();* obj.addNum(num);* double param_2 = obj.findMedian();*/

解法二:优先队列

package com.itheima.datastructure.Heap;import java.util.PriorityQueue;public class MedianFinder {PriorityQueue<Integer> left = new PriorityQueue<>((a, b) -> b - a);PriorityQueue<Integer> right = new PriorityQueue<>();public MedianFinder() {}public void addNum(int num) {// 两边个数一样时,左边个数加一。 左边个数加一时,应该先将新增元素加入右边,再挑 右边最小元素 加入左边if(left.size() == right.size()) {right.offer(num);left.offer(right.poll());} else {// 两边个数不一样时,右边个数加一。 右边个数加一时,应该先将新增元素加入左边,再挑 左边最大元素 加入右边left.offer(num);right.offer(left.poll());}}public double findMedian() {if(left.size() == right.size()) {return (left.peek() + right.peek()) / 2.0;} else {return left.peek();}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PMP--冲刺--敏捷中的角色职责与3个工件--题干关键词

文章目录 敏 捷 中 的 角 色 职 责 与 3 个 工 件--题干关键词说明题目 敏 捷 中 的 角 色 职 责 与 3 个 工 件–题干关键词 角色职责 1、产品负责人&#xff1a;题干关键词 “优先级排序、与客户沟通、下次迭代做什么、接受或拒绝用户故事”。 2、Scrum Master&#xff1a;题…

uniapp自定义网格布局用于选择金额、输入框焦点事件以及点击逻辑实战

样式 <view class="withdraw-section"><text class="section-title">提现金额</text><view class="amount-options"><view v-for="(item, index) in list" :key="index" class="amount-opt…

学习Java的日子 Day59 学生管理系统 web1.0版本

Day59 学生管理系统 web1.0 1.项目需求 有两个角色&#xff0c;老师和学生&#xff0c;相同的功能提取到父类用户角色 2.数据库搭建 设计学生表 设计老师表 插入数据 (超级管理员) 设计学科表 3.项目搭建 处理基础页面&#xff0c;分包&#xff0c;实体类&#xff0c;导入数据…

【初学人工智能原理】【12】循环:序列依赖问题

前言 本文教程均来自b站【小白也能听懂的人工智能原理】&#xff0c;感兴趣的可自行到b站观看。 代码及工具箱 本专栏的代码和工具函数已经上传到GitHub&#xff1a;1571859588/xiaobai_AI: 零基础入门人工智能 (github.com)&#xff0c;可以找到对应课程的代码 正文 对于…

LabVIEW水下根石监测系统

开发了一种基于LabVIEW平台开发的水下根石监测系统。该系统利用高精度姿态传感器与位移传感器&#xff0c;实现了水下根石状态的实时自动监测&#xff0c;提高了水利工程安全管理的现代化和精细化水平&#xff0c;具有高精度、高稳定性和良好的操作性。 项目背景&#xff1a; …

canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢?

实现如标题所示的这些文本效果&#xff0c;在css看来&#xff0c;不就是一两行css属性。然而&#xff0c;对于canvas来讲&#xff0c;要想呈现这样的文字样式&#xff0c;就没css那么轻松简便了。 既然如此&#xff0c;那为何还要使用对文本支持度不友好的canvas来绘制文字呢&a…

【开发视角】大模型 RAG 检索增强生成究竟是什么

【大白话讲懂】大模型 RAG 检索增强生成 话先说在前面&#xff0c;本文不讲不会讲太多原理&#xff0c;仅面向工程开发&#xff0c;从工作流程的宏观角度进行梳理&#xff0c;旨在快速上手。 RAG 是什么 基本定义 让我们先来解释名词&#xff0c;看看宏观框架。 RAG 的意思…

科普文:微服务之SpringBoot性能优化器动态线程池【Dynamic-Tp】特性和源码解读

一、简述 gitee地址&#xff1a;https://gitee.com/yanhom/dynamic-tp github地址&#xff1a;https://github.com/lyh200/dynamic-tp dynamic-tp是一个轻量级的动态线程池插件&#xff0c;它是一个基于配置中心的动态线程池&#xff0c;线程池的参数可以通过配置中心配置进…

C++中lambda使用mutable关键字详解

C中lambda使用mutable关键字详解 在《C初学者指南-5.标准库(第二部分)–更改元素算法》中&#xff0c;讲“generate”算法时有下面这段代码&#xff1a; auto gen [i0]() mutable { i 2; return i; }; std::vector<int> v; v.resize(7,0); generate(begin(v)1, begin…

C++ STL在算法题中的常用语法

Vector 1.将vector<int>中的元素全部置换为0 fill(vec.begin(), vec.end(), 0); 2.vector容器是可以直接用比较是否值等的&#xff01; Unordered_set 1. unordered_set的删除&#xff08;count的值也会减少&#xff09; 2.unordered_map中的int默认值是0&#xff0c;…

在Jira中使用AI

Jira已经可以使用AI功能了。 如果您使用的是Jira Cloud&#xff0c;您需要请管理员在管理页面中打开AI功能开关。&#xff08;AI功能在Standard版中未提供&#xff0c;请使用Premium或更高级的版本&#xff09;如果您使用的是自己部署的Jira Data Center&#xff0c;您需要请管…

算法学习day28

一、寻找右区间(二分法) 题意&#xff1a;题目很容易理解 但是转换为二分法有点晦涩 给你一个区间数组 intervals &#xff0c;其中 intervals[i] [starti, endi] &#xff0c;且每个 starti 都 不同 。区间 i 的 右侧区间 可以记作区间 j &#xff0c;并满足 startj > e…

OpenCV||超详细的灰度变换和直方图修正

一、点运算 概念&#xff1a;点运算&#xff08;也称为像素级运算或单像素操作&#xff09;是指对图像中每一个像素点进行独立、相同的操作&#xff0c;而这些操作不会考虑像素点之间的空间关系。点处理优势也称对比度拉伸、对比度增强或灰度变换等。 目的&#xff1a;点运算…

【EtherCAT】Windows+Visual Studio配置SOEM主站——静态库配置+部署

目录 一、准备工作 1. Visual Studio 2022 2. Npcap 1.79 3. SOEM源码 二、静态库配置 1. 修改SOEM源码配置 2. 编译SOEM源码 3. 测试 三、静态库部署 1. 新建Visual Studio工程 2. 创建文件夹 3. 创建主函数 4. 复制静态库 5. 复制头文件 6. 配置头文件…

链接、装载和库——1 简介

前言 关于个人的读书笔记 第一章 温故而知新 1.1 从hello&#xff0c;world说起 ​计算机在执行hello&#xff0c;world的时候发生了什么&#xff1f; 1.2 万变不离其宗 ​在计算机多如牛毛的硬件设备中。有三个部件最为关键&#xff0c;它们分别是 CPU、内存和 I/O 控制芯…

一次多波束和浅地层处理的经历—信标机出问题?

最近处理多波束和浅地层时&#xff0c;一个从来没有过的问题出现了。 多波束数据(.pds)是由PDS2000采集的&#xff0c;使用设备型号为T50P。浅地层数据(.raw)是有SESWIN采集的&#xff0c;使用设备型号为SES2000 Standard。 1、多波束处理 多波束数据采用CARIS11.3处理的。船…

开源LivePortrait,快速实现表情包自定义

最近可灵AI很火&#xff0c;看到网上生成的效果也很赞啊&#xff0c;之前发现快手可灵开源了LivePortrait&#xff0c;今天去玩了一下&#xff0c;很有意思。 比如下图官方展示效果&#xff1a; 这些图片开始自带表情了&#xff0c;主要就是通过LivePortrait来实现。 LivePor…

[E二叉树] lc572. 另一棵树的子树(dfs+前中序判断+树哈希+树上KMP+好题)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接&#xff1a;572. 另一棵树的子树 2. 题目解析 看到这个题目就感觉不简单&#xff0c;因为写了写 dfs 版本的&#xff0c;发现好像不太会… 还是简单粗暴一点&#xff0c;直接搞一个 前序中序&#xff0c;进行判断即可。我…

【PyTorch】神经风格迁移项目

神经风格迁移中&#xff0c;取一个内容图像和一个风格图像&#xff0c;综合内容图像的内容和风格图像的艺术风格生成新的图像。 目录 准备数据 处理数据 神经风格迁移模型 加载预训练模型 定义损失函数 定义优化器 运行模型 准备数据 创建data文件夹&#xff0c;放入…

数据恢复软件:电脑丢失文件,及时使用数据恢复软件恢复!

数据恢复软件什么时候会用到&#xff1f; 答&#xff1a;如果真的不小心删除文件&#xff0c;清空回收站&#xff0c;电脑重装系统等情况发生&#xff0c;我们要懂的及时停止使用电子设备&#xff0c;使用可靠的数据恢复软件&#xff0c;帮助我们恢复这些电子设备的数据&#…