【算法训练-排序算法 一】【手撕排序】快速排序、堆排序、归并排序

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【手撕排序系列】,使用【数组】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。手撕排序系列共3道常考题,分别是【快速排序、归并排序、堆排序】这个顺序也是面试出现频度的顺序
在这里插入图片描述

明确目标题后,附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

快速排序【MID】

首先来最高频的题目,快速排序

题干

在这里插入图片描述

解题思路

使用快速排序的思路来解决,快速排序(Quick Sort)是一种基于分治思想的排序算法,它通过将数组分成较小和较大的两部分,并分别对这两部分进行排序,最终将整个数组排序。快速排序是一种高效的排序算法,通常在平均情况下具有较快的执行速度。
在这里插入图片描述

下面是快速排序的基本思想和步骤:

  1. [划分]选择基准元素(Pivot): 从数组中选择一个元素作为基准元素。

  2. [划分]划分(Partition): 将数组分成两部分,使得基准元素左边的元素都小于等于基准元素,右边的元素都大于基准元素。这一步骤通常称为“划分”。

  3. [解决]递归排序: 递归地对基准元素左边和右边的子数组进行排序。也就是说,对小于基准元素的子数组和大于基准元素的子数组分别执行快速排序。

  4. [合并] 合并: 由于子数组都是在原数组中进行排序,所以最终整个数组也就被排序了。

这些步骤使得较大问题被分解成较小的子问题,这些子问题又能通过递归地应用快速排序来解决。在最好情况下,每次划分都能将数组均匀分成两半,这使得算法的时间复杂度为O(n log n)

然而,需要注意的是,快速排序的性能高度依赖于基准元素的选择。最坏情况下,如果每次划分都使数组分成极不平衡的两部分,算法的时间复杂度可能会退化到O(n^2)。为了应对这种情况,通常可以选择合适的基准元素,如随机选择或者采用三数取中等方法。

总之,快速排序是一种常用且高效的排序算法,尤其适用于大规模数据的排序。

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构
算法快速排序(分治算法)、二分查找
技巧双指针

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*** @param n int整型 the n* @return int整型*/public int[] sortArray(int[] nums) {return quikSort(nums, 0, nums.length - 1);}// 对数组进行快速排序private int[] quikSort(int[] nums, int start, int end) {int privot;if (start < end) {// 1 获取数组基准值元素位置privot = partition(nums, start, end);// 2 分治:左半边元素快排quikSort(nums, start, privot - 1);// 3 分治:右半边元素快排quikSort(nums, privot + 1, end);}return nums;}// 单次归位基准值方法private int partition(int[] nums, int start, int end) {// 0 随机选择基准数大小,并与最左侧位置交换int randomValueIndex = new Random().nextInt(end - start + 1) + start;swap(nums, start, randomValueIndex);// 1 数组左边第一个为基准值,双指针分别执行开始和结束int privot = nums[start];int i = start;int j = end;// 2 开始交互,直到ij碰面,顺排while (i < j) {// 2-1 从右边开始找,如果值一直大于基准值则一直循环,直到找到小于基准值的元素,终止// 需要注意,满足条件情况下,因为基准值在最左边,最后要与j交换,所以j停止元素一定要小于基准值,所以优先满足j的条件,j要先走while (i < j && nums[j] >= privot) {j--;}// 2-2 从左边开始找,如果值一直小于基准值则一直循环,直到找到大于基准值的元素,终止while (i < j && nums[i] <= privot) {i++;}// 2-3 找到要交换的元素,且依然满足i<j的条件,交换if (i < j) {swap(nums, i, j);}}// 3 i和j碰面,说明该交换的元素已经交换完了,最后交换基准值与碰面的值swap(nums, start, j);// 4 返回基准值的当前位置,需要按此位置分割return j;}// 元素交换方法private void swap(int[] numbers, int i, int j) {int temp = numbers[i];numbers[i] = numbers[j];numbers[j] = temp;}}

需要注意,为了保证平衡性,可以选择随机找个值作为基准值

int randomValueIndex = new Random().nextInt(end - start + 1) + start; 
swap(partNums, start, randomValueIndex);

复杂度分析

快速排序的时间复杂度和空间复杂度如下:

