跟着Datawhale重学数据结构与算法(3)---排序算法

news2024/12/23 0:01:04

开源链接:【 教程地址 】【电子网站】
【写博客的目的是记录自己学习过程,方便自己复盘,专业课复习】

数组排序:

数组排序
冒泡排序
选择排序
插入排序
归并排序
希尔排序
快速排序
堆排序
计数排序
桶排序
基数排序

1.冒泡排序

1.1 算法思想
其实就跟线性代数里面的求逆序数的思想一样,ε=(1234),数当前数字与前面数字相比,有几个比它大的,然后加起来就是交换次数。
ε=(2143)=0+1+0+1=2,那么要成从小打到排列就要交换2次。
冒泡排序的本质也是两两交换,再用上面举例子,最坏的情况是ε=(4321)=0+1+2+3=6,我得交换6次。
step1:把最大的元素通过交换到正确的位置,我需要交换三次。
step2:把第二大元素通过俩俩交换到正确的位置,我需要交换俩次。
......
last step:最后剩的那个元素就是最小元素,无需排序,因此就完成了排序。
你可以推广到n个元素的排序。

可见,如果我们要用程序实现这个算法,就要使用双层循环,内层循环用来保证每次将一个元素放到对的位置,外层循环用来遍历整个数组。

1.2代码实现:
/* 冒泡排序 */
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}
void bubbleSort(vector<int> &nums) {
    for (int i = nums.size() - 1; i > 0; i--) {
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
            }
        }
    }
}
1.3 优化代码

我们想这样一个问题,假如还是上面那个逆序数,如果这个数组他没有排好,那么他一定存在一个元素会发生交换操作,那反过来说,(逆否命题也成立),如果元素没有发生交换,那么说明这个数组元素是排列好了的。因此,我们就可以用一个bool变量放在内层交换函数后面,如果发生了交换,那么继续,如果没发生交换,那么直接退出for循环。

/* 冒泡排序 */
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}
void bubbleSortWithFlag(vector<int> &nums) {
    for (int i = nums.size() - 1; i > 0; i--) {
        bool flag = false; // 初始化标志位
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
                flag = true;
		}
        }
        if (!flag)
            break; // 此轮“冒泡”未交换任何元素,直接跳出
    }
}

要掌握的一些排序算法的复杂度:
在这里插入图片描述

2.选择排序

2.1 算法思想
就是分为两个状态空间,一个是未排序的状态空间,一个是已经排序好的状态空间,原理也非常简单,就是
每轮从未排序的区间选择最小的元素放在排序好的空间的末尾。,直到未排序好的状态空间仅剩一个元素肯定为数组的最大值。
2.2 代码实现
void selectionSort(vector<int> &nums) {
    int n = nums.size();
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (nums[j] < nums[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            swap(nums[i], nums[minIndex]);
        }
    }
}

3.插入排序

3.1 算法思想
其实插入排序跟我们打扑克的时候整理扑克的时候很像,所以他的算法流程其实也很简单。
step1:先假设我们揭到的第一张牌为基准。
step2:从第二张牌开始,依次与基准进行比较将他插入到合适的位置,这样前两个元素就排好序了。
以此类推.......
依次将当前元素插入到已经排序好的子数组中。

插入排序的核心思想是不断地将当前元素插入到已经排序好的子数组中,以保持子数组的有序性。

3.2 代码实现:
void insertionSort(vector<int> &nums) {
    int n = nums.size();
    for (int i = 1; i < n; i++) {
        int key = nums[i]; // 当前待插入的元素
        int j = i - 1;//排序好的子序列个数

        // 将当前元素插入到已经排序好的子数组中的合适位置
        // 从后往前进行比较,不符合条件就插入到此位置中。
        while (j >= 0 && nums[j] > key) {
            nums[j + 1] = nums[j]; // 将比当前元素大的元素往后移动一个位置
            j--;
        }
        nums[j + 1] = key; // 插入当前元素到合适的位置
    }
}

4.归并排序

4.1 算法思想
分割数组:将待排序数组递归地分割成长度大约相等的两个子数组,直到每个子数组只包含一个元素为止。
合并数组:将两个有序的子数组合并成一个有序的数组。合并过程中,逐个比较两个子数组的元素,并将较小的元素放入临时数组中,直到两个子数组都被合并完毕。
重复步骤 1 和步骤 2:对分割后的子数组进行递归排序,并合并已经排序好的子数组,直到所有子数组都合并成一个完整的有序数组。

