15. 算法之排序算法

news2024/11/28 9:38:24

前言

排序是在软件开发中经常遇到的需求。比如基于订单的创建时间倒排,基于金额大小排序等等,那么这些排序底层是怎么写的呢,本节,我们就常用排序算法展开介绍。

1. 冒泡排序

1.1 算法思想

冒泡排序是最基础的排序算法。冒泡排序的英文是bubble sort,它是一种基础的交换排序。
冒泡排序这种排序算法的每一个元素都可以像小气泡一样,根据自身大小,一点一点地向着数组的一侧移动。
按照冒泡排序的思想,我们要把相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置;当一个元素小于或等于右侧相邻元素时,位置不变。

1.2 代码实现

package org.wanlong.sort;

/**
 * @author wanlong
 * @version 1.0
 * @description: 冒泡排序
 * @date 2023/6/6 13:38
 */
public class BubbleSort {

    public static int[] sort(int[] nums) {
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = 0; j < nums.length - 1; j++) {
                //临时变量 用于交换
                int tmp = 0;
                if (nums[j] > nums[j + 1]) {
                    tmp = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = tmp;
                }
            }
        }
        return nums;
    }
}

1.3 测试验证

@Test
public void testBubble(){
    int[] nums = new int[]{5, 8, 6, 3, 9, 2, 1, 7};
    for (int i : BubbleSort.sort(nums)) {
        System.out.println(i);
    }
}

运行结果:

1
2
3
5
6
7
8
9

1.4 冒泡排序优化

1.4.1 优化项

  1. 已经循环过的不需要再遍历,每次外层循环会将一个元素移动到最左边或者最右边,为此,内层循环遍历的时候不需要每次都遍历整个数组
  2. 在外层循环处,设置标志isSort,默认为排好,如果不交换则跳出本次循环,说明所有元素有序,可以不继续了

1.4.2 代码实现

/**
* @Description: 排序优化
* @Author: wanlong
* @Date: 2023/6/6 13:51
* @param nums:
* @return int[]
**/
public static int[] sort2(int[] nums) {
   //是否已排序标识
   boolean isSort=true;
   for (int i = 0; i < nums.length - 1; i++) {
       //优化1,每次循环迭代会将最大的元素移动到末尾,所以内存循环不需要移动到末尾元素
       for (int j = 0; j < nums.length - 1-i; j++) {
           //临时变量 用于交换
           int tmp = 0;
           if (nums[j] > nums[j + 1]) {
               //有交换说明还没排好序,标识设置为false
               isSort=false;
               tmp = nums[j];
               nums[j] = nums[j + 1];
               nums[j + 1] = tmp;
           }
       }
       //排好了跳出循环
       if (isSort){
           break;
       }
   }
   return nums;
}

1.4.3 测试验证

@Test
public void testBubble2(){
    int[] nums = new int[]{5, 8, 6, 3, 9, 2, 1, 7};
    for (int i : BubbleSort.sort2(nums)) {
        System.out.println(i);
    }
}

运行结果:

1
2
3
5
6
7
8
9

2. 快速排序

2.1 算法思想

2.1.1 简介

同冒泡排序一样,快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。
不同的是,冒泡排序在每一轮中只把1个元素冒泡到数列的一端,而快速排序则在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分,这种思路就叫作分治法
在这里插入图片描述

2.1.2 基准元素选择

基准元素,英文是pivot,在分治过程中,以基准元素为中心,把其他元素移动到它的左右两边。
我们可以随机选择一个元素作为基准元素,并且让基准元素和数列首元素交换位置。
在这里插入图片描述

2.1.3 元素的交换

选定了基准元素以后,我们要做的就是把其他元素中小于基准元素的都交换到基准元素一边,大于基准元素的都交换到基准元素另一边。

2.1.3.1 双边循环法

  1. 选定基准元素pivot,并且设置两个指针left和right,指向数列的最左和最右两个元素
    在这里插入图片描述

  2. 接下来进行第1次循环:从right指针开始,让指针所指向的元素和基准元素做比较。如果大于或等于pivot,则指针向左移动;如果小于pivot,则right指针停止移动,切换到left指针轮到left指针行动,让指针所指向的元素和基准元素做比较。如果小于或等于pivot,则指针向右移动;

  3. 如果大于pivot,则left指针停止移动左右指针指向的元素交换位置,由于left开始指向的是基准元素,判断肯定相等,所以left右移1位
    在这里插入图片描述

  4. 由于7>4,left指针在元素7的位置停下。这时,让left和right指针所指向的元素进行交换。
    在这里插入图片描述

  5. 进入第2次循环,重新切换到right指针,向左移动。right指针先移,动到8,8>4,继续左移。由于2<4,停止在2的位置