时间复杂度:

  • 平均情况: 在平均情况下,快速排序的时间复杂度为O(n log n),其中n是待排序数组的长度。这是因为每次划分都能将数组大致均匀地分成两部分,导致递归的深度大约为log n,而每次划分的过程需要O(n)的时间。

  • 最坏情况: 在最坏情况下,如果每次划分都导致一个极不平衡的分割(例如每次选取的基准元素都是当前子数组的最大或最小元素),那么快速排序的时间复杂度可能退化到O(n^2)。这是因为需要执行n次划分,每次划分都需要O(n)的时间。为了避免最坏情况,通常采用随机选择基准元素或者三数取中法来减少极端情况的发生。

  • 最好情况: 快速排序的最好情况时间复杂度为O(n log n),与平均情况相同。这种情况发生在每次划分都能将数组准确地分成相等的两部分时。

空间复杂度:

快速排序的空间复杂度主要取决于递归调用的深度和每次划分所使用的额外空间。

  • 递归调用的深度: 在递归调用中,每次只需要保存一个基准元素的索引和部分数组的边界信息。因此,递归调用的深度为O(log n)

  • 每次划分所使用的额外空间: 每次划分需要O(1)的额外空间来存储基准元素和进行交换。

综合考虑,快速排序的空间复杂度为O(log n)。这是因为虽然递归调用的深度为O(log n),但在每层递归中所需的额外空间是常数级别的。这使得快速排序在空间上比某些其他排序算法(如归并排序)更加节省。

归并排序【MID】

然后再做一道次高频的题目:归并排序,题干与快排一样

题干

在这里插入图片描述

解题思路

基本思路:借助额外空间,合并两个有序数组,得到更长的有序数组。归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾

接下来实现递归的归并排序

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构
算法归并排序
技巧双指针

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*** @param n int整型 the n* @return int整型*/public int[] sortArray(int[] nums) {mergeSort(nums, 0, nums.length - 1);return nums;}// 对数组进行递归的归并排序private void mergeSort(int[] nums, int left, int right) {// 1 与快排不同的是,分割点直接选择中间位置int middle = left + (right - left) / 2;// 2 只要start<end,就一直进行归并if (left < right) {// 2-1 先分别归并左右两边,左右两边有序了,则最终有序mergeSort(nums, left, middle);mergeSort(nums, middle + 1, right);// 2-2 最后合并左右两边有序数组merge(nums, left, middle, right);}}// 单次划分的归并private void merge(int[] nums, int left, int middle, int right) {// 1 设置归并临时结果集,大小为归并后的总长度int[] result = new int[right - left + 1];// 2 定义两个排序数组的指针和结果集指针int i = left;int j = middle + 1;int k = 0;// 3 开始比较已排序集合值并将结果加入结果集while (i <= middle && j <= right) {if (nums[i] < nums[j]) {result[k++] = nums[i++];} else {result[k++] = nums[j++];}}// 4 如果有一个已经用完了,补充另一个排序数组while (i <= middle) {result[k++] = nums[i++];}while (j <= right) {result[k++] = nums[j++];}// 5 需要把这一段合并后已排序结果合并到整体nums的分段上for (int index = 0; index < result.length; index++) {nums[left + index] = result[index];}}}

复杂度分析

时间复杂度:O(Nlog⁡N),这里 N 是数组的长度
空间复杂度:O(N),辅助数组与输入数组规模相当

堆排序【MID】

最后做一道频度最低的题目:堆排序

题干

题干与快排及归并排序一致
在这里插入图片描述

解题思路

这里简单介绍下:数组中下标为 i 的节点的左子节点,就是下标为 i∗2 的节点,右子节点就是下标为 i∗2+1 的节点,父节点就是下标为 2i​ 的节点

1 建堆

我们首先将数组原地建成一个堆。所谓“原地”就是,不借助另一个数组,就在原数组上操作。建堆的过程是从后往前处理数组,并且每个数据都是从上往下堆化,因为叶子节点往下堆化只能自己跟自己比较,所以我们直接从最后一个非叶子节点开始,依次堆化就行了
在这里插入图片描述

2 排序