归并排序的时间复杂度为 O(n log n),其中 n 是待排序数组的大小。它的优点是稳定性好、适用于大型数据集和链表等数据结构。
我感觉这个方法很amazing。用到了递归的思想,分而治之,再合并。跟深度学习中的深度可分离卷积(Depthwise Convolution)或者说shuffleNet很像,其实就是分别卷积,然后再使用1x1卷积共享参数,保证信息的交流。算法是互通的,对于归并排序来说,先拆分着进行排序,然后再合并起来进行排序。
在这里插入图片描述

4.2 代码实现
// 合并两个有序数组
void merge(vector<int> &nums, int left, int mid, int right) {
    int n1 = mid - left + 1; // 左侧子数组的长度
    int n2 = right - mid; // 右侧子数组的长度
    // 创建临时数组用于存放合并后的结果
    vector<int> temp(n1 + n2);
    // 将左右两个子数组合并到临时数组中
    int i = left, j = mid + 1, k = 0;
    while (i <= mid && j <= right) {
        if (nums[i] <= nums[j]) {
            temp[k++] = nums[i++];
        } else {
            temp[k++] = nums[j++];
        }
    }

    // 将剩余的元素复制到临时数组中
    while (i <= mid) {
        temp[k++] = nums[i++];
    }
    while (j <= right) {
        temp[k++] = nums[j++];
    }

    // 将临时数组的元素复制回原数组中
    for (int p = 0; p < k; p++) {
        nums[left + p] = temp[p];
    }
}

// 归并排序
void mergeSort(vector<int> &nums, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(nums, left, mid); // 对左侧子数组进行排序
        mergeSort(nums, mid + 1, right); // 对右侧子数组进行排序
        merge(nums, left, mid, right); // 合并两个有序子数组
    }
}

5.希尔排序

5.1 算法思想

希尔排序是一种改进的插入排序算法,也称为缩小增量排序。它的基本思想是将待排序数组按一定步长进行分组,对每组进行插入排序,然后逐步减小步长,重复上述步骤,直到步长为 1,此时数组基本有序,最后再进行一次插入排序。

希尔排序的关键在于选择合适的步长序列,也就是元素间隔数gap,不同的步长序列会影响排序的效率。通常使用的步长序列有希尔原始提出的序列(逐步除以 2)、Hibbard 序列、Sedgewick 序列等。

我感觉哈其实有种层次聚类的意思,通过逐步减小步长来逐渐减小逆序对,来使得数组变得有序,来减少元素的移动次数。我记得层次聚类有种方法就是从大到小每次选择一个阈值进行聚类,逐渐减小阈值,直到所有样本都得到合理的分类即可。

5.2 代码实现
class Solution {
public:
    std::vector<int> shellSort(std::vector<int>& nums) {
        int size = nums.size();
        int gap = size / 2;

        while (gap > 0) {
            for (int i = gap; i < size; i++) {
                int temp = nums[i];
                int j = i;
                while (j >= gap && nums[j - gap] > temp) {
                    nums[j] = nums[j - gap];
                    j -= gap;
                }
                nums[j] = temp;
            }
            gap /= 2;
        }
        return nums;
    }

    std::vector<int> sortArray(std::vector<int>& nums) {
        return shellSort(nums);
    }
};

6.快速排序

6.1 算法思想
快速排序并不快,它使用分治策略来对一个数组进行排序。快速排序的基本思想是选择一个元素作为基准(pivot),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据要小,(左边比基准都小,右边比基准都大),然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。(由大到小)。

在这里插入图片描述

6.2 代码实现:
void quickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        // 选取基准元素为左端点
        int pivot = arr[low];
        int i = low;
        // 将小于基准的元素移动到分割点左侧,大于基准的元素移动到右侧
        for (int j = low + 1; j <= high; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr[i], arr[j]);
            }
        }
        // 基准元素交换到中间
        swap(arr[low], arr[i]);
        // 递归排序分割点左侧和右侧的子数组
        quickSort(arr, low, i - 1);
        quickSort(arr, i + 1, high);
    }
}

7.堆排序

7.1 算法理解