在这里插入图片描述

2.1.3.2 单边循环法

单边循环法只从数组的一边对元素进行遍历和交换。
开始和双边循环法相似,首先选定基准元素pivot。同时,设置一个mark指针指向数列起始位置,
这个mark指针代表小于基准元素的区域边界。
在这里插入图片描述
接下来,从基准元素的下一个位置开始遍历数组。
如果遍历到的元素大于基准元素,就继续往后遍历
如果遍历到的元素小于基准元素,则需要做两件事:
第一,把mark指针右移1位,因为小于pivot的区域边界增大了1;
第二,让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小
于pivot的区域

  1. 遍历到元素7,7>4,所以继续遍历。
    在这里插入图片描述
  2. 遍历到的元素是3,3<4,所以mark指针右移1位
    在这里插入图片描述
  3. 让元素3和mark指针所在位置的元素交换,因为元素3归属于小于pivot的区域。
    在这里插入图片描述
  4. 按照这个思路,继续遍历,后续步骤如图所示
    在这里插入图片描述

2.2 代码实现

2.2.1 双边循环

package org.wanlong.sort;

/**
 * @author wanlong
 * @version 1.0
 * @description: 快速排序
 * @date 2023/6/7 18:39
 */
public class QuickSort {

    public static void quickSort(int[] arr, int startIndex, int endIndex){
        // 递归结束条件:startIndex大于或等于endIndex时
        if (startIndex >= endIndex) {
            return;
        }
        // 得到基准元素位置
        int pivotIndex = partition(arr, startIndex, endIndex);
        // 根据基准元素,分成两部分进行递归排序
        quickSort(arr, startIndex, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, endIndex);
    }



    /**
     * 分治(双边循环法)
     *
     * @param arr        待交换的数组
     * @param startIndex 起始下标
     * @param endIndex   结束下标
     * @return
     */
    private static int partition(int[] arr, int startIndex, int endIndex) {
        // 取第1个位置(也可以选择随机位置)的元素作为基准元素
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;
        while (left != right) {
            //控制right 指针比较并左移
            while (left < right && arr[right] > pivot) {
                right--;
            }
            //控制left指针比较并右移
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            //交换left和right 指针所指向的元素
            if (left < right) {
                int p = arr[left];
                arr[left] = arr[right];
                arr[right] = p;
            }
        }
        //pivot 和指针重合点交换
        arr[startIndex] = arr[left];
        arr[left] = pivot;
        return left;
    }
}

2.2.2 单边循环

public class QuickSort {
    public static void quickSort2(int[] arr, int startIndex,
                                 int endIndex) {
        // 递归结束条件:startIndex大于或等于endIndex时
        if (startIndex >= endIndex) {
            return;
        }
        // 得到基准元素位置
        int pivotIndex = partition2(arr, startIndex, endIndex);
        // 根据基准元素,分成两部分进行递归排序
        quickSort2(arr, startIndex, pivotIndex - 1);
        quickSort2(arr, pivotIndex + 1, endIndex);
    }


    /**
     * 分治(单边循环法)
     *
     * @param arr 待交换的数组
     * @param startIndex 起始下标
     * @param endIndex 结束下标
     * @return
     */
    private static int partition2(int[] arr, int startIndex, int endIndex) {
        // 取第1个位置(也可以选择随机位置)的元素作为基准元素
        int pivot = arr[startIndex];
        int mark = startIndex;
        for (int i = startIndex + 1; i <= endIndex; i++) {
            if (arr[i] < pivot) {
                mark++;
                int p = arr[mark];
                arr[mark] = arr[i];
                arr[i] = p;
            }
        }
        arr[startIndex] = arr[mark];
        arr[mark] = pivot;
        return mark;
    }
}

2.3 测试验证

    @Test
    public void testQuickSingle(){
        int[] nums = new int[]{5, 8, 6, 3, 9, 2, 1, 7};
        QuickSort.quickSort2(nums,0,nums.length-1);
        for (int i : nums) {
            System.out.println(i);
        }
    }

    @Test
    public void testQuickDouble(){
        int[] nums = new int[]{5, 8, 6, 3, 9, 2, 1, 7};
        QuickSort.quickSort(nums,0,nums.length-1);
        for (int i : nums) {
            System.out.println(i);
        }
    }

