常见排序算法解析

在这里插入图片描述

芝兰生于深林,不以无人而不芳;君子修道立德,不为穷困而改节

文章目录

  • 插入排序
    • 直接插入排序
    • 希尔排序
  • 选择排序
    • 直接选择排序
    • 堆排序
  • 交换排序
    • 冒泡排序
    • 快速排序
      • 优化
      • 挖坑法
      • 前后指针法
      • 非递归版
  • 归并排序
      • 递归
      • 非递归
  • 总结


插入排序

插入排序(Insertion Sort)是一种简单直观的排序算法,它的工作原理类似于我们打扑克牌时对手中的牌进行排序的方式。插入排序的基本思想是将一个待排序的元素插入到已经排好序的部分的适当位置,使得插入后的序列仍然有序。

在这里插入图片描述

插入排序的基本步骤如下:

  1. 从第一个元素开始,认为第一个元素已经是排好序的部分
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5,直到所有元素都排序完成

插入排序是一种稳定的排序算法,其时间复杂度为 O(n2),其中 n 是待排序元素的数量。最好情况下,如果数组已经有序,时间复杂度为 O(n);最坏情况下,如果数组逆序,时间复杂度为 O(n^2)。其空间复杂度为 O(1),因为它仅使用了常量级的额外空间。

插入排序在小规模数据或基本有序数据的排序中性能较好,但在大规模数据排序中通常不如快速排序、归并排序等高级排序算法。然而,在某些特定情况下,插入排序可能会比其他高级算法更加有效,因此它仍然是一种重要的排序方法之一。

直接插入排序

直接插入排序是一种简单直观的排序算法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增加1的有序表。直接插入排序的实现过程通常如下:

  1. 初始状态:假设待排序的元素存储在一个数组中,初始时将第一个元素看作是已排序的序列,其余元素看作是未排序的序列。
  2. 遍历未排序序列:从第二个元素开始,依次将未排序的元素插入到已排序的序列中。
  3. 插入操作:对于未排序序列中的每个元素,依次与已排序序列中的元素比较,找到合适的位置并插入,使得已排序序列保持有序。
  4. 重复步骤2和3,直到所有元素都插入到已排序序列中,排序完成。