堆得结构分为大根堆和小根堆,是一个完全二叉树(从形式上讲它是个缺失的的三角形,但所缺失的部分一定是右下角某个连续的部
分,最后那一行可能不是完整的)。大小根堆从字面意思上就可以理解为树从根节点向下排列的顺序。
在这里插入图片描述在这里插入图片描述
采用顺序结构(数组)的形式来表示完全二,能够chong分利用存储空间。

7.2 代码实现
//使用数组来存储大顶堆(左孩子和哟孩子都小于父节点)
//维护堆的性质:如果父节点不满足大顶堆的性质,那就将孩子的最大值与父节点进行交换
//这样就满足了大顶堆的性质,复杂度O(logn)
建堆:(变成大顶堆),复杂度O(n)

//SO:首先将数列变成大顶堆的形式,然后自下而上,从底部与根节点进行交换,交换后底部元素
//从树中删除,删除完后放在数列的最右端,更新此时的二叉树和数组,然后继续将第二个元素与根节点
//元素进行交换,继续....直到树只剩一个元素,操作完成。
void heapify(int arr[], int n, int i){
/*数组,数组长度,待维护节点下标*/
int largest = i;
int lson = i*2 +1;
int rson = i*2 +2;

if(lson < n && arr[largest] < arr[lson])
	largest = lson;
if(rson < n && arr[largest] < arr[rson])
	largest = rson;
if (largest !=i)
{
	swap(&arr[largest],&arr[i]);
	heapify(arr,n,largest);
}
}
//堆排序入口
void heap_sort(int arr[], int n){
	int i;
	//建堆
	for(i=n/2-1;i>=0;i--)
		heapify(arr,n,i);
	//排序
	for(i=n-1;i>0;i--)
	{
		swap(&arr[i],&arr[0]);
		heapify(arr,i,0);
	}
}

稳定性:不稳定

8. 计数排序

8.1算法原理

通过计数而不是比较来进行排序,适用于范围比较小的整数序列
原始数组->计数数组->累计数组->最终结果
在这里插入图片描述

8.2 代码实现:
void counting_sort(int arr[], int len)
{
    if (len < 1) return;

    // 寻找最大的元素
    int max = arr[0];
    for (size_t i = 1; i < len; i++)
        if (arr[i] > max) max = arr[i];

    // 分配一个长度为max+1的数组存储计数,并初始化为0
    int *count = (int *)malloc(sizeof(int) * (max + 1));
    memset(count, 0, sizeof(int) * (max + 1));

    // 计数
    for (size_t i = 0; i < len; i++)
        count[arr[i]]++;

    // 统计计数的累计值
    for (size_t i = 1; i < max + 1; i++)
        count[i] += count[i - 1];

    // 创建一个临时数组保存结果
    int *output = (int *)malloc(sizeof(int) * len);

    // 将元素放到正确的位置上
    for (size_t i = 0; i < len; i++)
    {
        output[count[arr[i]] - 1] = arr[i];
        count[arr[i]]--;
    }

    // 将结果复制回原数组
    for (size_t i = 0; i < len; i++)
        arr[i] = output[i];
}

9.桶排序

9.1 算法原理

将待排序的元素分到有限数量的桶中,每个桶再分别进行排序,最后将各个桶中的元素合并得到排序结果。桶排序的核心思想是将一组数据分割成一定数量的桶,然后对每个桶中的数据进行排序,最后将所有桶中的数据按照顺序合并起来。首先,初始化一个足够数量的空桶。然后遍历待排序的数组,将每个元素放入相应的桶中;对每个非空的桶中的元素进行排序(可以使用其他排序算法,如插入排序);按照桶的顺序将所有非空桶中的元素合并得到排序结果。

9.2 代码实现
// 桶排序函数
void bucketSort(vector<float>& arr) {
    int n = arr.size();
    // 创建足够数量的空桶
    vector<vector<float>> buckets(n);

    // 将每个元素放入相应的桶中
    for (int i = 0; i < n; ++i) {
        int bucket_index = n * arr[i];
        buckets[bucket_index].push_back(arr[i]);
    }

    // 对每个非空桶中的元素进行排序(这里使用插入排序)
    for (int i = 0; i < n; ++i) {
        sort(buckets[i].begin(), buckets[i].end());
    }

    // 合并所有非空桶中的元素得到排序结果
    int index = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < buckets[i].size(); ++j) {
            arr[index++] = buckets[i][j];
        }
    }
}