运行结果和预期一致:

1
2
3
5
6
7
8
9

3. 堆排序

3.1 算法思想

堆排序:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
大顶堆:每个结点的值都大于或等于其左右孩子结点的值
小顶堆:每个结点的值都小于或等于其左右孩子结点的值

之前介绍过,因为堆是完全二叉树,那么我们完全可以用数组来维护堆这种数据结构
我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 2*(i+1)
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

  1. 构造初始堆,将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

在这里插入图片描述

  1. 此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-
    1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整
    在这里插入图片描述
  2. 找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换
    在这里插入图片描述
  3. 这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
    在这里插入图片描述
  4. 我们就将一个无需序列构造成了一个大顶堆。将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换
    将堆顶元素9和末尾元素4进行交换

在这里插入图片描述

  1. 重新调整结构,使其继续满足堆定义
    在这里插入图片描述
  2. 再将堆顶元素8与末尾元素5进行交换,得到第二大元素8
    在这里插入图片描述
  3. 后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

3.2 代码实现

package org.wanlong.sort;

/**
 * @author wanlong
 * @version 1.0
 * @description: 堆排序
 * @date 2023/6/7 18:59
 */
public class HeapSort {

    public static void sort(int[] array) {
        // 1. 把无序数组构建成最大堆
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustHeap(array, i, array.length);
        }
        // 2. 调整堆结构+交换堆顶元素与末尾元素,调整堆产生新的堆顶
        for (int i = array.length - 1; i > 0; i--) {
            // 最后1个元素和第1个元素进行交换
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;
            // “下沉”调整最大堆
            adjustHeap(array, 0, i);
        }
    }

    public static void adjustHeap(int[] array, int parentIndex, int length) {
        // temp 保存父节点值,用于最后的赋值
        int temp = array[parentIndex];
        int childIndex = 2 * parentIndex + 1;
        while (childIndex < length) {
            // 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
            if (childIndex + 1 < length && array[childIndex + 1] >
                    array[childIndex]) {
                childIndex++;
            }
            // 如果父节点大于任何一个孩子的值,则直接跳出
            if (temp >= array[childIndex])
                break;
            //无须真正交换,单向赋值即可
            array[parentIndex] = array[childIndex];
            parentIndex = childIndex;
            //下一个左孩子
            childIndex = 2 * childIndex + 1;
        }
        array[parentIndex] = temp;
    }
}

3.3 测试验证

@Test
public void testHeapSort(){
    int[] nums = new int[]{5, 8, 6, 3, 9, 2, 1, 7};
    HeapSort.sort(nums);
    for (int i : nums) {
        System.out.println(i);
    }
}

4. 计数排序

4.1 算法思想

计数排序,这种排序算法是利用数组下标来确定元素的正确位置的。

假设数组中有10个整数,取值范围为0~10,要求用最快的速度把这10个整数从小到大进行排序。
可以根据这有限的范围,建立一个长度为11的数组。数组下标从0到10,元素初始值全为0
假设数组数据为:9,1,2,7,8,1,3,6,5,3
下面就开始遍历这个无序的随机数列,每一个整数按照其值对号入座,同时,对应数组下标的元素进行加1操作
例如第1个整数是9,那么数组下标为9的元素加1
在这里插入图片描述
当数列遍历完毕时,数组的状态如下:
在这里插入图片描述
该数组中每一个下标位置的值代表数列中对应整数出现的次数,直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次,0不输出
则顺序输出是:1、1、2、3、3、5、6、7、8、9

可以看到,计数排序适合于连续的取值范围不大的数组不连续和取值范围过大会造成数组过大
如果起始数不是从0开始,比如分数排序
95,94,91,98,99,90,99,93,91,92
数组起始数为90,这样数组前面的位置就浪费了
可以采用偏移量的方式:
在这里插入图片描述

4.2 代码实现

package org.wanlong.sort;

/**
 * @author wanlong
 * @version 1.0
 * @description: 计数排序
 * @date 2023/6/5 19:00
 */
public class CountSort {