建堆结束之后,数组中的数据已经是按照大顶堆的特性来组织的。数组中的第一个元素就是堆顶,也就是最大的元素。我们把它跟最后一个元素交换,那最大元素就放到了下标为 n 的位置。这个过程有点类似上面讲的“删除堆顶元素”的操作,当堆顶元素移除之后,我们把下标为 n 的元素放到堆顶,然后再通过堆化的方法,将剩下的 n−1 个元素重新构建成堆。堆化完成之后,我们再取堆顶的元素,放到下标是 n−1 的位置,一直重复这个过程,直到最后堆中只剩下标为 1 的一个元素,排序工作就完成了
在这里插入图片描述

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构
算法堆排序
技巧双指针

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*** @param n int整型 the n* @return int整型*/public int[] sortArray(int[] nums) {heapSort(nums);return nums;}// 1 堆排序函数private void heapSort(int[] nums) {// 1 构建大顶堆,此时数组0的位置即为堆顶元素,也就是数组最大值int heapSize = nums.length;buildMaxHeap(nums, heapSize);// 2 将堆顶元素与数组末尾元素交换for (int i = nums.length - 1; i >= 1; i--) {// 2-1 交换末尾元素与堆顶元素swap(nums, i, 0);// 2-2 缩小堆的生成范围,不包含末尾元素,因为末尾元素已调整完heapSize--;// 2-3 因为将一个末尾元素放到了堆顶,且其不一定是剩余元素中最大的,所以需要重新进行堆调整heapify(nums, 0, heapSize);}}// 2 构建大顶堆函数private void buildMaxHeap(int[] nums, int heapSize) {for (int i = heapSize / 2; i >= 0; i--) {heapify(nums, i, heapSize);}}// 3 堆调整函数private void heapify(int[] nums, int i, int heapsize) {// 逐层的进行元素调整:这里包含=号是因为要允许存在只有左节点的情况while (2 * i + 1 <= nums.length) {// 1 确定节点的左右节点位置int leftChild = 2 * i;int rightChild = 2 * i + 1;int maxPos = 0;// 2 当前层:进行堆调整,获取(当前、左子节点、右子节点)中值最大的索引,与堆顶进行交换maxPos = i;if (leftChild < heapsize && nums[i] < nums[leftChild]  ) {maxPos = leftChild;}if ( rightChild < heapsize && nums[maxPos] < nums[rightChild] ) {maxPos = rightChild;}// 3 如果maxPos不是i,则交换if (i != maxPos) {swap(nums, i, maxPos);// 继续下沉进行堆调整i = maxPos;} else {break;}}}// 4元素交换方法private void swap(int[] numbers, int i, int j) {int temp = numbers[i];numbers[i] = numbers[j];numbers[j] = temp;}
}

复杂度分析

在堆排序算法中,建立堆的时间复杂度通常为O(n),其中n是要排序的元素数量。这是因为堆的构建分为两个阶段:堆的构建和堆的调整。

  1. 堆的构建:首先,将待排序的n个元素按照从左到右的顺序依次插入堆中,这个过程是线性时间的,即O(n)。

  2. 堆的调整:然后,需要对堆进行调整以满足堆的性质(通常是最大堆或最小堆)。这个调整阶段的时间复杂度取决于堆的高度,通常为O(log n)。在堆排序的整个过程中,堆的调整阶段需要执行n次,所以总时间复杂度是O(n log n)

总的来说,堆排序的建堆阶段的时间复杂度是O(n),而排序阶段的时间复杂度是O(n log n)。因此,堆排序的总时间复杂度为O(n + n log n),通常被表示为O(n log n),因为在渐进分析中,线性时间的操作通常被忽略。

由于堆是原地构建,所以空间复杂度为O(N)

拓展知识:分治算法、堆的基本概念、算法比较

1 分治算法

分治法是一种解决问题的算法设计范式,它将一个问题分解成多个相似的子问题,然后解决这些子问题,并将它们的解合并以得出原始问题的解。分治法的核心思想是将大问题分解成更小的、相似的子问题,通过解决子问题来解决原始问题。

分治法通常包含三个步骤:分解(Divide)、解决(Conquer)、合并(Combine)。

  1. 分解(Divide): 将原始问题划分为更小、相似的子问题。这一步骤通常是递归地进行的,即将问题逐步分解为更小规模的子问题。

  2. 解决(Conquer): 递归地解决子问题。当子问题足够小,可以直接求解时,就停止分解,转而解决这些子问题。

  3. 合并(Combine): 将子问题的解合并以得出原始问题的解。这是分治法的关键步骤,将各个子问题的解整合起来形成更大问题的解。

分治法通常用于解决一些可以被分解成相似子问题的问题,如排序、搜索、求解最短路径等。典型的分治算法包括归并排序快速排序。以下是一个分治法的示例:

归并排序:

  1. 分解(Divide): 将数组分成两半,分别对这两半进行排序。

  2. 解决(Conquer): 对分解得到的子数组递归地进行排序,直到子数组长度足够小。

  3. 合并(Combine): 将排好序的子数组合并,得到完整的有序数组。