bool insertSort(std::vector<int>& arr, int gap = 1)
{int n = arr.size();for (int i = 1; i < n; ++i) {int key = arr[i];int j = i - 1;// 将 arr[0..i-1] 中比 key 大的元素都向后移动一个位置while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];--j;}// 将 key 插入到合适的位置arr[j + 1] = key;}return true;
}

直接插入排序的时间复杂度取决于输入数据的初始状态。

  • 最好情况: 如果输入数组已经是有序的,那么每次比较只需要比较一次,时间复杂度为 (O(n))。
  • 最坏情况: 如果输入数组是逆序排列的,那么每次插入一个新元素时都需要比较和移动前面已排序部分的所有元素,时间复杂度为 (O(n2))。
  • 平均情况: 平均时间复杂度也是O(n2),因为在平均情况下,每次插入新元素的位置不确定,需要移动的元素个数与已排序部分的长度相关。

直接插入排序的空间复杂度主要取决于需要排序的数组本身,以及一些额外的辅助空间。

  • 原地排序: 直接插入排序是一种原地排序算法,它不需要额外的辅助空间,空间复杂度为 (O(1))。
  • 除数组本身外的空间: 在进行直接插入排序时,只需要使用常量级别的额外空间来存储临时变量和索引,因此空间复杂度较低。

综上所述,直接插入排序的时间复杂度主要取决于输入数据的初始状态,而空间复杂度相对较低,是一种简单但不够高效的排序算法。

希尔排序

希尔排序(Shell Sort)是一种基于插入排序的排序算法,它是由Donald Shell在1959年提出的,也称为“缩小增量排序”。在这里插入图片描述

希尔排序的基本思想是将原始数组分成若干个子序列,在每个子序列中进行插入排序,然后逐步缩小增量(间隔),最终完成对整个数组的排序。希尔排序的关键在于选择增量序列,并且在每轮排序中,将数组中相隔增量的元素分成一组,对各组进行插入排序。

  • 增量序列的选择对希尔排序的性能有较大影响,常用的增量序列包括希尔增量序列、Hibbard增量序列、Sedgewick增量序列等。
  • 希尔排序是不稳定排序算法,因为在排序过程中存在跨越多个子序列的元素交换。

希尔排序的时间复杂度取决于增量序列的选择,最坏情况下的时间复杂度为 O(n2),平均情况下的时间复杂度介于 (O(nlog2n)) 和 O(n2) 之间。希尔排序是一种原地排序算法,它的空间复杂度为 (O(1)),即常数级别的额外空间。

希尔排序相比直接插入排序效率高的原因主要是因为它利用了直接插入排序的优点,并通过分组插入的方式,使得数组在初始阶段就变得部分有序。

  1. 间隔插入: 希尔排序将整个数组按照一定的间隔分成若干个子序列,然后对每个子序列进行插入排序。通过这种方式,希尔排序可以在一定程度上解决直接插入排序中逆序对过多的问题,因为通过较大的间隔,可以更快地将较小的元素移到数组的前面。
  2. 逐步优化: 希尔排序通过不断减小间隔,逐步缩小子序列的规模,最终实现整个数组的基本有序。这种分阶段的逐步优化可以使得数组在排序过程中逐渐趋于有序状态,从而减少了每次插入操作的移动次数。
  3. 时间复杂度优化: 希尔排序的时间复杂度相比直接插入排序有所降低。尽管希尔排序的最坏时间复杂度仍然是 O(n2),但在一般情况下,由于数组在初始阶段就变得部分有序,因此实际的比较和移动操作会减少,从而提高了排序的效率。
算法步骤说明
确定增量序列选择一个增量序列,通常是 n/2, n/4, n/8,…, 1。增量序列的选择会影响到希尔排序的性能。
按增量进行分组根据选择的增量序列,将待排序的数组分成若干个子序列。
对各个子序列进行插入排序对每个子序列进行插入排序,这里的插入排序是根据增量值进行的,而不是对整个数组进行插入排序。
逐步减小增量重复步骤 2 和 3,直到增量为 1。最后一次排序使用增量为 1 的插入排序,这时候数组已经基本有序,插入排序的效率会比较高。
bool shellSort(std::vector<int>& arr)
{int gap = arr.size();while (gap){for (int i = 1; i < arr.size(); i += gap){int j = i - 1, key = arr[i];while (j >= 0 && arr[j] > key){arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}gap /= 2;}return true;
}

综上所述,希尔排序是一种简单而有效的排序算法,适用于中等大小的数据集合。尽管其时间复杂度并不是最优的,但在实际应用中,希尔排序的性能仍然比较可观,特别是对于某些特定的增量序列。

选择排序

直接选择排序

直接选择排序(Selection Sort)是一种简单直观的排序算法,它的思想是在未排序序列中找到最小(或最大)元素,将其放置在已排序序列的末尾,然后将未排序序列的第一个元素与已排序序列的最后一个元素交换位置,如此循环,直到整个数组排序完成。

在这里插入图片描述

  1. 从待排序序列中选择最小(或最大)的元素,记下其索引。
  2. 将最小(或最大)元素与待排序序列的第一个元素交换位置。
  3. 在剩余的待排序序列中继续执行步骤1和步骤2,直到所有元素都被排序。

直接选择排序的时间复杂度是 (O(n^2)),空间复杂度是 (O(1))。它是一种不稳定的排序算法,因为在选择过程中可能会改变相同元素的相对位置。

bool selectSort(std::vector<int>& arr)
{for (int i = 0; i < arr.size(); i++){int minNum = i;for (int j = i + 1; j < arr.size(); j++){if (arr[j] < arr[minNum]) minNum = j;}std::swap(arr[minNum], arr[i]);}return true;
}

堆排序

堆排序(Heap Sort)是一种基于完全二叉树(堆)的排序算法,它利用了堆的性质进行排序。堆是一种特殊的树形数据结构,具有以下特点:

  1. 在堆中,每个节点的值都大于等于(最大堆)或小于等于(最小堆)其子节点的值。
  2. 堆是一个完全二叉树,即除了最底层之外,其他层都被完全填满,且最底层从左到右填入。

堆排序的基本思想是:

  1. 将待排序的序列构建成一个堆(通常是最大堆)。
  2. 交换堆顶元素(最大值)和堆的最后一个元素,然后重新调整堆。
  3. 重复步骤2,直到整个序列有序。

在这里插入图片描述

堆排序的步骤:

  1. 构建堆:从最后一个非叶子节点开始,依次向前构建最大堆。
  2. 交换堆顶元素和堆的最后一个元素,并调整堆。
  3. 重复步骤2,直到堆中只剩一个元素。

堆排序是一种原地排序算法,它的时间复杂度是 (O(n log n)),空间复杂度是 (O(1))。堆排序是一种不稳定的排序算法,因为在调整堆的过程中可能改变相同元素的相对顺序。

void adjustDown(std::vector<int>& arr, int cur, int len)
{int leftChild = cur * 2 + 1;while (leftChild < len) {int rightChild = leftChild + 1;int maxChild = (rightChild < len && arr[rightChild] > arr[leftChild]) ? rightChild : leftChild;if (arr[maxChild] > arr[cur]){std::swap(arr[maxChild], arr[cur]);cur = maxChild;leftChild = cur * 2 + 1;}else break;}
}void createMaxHeap(std::vector<int>& arr)
{int len = arr.size();for (int i = len / 2; i >= 0; i--){adjustDown(arr, i, len);}
}bool heapSort(std::vector<int>& arr)
{createMaxHeap(arr);for (int i = arr.size() - 1; i > 0; i--){std::swap(arr[0], arr[i]);adjustDown(arr, 0, i);}return true;
}

交换排序

冒泡排序

冒泡排序是一种简单直观的排序算法,它的基本思想是通过交换相邻元素多次对数组进行遍历,每次遍历都将最大(或最小)的元素交换到数组的末尾(或开始)。这个过程类似于气泡在水中上升的过程,故称为冒泡排序。

在这里插入图片描述

  1. 从数组的第一个元素开始,依次比较相邻的两个元素,如果它们的顺序不正确(比如前面的元素大于后面的元素),则交换它们的位置。
  2. 继续比较相邻的元素,直到整个数组被遍历一次。
  3. 重复上述步骤,每次遍历都将最大(或最小)的元素交换到数组的末尾(或开始),并缩小待排序数组的范围。

冒泡排序的时间复杂度取决于数组的初始状态,最好情况下(数组已经有序)时间复杂度为 (O(n)),最坏情况下(数组逆序)时间复杂度为 (O(n^2)),平均时间复杂度也为 (O(n^2))。冒泡排序是一种原地排序算法,只需要常数级别的额外空间来存储临时变量,因此空间复杂度为 (O(1))。

冒泡排序是一种稳定的排序算法。在相邻元素相等时,不会改变它们的相对顺序,因此是稳定的。冒泡排序的实现较为简单,但由于其时间复杂度较高,在实际应用中并不常见,通常被更高效的排序算法所取代,比如快速排序、归并排序等。

bool bubbleSort(std::vector<int>& arr)
{for (int i = 0; i < arr.size(); i++){for (int j = 1; j < arr.size(); j++){if (arr[j - 1] > arr[j]) std::swap(arr[j - 1], arr[j]);}}return true;
}

快速排序

快速排序(Quick Sort)是一种高效的排序算法,属于交换排序的一种。它的基本思想是通过分治法(Divide and Conquer)来实现排序。

在这里插入图片描述

  1. 选择基准值: 从数组中选择一个元素作为基准值(pivot)。
  2. 分区操作: 将数组中小于基准值的元素放置在基准值的左侧,大于基准值的元素放置在右侧,基准值则放置在正确的位置上。
  3. 递归排序: 对基准值左右两侧的子数组进行递归排序,直到整个数组有序。
实现思路说明
选择基准值通常选择数组中的第一个元素、最后一个元素或者随机一个元素作为基准值。
分区操作使用两个指针,一个指向数组的起始位置,一个指向数组的末尾位置。通过交替移动这两个指针,并交换元素,将数组分为两个部分。
递归排序对分区后的左右子数组分别进行递归排序。

快速排序的平均时间复杂度为 (O(nlogn)),最坏情况下的时间复杂度为 (O(n^2)),最好情况下的时间复杂度也为 (O(nlog n))。快速排序的性能很大程度上取决于选取的基准值。快速排序是一种原地排序算法,它的空间复杂度主要取决于递归调用栈的深度,平均空间复杂度为 (O(log n)),最坏情况下空间复杂度为 (O(n))。

快速排序是一种不稳定的排序算法,因为在分区操作过程中,相同元素的相对位置可能会发生变化。

	int sort(std::vector<int>& arr, int begin, int end){int key = begin;while (begin < end){while (begin < end && arr[end] >= arr[key]) end--;while (begin < end && arr[begin] <= arr[key]) begin++;std::swap(arr[begin], arr[end]);}std::swap(arr[key], arr[begin]);return begin;}void _quikSort(std::vector<int>& arr, int begin, int end){if (begin >= end) return;int key = sort(arr, begin, end);_quikSort(arr, begin, key - 1);_quikSort(arr, key + 1, end);}bool quikSort(std::vector<int>& arr){_quikSort(arr, 0, arr.size() - 1);return true;}

优化

在快速排序中使用三数取中和小区间直接插入排序的优化,可以提高快速排序的性能,尤其是在处理小规模数据时。三数取中法是一种选择基准值的方法,它可以避免选取到最坏情况下的基准值,从而提高快速排序的性能。具体步骤如下:

  1. 从待排序数组的起始、中间和末尾位置选择三个元素。
  2. 对这三个元素进行排序,并取其中间位置的元素作为基准值。

在快速排序中,当待排序的子数组规模较小时,直接插入排序的性能可能比快速排序更好。因此,可以在快速排序中添加一个判断条件,当子数组的规模小于某个阈值时,使用直接插入排序对子数组进行排序。

以下是结合三数取中和小区间直接插入排序优化的快速排序算法的代码:

	int getKey(std::vector<int>& arr, int begin, int end){int mid = (begin + end) / 2;if (arr[end] > arr[begin]){if (arr[mid] > arr[end]) return end;else if (arr[mid] < arr[begin]) return begin;else return mid;}else{if (arr[mid] < arr[end]) return end;else if (arr[mid] > arr[begin]) return begin;else return mid;}}int sort(std::vector<int>& arr, int begin, int end){int cur = getKey(arr, begin, end);std::swap(arr[cur], arr[begin]);int key = begin;while (begin < end){while (begin < end && arr[end] >= arr[key]) end--;while (begin < end && arr[begin] <= arr[key]) begin++;std::swap(arr[begin], arr[end]);}std::swap(arr[key], arr[begin]);return begin;}void _quikSort(std::vector<int>& arr, int begin, int end){if (begin >= end) return;if (end - begin < 20){insertSort(arr);}else{int key = sort(arr, begin, end);_quikSort(arr, begin, key - 1);_quikSort(arr, key + 1, end);}}bool quikSort(std::vector<int>& arr){_quikSort(arr, 0, arr.size() - 1);return true;}

在实际代码中,要根据具体情况选择合适的阈值来确定是否使用直接插入排序,以及合适的阈值大小。此外,三数取中法的选择也可以根据实际情况进行优化和改进。

挖坑法

挖坑法也是快速排序的一种优化方法,用于在分区操作中避免不必要的元素交换。其基本思想是通过找到一个“坑”,将基准值放入坑中,然后利用坑的位置来实现元素的移动和交换,从而实现分区操作。

在这里插入图片描述

  1. 选择基准值:从待排序数组中选择一个元素作为基准值。
  2. 分区操作:
    • 将基准值保存到一个临时变量中,此时数组中留下了一个“坑”。
    • 从数组的另一端开始(通常是右侧),找到一个小于基准值的元素,并将其填入坑中。
    • 然后从数组的左侧开始,找到一个大于基准值的元素,并将其放入刚才填入的坑中。
    • 重复上述过程,直到左右指针相遇。
    • 最后将基准值放入最后一个坑中,此时基准值左侧的元素都小于基准值,右侧的元素都大于基准值。
  3. 递归排序:对基准值左右两侧的子数组进行递归排序。

挖坑法的优点是减少了元素的交换次数,因为每次交换都需要消耗额外的时间,通过找到“坑”来避免了多余的交换操作,提高了快速排序的效率。

int potholingSort(std::vector<int>& arr, int left, int right)
{int cur = left, val = arr[left];while (left < right){while (left < right && arr[right] >= val) right--;arr[cur] = arr[right];cur = right;while (left < right && arr[left] <= val) left++;arr[cur] = arr[left];cur = left;}arr[left] = val;return left;
}

前后指针法

int twopointSort(std::vector<int>& arr, int left, int right)
{int prev = left, cur = prev + 1, key = left;while (cur <= right){if (arr[cur] < arr[key]) std::swap(arr[++prev], arr[cur]);cur++;}std::swap(arr[key], arr[prev]);return prev;
}

在这里插入图片描述

  1. 函数定义

    • twopointSort 函数接受一个整数向量 arr,以及数组的左右边界 leftright
    • 函数返回的是基准值最终的位置。
  2. 前后指针

    • 在该算法中,使用了两个指针 prevcur,分别表示前后指针。
    • prev 初始化为 left,表示指向数组的最左边界。
    • cur 初始化为 prev + 1,表示指向 prev 右边的第一个元素。
  3. 分区操作

    • 使用循环遍历数组,从 cur 开始向右移动,直到达到数组的右边界 right
    • 在遍历过程中,如果当前元素 arr[cur] 小于基准值 arr[key](这里的 key 是选择的基准值的索引),则将其与 prev 右边的元素交换,并将 prev 指针向右移动一位。
    • 最后,将基准值 arr[key]arr[prev] 交换,将基准值放在正确的位置上。
  4. 返回值

    • 返回的是基准值最终的位置 prev

这种前后指针的快速排序思想在于通过两个指针的移动,将小于基准值的元素放在基准值的左边,大于基准值的元素放在基准值的右边。最终,将基准值放在正确的位置上,使得左侧的元素都小于等于基准值,右侧的元素都大于等于基准值。

非递归版

非递归版本的快速排序可以使用栈来模拟递归调用的过程,以避免使用递归函数带来的额外开销。

void sort(std::vector<int>& arr, int left, int right)
{if (left >= right) return;if (right - left < 20) insertSort(arr);else{std::stack<std::vector<int>> st;st.push({ left, right });while (st.size()){auto tmp = st.top();st.pop();int begin = tmp[0], end = tmp[1];int key = twopointSort(arr, begin, end);if(begin < key - 1) st.push({ begin, key - 1 });if(key + 1 < end) st.push({ key + 1, end });}}
}

在这个非递归版本的快速排序中,使用了一个栈 stk 来模拟递归调用的过程。每次循环中,从栈中弹出当前的左右边界,然后进行分区操作,将左右子数组的边界压入栈中,以便下一轮循环继续处理。这样就实现了快速排序的非递归版本。

归并排序

归并排序是一种经典的排序算法,它采用分治法的思想,将待排序的数组分成若干个子数组,分别对子数组进行排序,然后将排好序的子数组合并成一个整体有序的数组。

在这里插入图片描述

  1. 分解:将待排序的数组递归地分解为越来越小的子数组,直到每个子数组只包含一个元素。
  2. 合并:将相邻的子数组合并成一个有序的数组,然后依次将合并后的有序数组再次合并,直到合并成一个完整的有序数组。

归并排序的关键在于合并操作,合并两个有序数组的过程是比较简单的。

  1. 分解:将数组分成两个子数组,然后分别对左右两个子数组进行归并排序。
  2. 合并:将排好序的左右两个子数组合并成一个有序数组。

递归

void _Merger(std::vector<int>& arr, int begin, int end, std::vector<int>& tmp)
{if (begin >= end) return;int mid = (begin + end) / 2;_Merger(arr, begin, mid, tmp);_Merger(arr, mid + 1, end, tmp);int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int cur = begin;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]) tmp[cur++] = arr[begin1++];else tmp[cur++] = arr[begin2++];}while (begin1 <= end1) tmp[cur++] = arr[begin1++];while (begin2 <= end2) tmp[cur++] = arr[begin2++];memcpy(&arr[begin], &tmp[begin], sizeof(int) * (end - begin + 1));
}