    public static int[] countSort(int[] array, int offset) {
        int[] nums = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            int n = (array[i] - offset);
            //数字自增
            nums[n]++;
        }
        int[] nums2 = new int[array.length];
        // i是计数数组下标,k是新数组下标
        for (int i = 0, k = 0; i < nums.length; i++) {
            for (int j = 0; j < nums[i]; j++) {
                nums2[k++] = i + offset;
            }
        }
        return nums2;
    }
}

4.3 测试验证

@Test
public void testCountSort(){
    int[] nums = new int[]{95, 94, 91, 98, 99, 90, 99, 93, 91, 92};
    for (int i : CountSort.countSort(nums,90)) {
        System.out.println(i);
    }
}

5. 桶排序

5.1 算法思想

桶排序同样是一种线性时间的排序算法。桶排序需要创建若干个桶来协助排序。

每一个桶(bucket)代表一个区间范围,里面可以承载一个或多个元素。

  1. 桶排序的第1步,就是创建这些桶,并确定每一个桶的区间范围具体需要建立多少个桶,如何确定桶的区间范围,有很多种不同的方式。

  2. 我们这里创建的桶数量等于原始数列的元素数量,除最后一个桶只包含数列最大值外, 前面各个桶的区间按照比例来确定。
    区间跨度 = (最大值-最小值)/ (桶的数量 - 1)

  3. 假设有一个非整数数列如下:
    4.5,0.84,3.25,2.18,0.5
    在这里插入图片描述

  4. 遍历原始数列,把元素对号入座放入各个桶中。
    在这里插入图片描述

  5. 对每个桶内部的元素分别进行排序(显然,只有第1个桶需要排序)
    在这里插入图片描述

  6. 遍历所有的桶,输出所有元素

5.2 代码验证

package org.wanlong.sort;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

/**
 * @author wanlong
 * @version 1.0
 * @description:
 * @date 2023/6/3 19:01
 */
public class BucketSort {

    public static double[] bucketSort(double[] array) {
        double max = 0;
        double min = 0;
        //获得最大值和最小值之间的差
        for (int i = 0; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
            if (array[i] < min) {
                min = array[i];
            }
        }
        double d = max - min;
        //桶初始化
        int bucketNum = array.length;
        ArrayList<LinkedList<Double>> bucketList =
                new ArrayList<LinkedList<Double>>(bucketNum);
        for (int i = 0; i < bucketNum; i++) {
            bucketList.add(new LinkedList<Double>());
        }
        //将每个元素放入桶中
        for (int i = 0; i < array.length; i++) {
            int num = (int) ((array[i] - min) * (bucketNum - 1) / d);
            bucketList.get(num).add(array[i]);
        }
        //对每个桶内部进行排序
        for (int i = 0; i < bucketList.size(); i++) {
            Collections.sort(bucketList.get(i));
        }
        //输出全部元素
        double[] sortedArray = new double[array.length];
        int index = 0;
        for (LinkedList<Double> list : bucketList) {
            for (double element : list) {
                sortedArray[index] = element;
                index++;
            }
        }
        return sortedArray;
    }
}

5.3 测试验证

@Test
public void testBucketSort(){
    double[] nums = {4.12, 6.421, 0.0023, 3.0, 2.123, 8.122, 4.12, 10.09};
    for (double i : BucketSort.bucketSort(nums)) {
        System.out.println(i);
    }
}

6. 总结

6.1 稳定性概念介绍

如果大小相同的两个值在排序之前和排序之后的先后顺序不变,那就可以说这种排序算法是稳定的。
比如初始序列为 1,2,2,3,4
排序后序列:1,2,2,3,4
并且序列中两个2的元素顺序没有调整,则算法可认为是稳定的。反之,则算法是不稳定的

6.2 几种排序算法比较

在这里插入图片描述

6.3 适用场景

严格来说,算法的优劣不是绝对的,算法的性能取决于数据的大小,数据顺序是否已基本有序,存储空间限制等。下面列出来的是一些网上的经验总结,但是我们实际开发中,需要基于实际情况,来决定使用哪种排序算法

  1. 当数据规模较大时,应用速度最快的排序算法,可以考虑使用快速排序。当记录随机分布的时候,快速排序平均时间最短,但是会出现最坏的情况,这个时候的时间复杂度是O(n^2),且递归深度为n,所需的占空间为O(n)
  2. 堆排序不会出现快排那样最坏情况,且堆排序所需的辅助空间比快排要少,但是这两种算法都不是稳定的,要求排序时是稳定的,可以考虑用归并排序。
  3. 特殊的桶排序、基数排序都是稳定且高效的排序算法,但有一定的局限性:
    1、关键字可分解。
    2、记录的关键字位数较少,如果密集更好
    3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。
  4. 当文件的初态已经基本有序,可以用优化后的冒泡排序。

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

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