分治法的优点在于它可以将问题分解成独立的子问题,每个子问题的求解都相对简单。这使得算法设计和理解变得更加清晰。然而,分治法有时会在子问题的合并阶段引入额外的开销,因此在设计分治算法时需要权衡分解和合并的成本。

堆的基本概念

在计算机科学中,一个"堆"(Heap)通常指的是一种特殊的数据结构,它是一种树状结构,通常用于优先队列和堆排序等算法。
在这里插入图片描述

堆具有以下特点:

  1. 完全二叉树结构:堆通常是一棵完全二叉树,这意味着树的所有层级都被填满,除了最底层,最底层的节点从左向右依次填充。这个特性使得堆可以有效地使用数组来表示,因为树的节点可以在数组中按照特定的规则排列,从而节省内存和提高访问效率。

  2. 堆序性质:堆被维护为满足"堆序性质"(Heap Property)的树,这意味着在最大堆(Max Heap)中,对于任意节点i,其父节点的值必须大于或等于i的值;而在最小堆(Min Heap)中,父节点的值必须小于或等于i的值。这个性质使得在堆中的根节点永远是最大值(最大堆)或最小值(最小堆)。

堆被广泛用于解决一些基本问题,如:

  • 优先队列:通过使用最小堆或最大堆,可以实现高效的优先队列,其中具有最高(或最低)优先级的元素在队列的顶部。

  • 堆排序:堆排序是一种基于堆数据结构的排序算法,它利用堆的特性来进行排序。在堆排序中,首先将未排序的元素构建为一个堆,然后反复删除堆顶元素,将其放入已排序部分,直到堆为空。

  • 调度算法:堆可以用于操作系统中的进程调度,其中具有最高优先级的进程被安排在最前面。

  • 最短路径算法:一些最短路径算法,如Dijkstra算法,使用最小堆来快速查找最小距离的节点。

总之,堆是一种重要的数据结构,它提供了高效的方式来管理数据,特别是在需要按优先级对数据进行操作时。最大堆和最小堆分别用于找到最大值和最小值,这使得堆在许多领域中非常有用。

3 排序算法比较

以下是快速排序、归并排序和堆排序的比较,包括时间复杂度、空间复杂度和一些其他关键特点:

特性快速排序归并排序堆排序
时间复杂度平均情况 O(n log n)平均情况 O(n log n)平均情况 O(n log n)
最坏情况 O(n^2)最坏情况 O(n log n)最坏情况 O(n log n)
最佳情况 O(n log n)最佳情况 O(n log n)最佳情况 O(n log n)
稳定性不稳定稳定不稳定
空间复杂度平均情况 O(log n)平均情况 O(n)平均情况 O(1)
最坏情况 O(n)最坏情况 O(n)最坏情况 O(1)
适用性通常用于大型数据集通常用于大型数据集通常用于内存受限情况
且需要稳定排序时
分治策略
额外的数据移动较多较少较少
需要的额外空间递归调用的栈空间辅助数组常数额外空间
(in-place)
实现复杂度中等中等相对较高

总结:

  • 快速排序通常在平均情况下具有较好的性能,但在最坏情况下性能较差,因此不适用于某些特定情况。
  • 归并排序具有一致的性能,但需要较大的额外内存空间,通常不适用于内存受限的情况。
  • 堆排序通常需要较少的额外内存空间,但在排序稳定性和实现复杂性方面有一些局限性,适合内存受限的情况。

选择排序算法应根据具体情况和性能要求来决定,没有一种算法适用于所有情况。

4 稳定性分析

“稳定性"是指排序算法在处理具有相等键值的元素时,能否保持它们在原始序列中的相对顺序。一个排序算法被称为"稳定”,如果对于相等的元素,它们的相对顺序在排序后仍然保持不变。相反,如果排序算法不能保持相等元素的相对顺序,那么它被称为"不稳定"。

下面是对快速排序、归并排序和堆排序的稳定性解释:

  1. 快速排序:快速排序是一个不稳定的排序算法。在快速排序中,相等元素的相对顺序可能会发生变化,具体取决于选择的划分策略和元素交换操作。

  2. 归并排序:归并排序是一个稳定的排序算法。在归并排序中,相等元素的相对顺序始终保持不变。这是因为在合并过程中,如果有相等的元素,它们会按照它们在原始数组中的顺序放置在合并后的数组中。

  3. 堆排序:堆排序通常是一个不稳定的排序算法。虽然堆排序的堆构建过程可能会改变相等元素的相对顺序,但在堆化和排序的过程中,相等元素的相对顺序通常不会被保持。