10.基数排序

10.1 算法思想

基数排序是一种非比较性的排序算法,它按照数字的位数来进行排序。基数排序可以分为 LSD最低位优先(Least Significant Digit)和 MSD最高位优先(Most Significant Digit)两种实现方式。下面我们来介绍 LSD 基数排序的算法思想和实现。

LSD 基数排序的基本思想是从数字的最低位(个位)开始,依次对数字进行排序,直到最高位(最高位数的位数)。在每一轮排序过程中,对所有数字根据当前位上的数字进行桶排序。通过这样的逐位排序过程,最终可以得到排好序的结果。
这个方法跟人类很像,我们通常来比较两个事物所有的特征(差异)来区分,LSD这个就相当于先从个位进行排序,个位排完后,在向上观察最大的特征差异,然后到第一位的比较。

10.2 代码实现
//代码也非常简单
void radixSort(vector<int>& arr) {
    // 获取数组中的最大值
    int max_num = *max_element(arr.begin(), arr.end());

    // LSD 基数排序
    for (int digit = 1; max_num / digit > 0; digit *= 10) {
        // 创建 10 个桶,用于存放当前位上的数字
        vector<vector<int>> buckets(10);

        // 将数组中的元素按照当前位上的数字放入相应的桶中
        for (int num : arr) {
            int digit_value = getDigit(num, digit);
            buckets[digit_value].push_back(num);
        }

        // 从桶中按顺序取出元素,替换原数组中的元素
        int index = 0;
        for (auto& bucket : buckets) {
        // auto 是为了让编译器自动推导出 bucket 的类型
            for (int num : bucket) {
                arr[index++] = num;
            }
            bucket.clear();
        }
    }
}

Leetcode之旅~:

【1】LCR.164破解闯关密码

直接偷懒,写到这累的不行了,我直接sort(快速排序)+拼接字符串。

class Solution {
public:
    // 自定义比较函数,用于确定两个数字拼接后的结果的大小
    static bool compare(const int& x, const int& y) {
        string xy = to_string(x) + to_string(y);
        string yx = to_string(y) + to_string(x);
        return xy < yx;
    }

    string crackPassword(vector<int>& password) {
        // 将数组按照自定义的比较函数排序
        sort(password.begin(), password.end(), compare);
        
        // 将排序后的数组拼接成字符串
        string result = "";
        for (int num : password) {
            result += to_string(num);
        }
        return result;
    }
};

【2】移动0

直接双指针遍历,移动非零元素,然后遍历完后,直接left后面全部赋0.

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int left = 0; // left 指针
        int n = nums.size();
        
        // 遍历数组
        for (int right = 0; right < n; right++) {
            if (nums[right] != 0) {
                // 将非零元素放到 left 指针位置
                nums[left++] = nums[right];
            }
        }
        
        // 将 left 指针后面的所有元素置为 0
        while (left < n) {
            nums[left++] = 0;
        }
    }
};