在归并排序中,递归函数 mergeSort 对数组进行分解并递归地调用自身,直到分解出的子数组只有一个元素时停止递归。然后,通过 merge 函数将相邻的两个有序子数组合并为一个更大的有序数组。最终,整个数组就变得有序了。

非递归

void _MergerNonR(std::vector<int>& arr, std::vector<int>& tmp)
{int n = 1;while (n < arr.size()){for (int i = 0; i < arr.size(); i += 2 * n){int begin1 = i, end1 = i + n - 1;int begin2 = i + n, end2 = i + 2 * n - 1;if (begin1 >= arr.size() || begin2 >= arr.size()) break;if (end2 >= arr.size()) end2 = arr.size() - 1;int cur = i;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]) tmp[cur++] = arr[begin1++];else tmp[cur++] = arr[begin2++];}while (begin1 <= end1) tmp[cur++] = arr[begin1++];while (begin2 <= end2) tmp[cur++] = arr[begin2++];memcpy(&arr[i], &tmp[i], sizeof(int) * (end2 - i + 1));}n *= 2;}
}

在非递归版本的归并排序中,外层循环控制子数组的大小 n,初始值为1,每次循环翻倍,直到子数组的大小超过数组的长度。内层循环每次从数组的开头开始,将相邻的子数组两两合并,并将合并后的结果放回原数组。这样就完成了非递归版本的归并排序。