稳定性在某些应用中很重要,特别是在需要按多个条件进行排序或者需要保持原始数据的某种有序性时。在这些情况下,稳定的排序算法更有用。如果稳定性不是关键因素,那么可以选择性能更高的不稳定排序算法。

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

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

相关文章

UPS监控技术,你一定要试试,太绝了!

UPS&#xff08;不间断电源&#xff09;监控系统是一种关键的技术&#xff0c;用于监视、管理和维护不间断电源设备&#xff0c;以确保电力供应的稳定性和可用性。这对于各种组织和企业来说至关重要&#xff0c;因为电力中断可能导致生产中断、数据丢失和设备损坏&#xff0c;对…

全流量安全分析发现内部系统外联异常

内部系统外连监控的重要性在于保护企业的信息安全和预防数据泄露&#xff0c;以下是几个重要的理由&#xff1a; 1、检测异常活动&#xff1a;通过监控内部系统的外连连接&#xff0c;可以及时发现是否有未经授权或异常的链接尝试。这可能表示存在恶意软件、黑客攻击或内部员工…

MySQL JSON_TABLE() 函数

JSON_TABLE()函数从一个指定的JSON文档中提取数据并返回一个具有指定列的关系表。 应用&#xff1a;数据库字段以JSON 存储后&#xff0c;实际应用需要对其中一个字段进行查询 语法 JSON_TABLE(json,path COLUMNS(column[,column[,...]]))column:name参数 json必需的。一个 …

使用Python创建faker实例生成csv大数据测试文件并导入Hive数仓

文章目录 一、Python生成数据1.1 代码说明1.2 代码参考 二、数据迁移2.1 从本机上传至服务器2.2 检查源数据格式2.3 检查大小并上传至HDFS 三、beeline建表3.1 创建测试表并导入测试数据3.2 建表显示内容 四、csv文件首行列名的处理4.1 创建新的表4.2 将旧表过滤首行插入新表 一…

docker部署的jenkins配置(接口自动化)

目录 一、jenkins汉化1.点击Manage Jenkins&#xff08;系统管理&#xff09;&#xff0c;点击Plugins&#xff08;插件&#xff09;2.安装Locale插件 二、jenkins配置allure报告1.安装allure插件2.配置 三、配置jenkins项目1.新建任务2.创建项目3.源码管理4.构建触发器5.增加构…

12个VIM编辑器的高级玩法

vim 是一个很好用的编辑器&#xff0c;应用十分广泛。但关于 vim&#xff0c;总有一些你不知道的事情&#xff0c;我们需要持续不断的学习。 我经常使用 vim&#xff0c;也经常在各大社区、论坛看到 vim 专家用户分享经验&#xff0c;今天我们就总结其中常用的一部分&#xff…

科普向丨语音芯片烧录工艺的要求

语音芯片烧录工艺要求烧录精度、速度、内存容量、电源稳定性、兼容性和数据安全性。这些要素需优化和控制以保证生产高效、稳定、安全并烧录出高质量的语音芯片。不同厂家生产的语音芯片在烧录工艺上存在差异&#xff0c;需相应设计和研发以实现兼容。 一、烧录精度 语音芯片烧…

hive排序

目录 order by (全局排序asc ,desc) sort by(reduce 内排序) Distribute by(分区排序) Cluster By&#xff08;当 distribute by 和 sorts by 字段相同时 &#xff0c;可以使用 &#xff09; order by (全局排序asc ,desc) INSERT OVERWRITE LOCAL DIRECTORY /home/test2 …

城市综合管廊运维的系统集成方案

摘 要&#xff1a;从网络拓扑结构、开放式实时以太网协议、控制层系统配置方面介绍了综合管廊的系统网络架构设计&#xff0c;分析了无线网络特性&#xff0c;阐述了基于HTML5架构所能实现的功能的初步构想&#xff0c;以便于综合管廊运维人员巡检&#xff0c;确保管廊本体安全…

2023年(2024届)双非保研历程(中南、东南、浙大、信工所、国防科大、北邮)