相关文章

HashMap 的底层原理和源码分析

tip&#xff1a;作为程序员一定学习编程之道&#xff0c;一定要对代码的编写有追求&#xff0c;不能实现就完事了。我们应该让自己写的代码更加优雅&#xff0c;即使这会费时费力。 推荐&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; 文章目录 一、HashMap…

【迷宫】地下迷宫游戏-微信小程序开发流程详解

可曾记得&#xff0c;小时候上学路边买的透明铅笔盒&#xff0c;里面内嵌了一个小球&#xff0c;它用重力可从起点滚动到终点&#xff0c;对小朋友来说是感觉有趣的&#xff0c;在这个游戏的基础上&#xff0c;弄一款微信小程序的迷宫探索游戏试试&#xff0c;在不同关卡的迷宫…

14 【Vuex】

1.理解 Vuex 1.1 Vuex 是什么 概念&#xff1a;专门在Vue中实现集中式状态&#xff08;数据&#xff09;管理的一个Vue插件&#xff0c;对Vue应用中多个组件的共享状态进行集中式的管理&#xff08;读/写&#xff09;&#xff0c;也是一种组件间通信的方式&#xff0c;且适用…

基于RK3399+FPGA的地面测试台多参数数据记录仪方案(一)软件设计及测试

完成了测试台软件分析和编程环境搭建后&#xff0c;接下来就是软件的编写。本章主要包括 软件窗口界面设计和功能代码实现。以某型号数据记录仪的工作需求为目标&#xff0c;根据测试 工作流程&#xff0c;以 Linux-Qt 为主要开发手段&#xff0c;设计一款功能完备、界面友…

Java8 Stream详解及结束操作方法使用示例(三)

结束操作是指结束 Stream 该如何处理的操作&#xff0c;并且会触发 Stream 的执行。下面是一些常用的结束操作方法。结束操作会对数据源进行遍历&#xff0c;因此是及早求值的。 Java8 Stream详解及中间操作方法使用示例&#xff08;一&#xff09; ​​​​​​​Java8 Strea…

三种经典博弈(取石子问题)

三种经典博弈 巴什博奕威佐夫博奕尼姆博奕 博弈是有一种很有意思的游戏&#xff0c;就是有物体若干堆&#xff0c;可以是火柴棍或是围棋子等等均可。两个人轮流从堆中取物体若干&#xff0c;规定最后取光物体者取胜。这是我国民间很古老的一个游戏&#xff0c;别看这游戏极其简…

一体化协同平台助力企业回归生产本质,创造价值

核心观点 单点工具的串联无法有效解决研效痛点问题&#xff0c;企业需要通过一体化协同平台提高端到端价值流动效率。一体化协同平台的价值是软件工程理念最大化落地、数字化研发管理、沉浸式研发体验。一体化协同平台集成需要评估闭环效率杠杆&#xff0c;确定集成边界和集成…

什么是关系模型? 关系模型的基本概念

关系模型由IBM公司研究员Edgar Frank Codd于1970年发表的论文中提出&#xff0c;经过多年的发展&#xff0c;已经成为目前最常用、最重要的模型之一。 在关系模型中有一些基本的概念&#xff0c;具体如下。 (1)关系(Relation)。关系一词与数学领域有关&#xff0c;它是集合基…

TensorRT 从7.2升级到8.5,改写plugin以适配新版本

前言 TensorRT是NVIDIA推出的一款高效深度学习模型推理框架&#xff0c;其包括了深度学习推理优化器和运行时&#xff0c;能够让深度学习推理应用拥有低时延和高吞吐的优点。 TensorRT的版本迭代速度非常快&#xff0c;很多之前写的plugin在版本升级后可能就没法直接使用&…

广州虚拟动力携数字人交互技术产品参展第十九届深圳文博会

2023年6月7-11日 深圳国际会展中心 第十九届深圳文博会正式举办&#xff01; 广州虚拟动力携数字人交互技术产品参展 诚邀您莅临粤港澳大湾区馆&#xff08;11号馆&#xff09; 广东参展团展位参观交流 数字技术&#xff0c;引领文化产业新发展 中国文化及相关文化产业的…