总结

下面是对这些排序算法的时间复杂度、空间复杂度和稳定性的总结:

排序算法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
直接插入排序O(n^2)O(n^2)O(n)O(1)稳定
希尔排序O(n log^2 n)O(n^2)O(n log n)O(1)不稳定
直接选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定
冒泡排序O(n^2)O(n^2)O(n)O(1)稳定
快速排序O(n log n)O(n^2)O(n log n)O(log n)不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)稳定
  • 时间复杂度
    • 平均时间复杂度表示在所有可能情况下的运行时间的期望值。
    • 最坏时间复杂度表示在最差情况下的运行时间。
    • 最好时间复杂度表示在最好情况下的运行时间。
  • 空间复杂度:表示算法在执行过程中所需的额外空间。
  • 稳定性:如果排序算法能够保持相同键值的原始顺序,则称其为稳定的,否则称为不稳定的。

根据上述总结,可以选择适合特定场景和需求的排序算法。例如,对于大规模数据集合,快速排序和堆排序通常是比较好的选择,而对于小规模数据集合,直接插入排序可能更有效。同时,如果需要保持相同键值的相对位置不变,应选择稳定的排序算法。

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

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

相关文章

【编程小记】在Windows下使用C/C++代码判断一个文件是否被其他进程占用

在Windows下使用C/C代码判断文件是否被占用 一、原理二、函数简单介绍三、实例代码 一、原理 在Windows下有一个Windows API叫做CreateFile&#xff0c;通过这个接口我们可以创建或打开文件&#xff0c;我们打开文件时可以采用独占模式进行打开&#xff0c;如果能够打开文件说…