【3】[数组排序](https://leetcode.cn/problems/sort-an-array/)

直接用前面冒泡排序的代码-》超出时间限制emmmm
快速排序也超出时间限制了,这怎么搞,我想想,数组太大了,那就小规模用插入排序更快一些,大数组用快速。

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
    
    // 快速排序函数
    void quickSort(vector<int>& nums, int low, int high) {
        if (low < high) {
            // 对小规模数组(<=10)使用插入排序
            if (high - low + 1 <= 10) {
                insertionSort(nums, low, high);
                return;
            }
            
            // 随机选择基准元素
            int pivotIndex = rand() % (high - low + 1) + low;
            swap(nums[pivotIndex], nums[high]);
            pivotIndex = partition(nums, low, high);
            quickSort(nums, low, pivotIndex - 1);
            quickSort(nums, pivotIndex + 1, high);
        }
    }
    
    // 划分函数,返回分割点的位置
    int partition(vector<int>& nums, int low, int high) {
        int pivot = nums[high]; // 基准元素
        int i = low - 1; // 定义分割点的位置

        for (int j = low; j < high; j++) {
            if (nums[j] < pivot) {
                i++;
                swap(nums[i], nums[j]);
            }
        }
        
        swap(nums[i + 1], nums[high]); // 将基准元素放到正确的位置
        return i + 1;
    }
    
    // 插入排序函数
    void insertionSort(vector<int>& nums, int low, int high) {
        for (int i = low + 1; i <= high; i++) {
            int key = nums[i];
            int j = i - 1;
            while (j >= low && nums[j] > key) {
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = key;
        }
    }
};

通过了,累了,不敲了

参考资料:

[1] 【教程地址 】[电子网站]
[2] Hello 算法教程
[3] https://blog.csdn.net/qq_39181839/article/details/109478094
[4] https://blog.csdn.net/u010452388/article/details/81283998

感谢:DataWhale社区

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

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

相关文章

微信小程序-------模板与配置

能够使用 WXML 模板语法渲染页面结构能够使用 WXSS 样式美化页面结构能够使用 app.json 对小程序进行全局性配置能够使用 page.json 对小程序页面进行个性化配置能够知道如何发起网络数据请求 一.WXML 模板语法 数据绑定 1. 数据绑定的基本原则 ① 在 data 中定义数据 ② 在…

软考 系统架构设计师系列知识点之软件可靠性基础知识(5)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之软件可靠性基础知识&#xff08;4&#xff09; 所属章节&#xff1a; 第9章. 软件可靠性基础知识 第1节 软件可靠性基本概念 9.1.3 可靠性目标 前文定量分析软件的可靠性时&#xff0c;使用失效强度来表示软件缺陷对…

C/C++开发,opencv-ml库学习,随机森林(RTrees)应用

目录 一、随机森林算法 1.1 算法简介 1.2 OpenCV-随机森林&#xff08;Random Forest&#xff09; 二、cv::ml::RTrees应用 2.2 RTrees应用 2.2 程序编译 2.3 main.cpp全代码 一、随机森林算法 1.1 算法简介 随机森林算法是一种集成学习&#xff08;Ensemble Learning&a…

SVN泄露+HG泄露

一、SVN泄露 &#xff08;1&#xff09;什么是SVN 一种开放源代码版本控制系统&#xff0c;用于管理项目的文件和代码 SVN能做什么 1.共享资源并修改 2.记录资源变更&#xff0c;可恢复到任意一个修改点 漏洞描述 版本控制系统中的一个安全漏洞&#xff0c;允许未授权的…

xgp加速器免费 微软商店xgp用什么加速器

2001年11月14日深夜&#xff0c;比尔盖茨亲自来到时代广场&#xff0c;在午夜时分将第一台Xbox交给了来自新泽西的20岁年轻人爱德华格拉克曼&#xff0c;后者在回忆中说&#xff1a;“比尔盖茨就是上帝。”性能超越顶级PC的Xbox让他们趋之若鹜。2000年3月10日&#xff0c;微软宣…

2024年vue 开发环境 Node.js于win10环境下的安装

2024年vue 开发环境 Node.js于win10环境下的安装 导航 文章目录 2024年vue 开发环境 Node.js于win10环境下的安装导航一、下载node.js二、安装node.js三、测试(一)四、环境配置五、测试(二)六、安装淘宝镜像七、安装vue脚手架 一、下载node.js Node.js 官方网站下载&#xff…

hive搭建完整教学

目录 简介准备工作安装步骤&#xff08;一&#xff09;、下载hive包并解压到指定目录下&#xff08;二&#xff09;、设置环境变量&#xff08;三&#xff09;、下载MySQL驱动包到hive的lib目录下&#xff08;四&#xff09;、将hadoop的guava包拷贝到hive&#xff08;五&#…

揭露 FileSystem 引起的线上 JVM 内存溢出问题

作者&#xff1a;来自 vivo 互联网大数据团队-Ye Jidong 本文主要介绍了由FileSystem类引起的一次线上内存泄漏导致内存溢出的问题分析解决全过程。 内存泄漏定义&#xff08;memory leak&#xff09;&#xff1a;一个不再被程序使用的对象或变量还在内存中占有存储空间&#x…

区块链基础——区块链应用架构概览

目录 区块链应用架构概览&#xff1a; 1、区块链技术回顾 1.1、以太坊结点结构 1.2、多种应用场景 2、区块链应用架构概览 2.1、传统的Web2 应用程序架构 2.2、Web3 应用程序架构——最简架构 2.3、Web3 应用程序架构——前端web3.js ether.js 2.4、Web3 应用程序架构—…

无人零售与传统便利店的竞争优势

无人零售与传统便利店的竞争优势 成本控制 • 无人零售 显著降低了人力成本&#xff0c;无需支付店员薪资和相关福利&#xff0c;且通过智能化管理减少能源消耗与维护费用&#xff0c;尤其在高租金和高人流区域效益突出。 • 传统便利店 则承担较高的人员开支&#xff0c;…

如何申请免费SSL证书,把网站升级成HTTPS

HTTPS&#xff08;Hyper Text Transfer Protocol Secure&#xff09;是一种用于安全数据传输的网络协议&#xff0c;它可以有效地保护网站和用户之间的通信安全。然而&#xff0c;要使一个网站从HTTP升级到HTTPS&#xff0c;就需要一个SSL证书。那么&#xff0c;如何申请免费的…

java8 Stream流常用方法(持续更新中...)

java8 Stream流常用方法 1.过滤数据中年龄大于等于十八的学生2.获取对象中其中的一个字段并添加到集合(以学生姓名&#xff08;name&#xff09;为例)3.获取对象中其中的一个字段并转为其他数据类型最后添加到集合(以学生性别&#xff08;sex&#xff09;为例&#xff0c;将Str…

Django框架之Django安装与使用

一、Django框架下载 首先我们需要先确定好自己电脑上的python解释器环境&#xff0c;否则会导致后面项目所需要的库安装不了以及项目无法运行的问题。 要下载Django并开始使用它&#xff0c;你可以按照以下步骤进行&#xff1a; 1、安装Python 首先&#xff0c;确保你的计算…

Oracle 监控 SQL 精选 (一)

Oracle数据库的监控通常涉及性能、空间、会话、对象、备份、安全等多个层面。 有效的监控可以帮助 DBA 及时发现和解决问题&#xff0c;提高数据库的稳定性和性能&#xff0c;保障企业的数据安全和业务连续性。 常用的监控指标有&#xff1a; 性能指标&#xff1a; 查询响应时间…

vue+springboot项目的登录验证码(JAVA自带)

后台springboot CaptureController package com.example.controller;import com.example.common.Result; import com.example.service.AuthCodeService; import com.example.utils.CodeUtils; import lombok.SneakyThrows; import org.apache.ibatis.annotations.Param; impo…

ELF 1技术贴|CAN接口浅析:从原理到对测

引言 在当今智能化、网络化的时代&#xff0c;各种电子设备间的高效通信成为了技术发展的关键。而控制器局域网络&#xff08;Controller Area Network&#xff0c;简称CAN&#xff09;&#xff0c;作为嵌入式系统中不可或缺的通信协议&#xff0c;正扮演着链接桥梁的重要角色…

大厂常见算法50题-用两个栈实现队列

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注, 一起巧‘背’算法! 文章目录 题目解法总结 题目 解法 先搞清队列与栈的特点&#xff1a;队列先进先出&#xff0c;栈先进后出两个栈的分工&#xff1a;栈A入数据&#xff0c;栈B出数据需要保证取数…

COOIS 生产订单显示系统增强

需求说明&#xff1a;订单系统显示页面新增批量打印功能 增强点&#xff1a;CL_COIS_DISP_LIST_NAVIGATION -->TOOLBAR方法中新增隐式增强添加自定义打印按钮 增强点&#xff1a;BADI-->WORKORDER_INFOSYSTEM新增增强实施 实现位置&#xff1a;IF_EX_WORKORDER_INFOSYS…

【Leetcode】377. 组合总和 Ⅳ

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 给你一个由 不同 整数组成的数组 n u m s nums nums&#xff0c;和一个目标整数 t a r g e t target target 。请你从 n u m s nums nums 中找出并返回总和为 t a r g e t targ…

【STM32+HAL+Proteus】系列学习教程---ADC(查询、中断、DMA模式下的电压采集)

实现目标 1、学会STM32CubeMX软件关于ADC的配置 2、掌握ADC三种模式&#xff08;查询、中断、DMA&#xff09;编程 3、具体目标&#xff1a;1、将开发板单片机采集到的电压值上传至上位机串口调试助手显示。 一、ADC 概述 1、什么是ADC? ADC&#xff08;Analog to Digit…