【Apache Pinot】Controller、Broker 和 Server 的概念和工作流程

背景 笔者最近一段时间使用 Apache Pinot 比较多&#xff0c;发现目前国内使用 Pinot 的很少&#xff0c;所以跟他相关的资料也比较少&#xff0c;本人在扩容&#xff0c;升级&#xff0c;部署&#xff0c;查询等方面操作有些许经验&#xff0c;知道其中有很多细节需要注意和规…

北邮22信通:实验七 三角波-方波(锯齿波-矩形波)发生器实验报告(着急验收的同学先看看,后续细节正在赶来中)

北邮22信通一枚~ 持续更新模电实验讲解 关注作者&#xff0c;解锁更多邮苑模电实验报告~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22信通——电子电路_青山如墨雨如画的博客-CSDN博客 实验七 三角波-方波&#xff08;锯齿波-矩形波&#xff09;发生器实验…

数据结构——树的概念、二叉树的概念

文章目录 引言1.树的概念1.1.树的其他相关概念 2.树的代码实现的结构2.1.树形结构的应用 3.二叉树的概念3.1.特殊二叉树的概念3.1.1.完全二叉树3.1.2.满二叉树 3.2.二叉树试题讲解3.2.1.试题一3.2.2.试题二3.2.3.试题三 4.二叉树的存储结构4.1.顺序结构存储4.2.链式结构存储 引…

重温经典:简读光干涉、衍射原理

如果您不是光学专业的&#xff0c;或者是文科生&#xff0c;那么您想到光的干涉和衍射第一反应应该是很多公式对不对&#xff1f;头好大是不是&#xff1f;好&#xff0c;那么今天我们就不用一个公式来重新解读光的干涉和衍射。 光&#xff0c;也叫电磁波&#xff0c;他的表现…

java并发编程:Java线程池详解

文章目录 为什么要用线程池线程池的原理ThreadPoolExecutor提供的构造方法ThreadPoolExecutor的策略线程池主要的任务处理流程ThreadPoolExecutor如何做到线程复用的&#xff1f; 四种常见的线程池newCachedThreadPoolnewFixedThreadPoolnewSingleThreadExecutornewScheduledTh…

web3.js获取导入

我们访问 https://github.com/ 我们搜索 web3.js 然后我们直接点击第一个进去 进入之后 往下拉 你会看到 它支持node项目的两种引入方式 这里 大家可以直接下载我的资源 https://download.csdn.net/download/weixin_45966674/87878737 下载好解压出来就会有一个 web3.min.js…

C#读写EM4205/4305/4469卡复制ID卡制做FDX-B动物标签源码

EM4305/EM4205卡是采用瑞士EM微电子公司工作频率为125kHz&#xff0c;具有读、写功能的非接触式RFID射频芯片&#xff0c;它具有功耗低、可提供多种数据传输速率和数据编码方法等特点&#xff0c;适合射频芯片ISO 11784/11785规范&#xff0c;该芯片被广泛应用于动物识别和跟踪…

《C++高级编程》读书笔记(五、六:面向对象设计设计可重用代码)

1、参考引用 C高级编程&#xff08;第4版&#xff0c;C17标准&#xff09;马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门&#xff0c;笔记链接如下 21天学通C读书笔记&#xff08;文章链接汇总&#xff09; 1. 过程化的思考方式 过程语言&#xff08;例如 C&#xff09;将…

玄奘文旅集团主办学习强企玄奘之路戈壁挑战赛在敦煌圆满完赛!

“学习改变命运&#xff0c;强企复兴中国”&#xff0c;2023年4月16日-20日&#xff0c;由玄奘文旅集团主办的“第23届26届学习强企玄奘之路戈壁挑战赛&#xff08;联赛&#xff09;”在千年古城敦煌圆满落幕&#xff01; 这是商界各行业的一次跨界融合&#xff0c;也是疫情过…

感性了解一下互斥和信号量

一、互斥的四个概念 我们把大家都能看到的资源叫做&#xff1a;公共资源 a、互斥&#xff1a;任何一个时刻&#xff0c;都只允许一个执行流在进行共享资源的访问——加锁 b、我们把任何一个时刻&#xff0c;都只允许一个执行流进行访问的共享资源叫做临界资源 c、临界资源需…