Word Game

题目链接&#xff1a;Problem - C - Codeforces 解题思路&#xff1a; 用map存字母和出现的次数&#xff0c;然后遍历三个字母数组&#xff0c;如果map值为1&#xff0c;则加三分&#xff0c;为2加1分&#xff0c;否则不加分。 下面是c代码&#xff1a; #include<iostrea…

第41期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

Linux学习-二级指针的使用

目录 ###指针传参时要用二级指针 ###函数体内部想要修改函数外部指针变量值的时候需要使用二级指针(指针变量的地址) ###指针传参时要用二级指针 char *str[5]; int Fun(char **ppstr,int len); ###函数体内部想要修改函数外部指针变量值的时候需要使用二级指针(指针变量的…

#微信小程序创建(获取onenet平台数据)

1.IDE&#xff1a;微信开发者工具 2.实验&#xff1a;创建一个小程序&#xff08;http get获取onenet平台数据&#xff09; 3.记录&#xff1a; 百度网盘链接&#xff1a;https://pan.baidu.com/s/1eOd-2EnilnhPWoGUMj0fzw 提取码: 2023 &#xff08;1&#xff09;新建一个工…

【C++STL详解 —— string类】

【CSTL详解 —— string类】 CSTL详解 —— sring类一、string的定义方式二、string的插入三、string的拼接四、string的删除五、string的查找六、string的比较七、string的替换八、string的交换九、string的大小和容量十、string中元素的访问十一、string中运算符的使用十二、…