个人情况 学校层次&#xff1a;双非 专业&#xff1a;信息安全&#xff08;投的基本都是网安&#xff09; 排名&#xff1a;2/66 英语&#xff1a;六级565&#xff0c;四级560 竞赛&#xff1a;大英赛国三、美赛H、蓝桥杯省一、数竞省一、词达人省一、数模国赛省三。 论文&…

第二证券:国际油价大幅上涨 后市恐难持续走高

上个买卖周&#xff0c;受巴以冲突影响&#xff0c;原油商场成为各方关注的焦点。到上星期五收盘&#xff0c;布伦特原油周内涨幅达7%以上&#xff0c;为本年2月以来最大周涨幅&#xff0c;WTI原油周内累计上涨近6%。业内人士认为&#xff0c;其时地缘要素是导致油价出现异动的…

语音识别whisper的介绍、安装、错误记录

介绍 Whisper是OpenAI于2022年9月份开源的通用的语音识别模型。它是在各种音频的大型数据集上训练的模型&#xff0c;也是一个可以执行多语言语音识别、语音翻译和语言识别的多任务模型。 论文链接&#xff1a;https://arxiv.org/abs/2212.04356 github链接&#xff1a;https:…

关于京东API数据接口业务的详细介绍

参数说明 通用参数说明 url说明 https://…….cn/平台/API类型/ 平台&#xff1a;淘宝&#xff0c;京东等&#xff0c; API类型:[item_search,item_get,item_search_shop等]version:API版本key:调用key,测试key:test_api_keysecret:调用secret,测试secret:(不用填写)cache:[ye…

2023年中国城市矿产行业产值及发展趋势分析[图]

城市矿产是指工业化和城镇化过程中产生和蕴藏于废旧机电设备、电线电缆、通讯工具、汽车、家电、电子产品、金属和塑料包装物以及废料中&#xff0c;可循环利用的钢铁、有色金属、贵金属、塑料、橡胶等资源。 开展“城市矿产”示范基地建设是缓解资源瓶颈约束&#xff0c;减轻环…

第二证券:券商etf的买卖规则?

在当时迅速发展的证券商场中&#xff0c;ETF已经成为出资者的首选。ETF&#xff08;Exchange Traded Fund&#xff09;是一种证券东西&#xff0c;它被规划成类似于股票的生意办法。即出资者可以在证券生意所上以股票办法进行购买和出售。详细到券商ETF的生意规矩&#xff0c;咱…

【数据结构】双链表的相关操作(声明结构体成员、初始化、判空、增、删、查)

双链表 双链表的特点声明双链表的结构体成员双链表的初始化带头结点的双链表初始化不带头结点的双链表初始化调用双链表的初始化 双链表的判空带头结点的双链表判空不带头结点的双链表判空 双链表的插入&#xff08;按值插入&#xff09;头插法建立双链表带头结点的头插法每次调…

前端代码优化之从系统区分处理的业务场景看如何优化代码中的if判断

最近有个三端统一的技术场景&#xff0c;主要是以前移动端的 hybrid 网页在不考虑 UI 适配的情况下、期望能够直接在 PC 客户端投放。在评估修改面的时候发现了一段可以深思的代码&#xff1a; if (platform iphone) {location.href iphoneClientUrl; } else {location.href…

Webpack和JShaman相比有什么不同?

Webpack和JShaman相比有什么不同&#xff1f; Webpack的功能是打包&#xff0c;可以将多个JS文件打包成一个JS文件。 JShaman专门用于对JS代码混淆加密&#xff0c;目的是让JavaScript代码变的不可读、混淆功能逻辑、加密代码中的隐秘数据或字符&#xff0c;是用于代码保护的…

LED显示屏高刷新率和低刷新率有什么区别

LED显示屏的刷新率是指图像在LED显示屏上更新的速度&#xff0c;也即屏幕上的图像每秒钟出现的次数&#xff0c;它的单位是赫兹&#xff08;Hz&#xff09;。LED显示屏的刷新率越高&#xff0c;图像闪烁感就越小&#xff0c;稳定性也就越高&#xff0c;换言之对视力的保护也越好…

图片批处理工具 PhotoMill X直装 for mac

PhotoMill X是一款强大的图像处理软件&#xff0c;它可以帮助用户快速地对照片进行编辑、调整和转换。它支持在单个或批量模式下处理大量的图像文件&#xff0c;并具有直观的用户界面和易于使用的工具。 PhotoMill X具有的功能有&#xff1a; 裁剪、缩放、旋转、调整明暗度、…