2024年腾讯云发红包了,可用于抵扣订单金额,你们领了吗?

在2024年腾讯云新春采购节优惠活动上&#xff0c;可以领取新年惊喜红包&#xff0c;打开活动链接 https://curl.qcloud.com/oRMoSucP 会自动弹出红包领取窗口&#xff0c;如下图&#xff1a; 腾讯云2024新春采购节红包领取 如上图所示&#xff0c;点击“领”红包&#xff0c;每…

回溯算法01-组合(Java)

1.组合 题目描述 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]示例 2&#xff1a; 输入&#x…

VMware虚拟机安装Centos7图解,提供软件包镜像(详细安装,小白入门必看)

目录 1. 安装vmware软件 2. 下载centos7镜像 3. 使用镜像安装centos操作系统 3.1 创建新的虚拟机 3.2 开机安装系统 4. 尝试网络连通性 5. 配置静态ip地址 1. 安装vmware软件 下载链接&#xff08;包含激活码&#xff09;&#xff1a;VMware https://www.alipan…

C语言初学10:共同体

一、共同体作用 提供一种在相同内存位置存储不同数据类型的有效方式 二、共同体定义 union [union tag] //tag是可选参数 {member definition;member definition;...member definition; } [one or more union variables]; // 共同体变量是可选的 三、共同体占用空间大小 #…

vCity 2.8 – 在线浏览器元宇宙游戏平台(虚拟现实网页游戏平台)可获取现实金钱对接贝宝

vCity Nulled 是一个采用最新网络技术开发的在线浏览器游戏平台。vCity Free Download Scripts 是多功能游戏脚本&#xff0c;可帮助您根据自己的喜好创建自己的在线浏览器游戏&#xff0c;因为它有许多选项和设置&#xff0c;可以通过这些选项和设置来修改和创建您想要的浏览器…

亚信安慧AntDB:编织数据丝路,缔造创新篇章

亚信安慧AntDB作为一款具备国产化升级改造经验的数据库系统&#xff0c;在15年的平稳运行中积累了丰富经验。通过持续的创新和技术进步&#xff0c;AntDB不断优化性能和功能&#xff0c;满足用户的需求&#xff0c;与国际先进数据库系统保持竞争力。 AntDB秉承着与用户和行业保…

【广度优先搜索】【堆】【C++算法】407. 接雨水 II

作者推荐 【二分查找】【C算法】378. 有序矩阵中第 K 小的元素 本文涉及知识点 广度优先搜索 堆 LeetCoce407. 接雨水 II 给你一个 m x n 的矩阵&#xff0c;其中的值均为非负整数&#xff0c;代表二维高度图每个单元的高度&#xff0c;请计算图中形状最多能接多少体积的雨…

使用MockJS模拟数据,如何获取入参?

场景描述 在使用MockJS进行模拟数据的时候&#xff0c;会遇到一种场景&#xff0c;当参数1时&#xff0c;展示A类数据&#xff0c;当参数B时&#xff0c;展示B类数据&#xff0c;为了实现这场景&#xff0c;那就要在模拟数据时拿到请求参数&#xff1f; 实现逻辑 mock方法的…

遇见未来的你——陪伴是最长情的告白

目录 一、背景介绍二、思路&方案三、过程1.家庭中彼此的陪伴最长情2.事业中与合伙人与同事与朋友与产品的陪伴最长情3.人生中与计划与落实与啊哈的陪伴最长情4.肉体与灵魂分分合合的体验 四、总结 一、背景介绍 人有时候一转身就是一辈子&#xff0c;所以珍惜转身的每一个…

项目一:踏上Java开发之旅(2023软件1班)

文章目录 一、实战概述二、实战步骤任务1&#xff1a;安装配置JDK开发第一个Java程序1、安装JDK2、配置Java环境变量3、开发第一个Java程序&#xff08;1&#xff09;编写源程序 - HelloWorld.java&#xff08;2&#xff09;编译成字节码文件 - HelloWorld.class&#xff08;3&…

vulhub中ThinkPHP5 5.0.23 远程代码执行漏洞复现

ThinkPHP是一款运用极广的PHP开发框架。其5.0.23以前的版本中&#xff0c;获取method的方法中没有正确处理方法名&#xff0c;导致攻击者可以调用Request类任意方法并构造利用链&#xff0c;从而导致远程代码执行漏洞。 环境启动后&#xff0c;访问http://your-ip:8080即可看到…

深度学习模型部署(番外4)模型量化方案及实战

量化方案 根据量化的时机可以分为训练时量化QAT和训练后量化PTQ。二者的工作流程图如下&#xff1a; QAT量化流程图 PTQ量化流程图 其中的CLE为层间放缩平衡Cross-Layer Equalization&#xff0c;在使用per_tensor粒度进行量化时&#xff0c;同一个tensor中可能数据不平衡情况…

upload-Labs靶场“11-15”关通关教程

君衍. 一、第十一关 %00截断GET上传1、源码分析2、%00截断GET上传 二、第十二关 %00截断POST上传1、源码分析2、%00截断POST上传 三、第十三关 文件头检测绕过1、源码分析2、文件头检测绕过 四、第十四关 图片检测绕过上传1、源码分析2、图片马绕过上传 五、第十五关 图片检测绕…

华中某科技大学校园网疑似dns劫持的解决方法

问题 在校园网ping xxx.ddns.net&#xff0c;域名解析失败 使用热点ping xxx.ddns.net&#xff0c;可以ping通 尝试设置windows dns首选dns为114.114.114.114&#xff0c;重新ping&#xff0c;仍然域名解析失败 猜测【校园网可能劫持dns请求】 解决方法 使用加密的dns请求…