数据结构——各种常见算法的实现方法和思路

文章目录

  • 常见的排序算法
    • 类型
    • 复杂度和稳定性
  • 1.冒泡排序
  • 2.直接插入排序
  • 3.希尔排序
  • 4.简单选择排序
    • 方法1:双向遍历选择排序
    • 方法2:单向遍历选择排序
  • 5.归并排序
    • 方法1:递归
    • 方法2:非递归
  • 6.快速排序
    • 方法1:随机取keyi
    • 方法2:三数取中
    • 方法3:挖坑法
    • 方法4:前后指针法
    • 方法5:非递归
    • 方法6:三路划分
  • 7.堆排序
  • 8.计数排序
  • 9.基数排序
  • 9.桶排序

在这里插入图片描述

常见的排序算法

类型

在这里插入图片描述

复杂度和稳定性

数据结构的复杂度和稳定性是评价数据结构优劣的两个方面,复杂度主要关注数据结构在执行操作时所需的时间和空间资源消耗,而稳定性主要关注数据结构在执行操作时是否能够保证原有数据的相对位置不变。

在这里插入图片描述

1.冒泡排序

请添加图片描述
请添加图片描述

这是一个冒泡排序算法的实现,它可以对一个整型数组按照从大到小的顺序进行排序。

算法的基本思想是通过不断比较相邻的元素并交换它们的位置,将较大的元素逐渐“冒泡”到数组的顶端。具体来说,该算法通过双重循环实现:外层循环控制比较的轮数,内层循环负责相邻元素的比较和交换操作。在每一轮比较中,如果当前元素比它后面的元素小,就交换它们的位置,使得较大的元素逐渐向数组的前部移动。

该算法的时间复杂度为O(n^2),其中n为数组的长度。虽然它的时间复杂度较高,但是它的实现简单、容易理解,对于小规模的数据排序来说还是比较实用的。

//从小到大
void BubbleSort1(int* a, int n)
{for (int i = 0; i < n-1; i++){for (int j = 0; j < n - 1 - i; j++){if (a[j] > a[j + 1]){int temp = a[j];a[j] = a[j + 1];a[j + 1] = temp;}}}
}

这也是一个冒泡排序算法的实现,可以对一个整型数组按照从小到大的顺序进行排序。

与上一个算法相比,该算法的唯一区别在于内层循环中比较的方式。在该算法中,如果当前元素比它后面的元素大,就交换它们的位置,使得较小的元素逐渐向数组的前部移动。

该算法的时间复杂度同样为O(n^2),其中n为数组的长度。由于冒泡排序算法的优化空间比较有限,因此它的时间复杂度较高,不适合用于大规模数据的排序。

//从大到小
void BubbleSort2(int* a, int n)
{for (int i = 0; i < n - 1; i++){for (int j = 0; j < n - 1 - i; j++){if (a[j] < a[j + 1]){int temp = a[j];a[j] = a[j + 1];a[j + 1] = temp;}}}
}

2.直接插入排序

在这里插入图片描述
插入排序是一种简单直观的排序算法,其核心思想是将未排序的元素逐个插入到已排序的序列中,以便生成一个新的有序序列。插入排序的过程类似于打扑克牌时的整理牌的方法,即将一张牌插入到已经排好序的牌中的适当位置。

插入排序的基本思路是从第二个元素开始,逐个将元素插入到前面已经排好序的子序列中。具体而言,对于第 i 个元素,我们将其与前面的元素逐个比较,找到第一个比它小的元素,然后将它插入到这个元素后面,即可保证前 i 个元素已经排好序。这个过程不断重复,直到整个序列都有序为止。

插入排序的时间复杂度为 O(n^2),它的性能与输入数据的初始顺序有关。如果输入数据已经接近有序,那么插入排序的效率会比较高,因为它只需要进行少量的比较和移动操作。但如果输入数据的顺序比较随机,那么插入排序的效率会比较低,因为它需要进行大量的比较和移动操作。


// 插入排序
void InsertSort(int* a, int n)
{//从第2个元素开始插入排序for (int i = 1; i < n; i++){int end = i - 1;  // 已排序序列的最后一个元素的下标int tmp = a[i];     // 待插入的元素//将待插入的元素与已排序的元素从后往前依次比较while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];  // 如果比已排序的元素小,则将已排序的元素后移一位end--;}else{break;  // 如果找到一个比待插入元素小的位置,则退出循环}}a[end + 1] = tmp;  // 将待插入的元素插入到已排序的序列中}
}

3.希尔排序

在这里插入图片描述
希尔排序(Shell Sort)是插入排序的一种改进版本,也称为缩小增量排序(diminishing increment sort)。它通过将待排序的序列分割成若干个子序列,对每个子序列进行插入排序,从而实现对整个序列的排序。

希尔排序的核心思想是将相距某个“增量”的元素组成一个子序列,对每个子序列进行插入排序,使得整个序列在增量不断缩小的情况下逐步变得有序。最后当增量为1时,整个序列就变成了一个有序序列。

具体实现中,希尔排序先将待排序的元素按照一定的增量分成若干个子序列,对每个子序列进行插入排序。然后,逐渐减小增量,继续对子序列进行插入排序,直到增量为1时,完成最后一次排序,整个序列就变成了有序序列。

希尔排序的时间复杂度为 O(n log n) 到 O(n^2),具体取决于增量的选择和子序列的划分方式。希尔排序的优点是它的实现比较简单,只需要对插入排序进行一些改进即可。缺点是增量序列的选择比较困难,不同的增量序列对性能的影响也比较大。

//希尔排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap /= 2;for (int i = 0; i < n - gap; i++){int end = i;int tmp = a[i + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

4.简单选择排序

在这里插入图片描述

选择排序(Selection Sort)是一种简单直观的排序算法,其核心思想是在未排序序列中选择最小(或最大)的元素,放到已排序序列的末尾,直到所有元素都排完为止。

具体实现中,选择排序从未排序序列中找到最小(或最大)的元素,然后将它与未排序序列的第一个元素交换,这样就可以把该元素放到已排序序列的末尾。然后,在剩余的未排序序列中继续找到最小(或最大)的元素,重复上述操作,直到所有元素都排完为止。

选择排序的时间复杂度为 O(n^2),它的性能比冒泡排序略好,但比插入排序差。选择排序的优点是它的实现比较简单,只需要进行 n-1 次比较和 n 次交换,因此在某些情况下它的性能可能比其他排序算法更好。缺点是它的时间复杂度比较高,不适合对大规模数据进行排序。

方法1:双向遍历选择排序

//交换
Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void SelectSort1(int* a, int n) 
{int left = 0;int right = n - 1;while (left < right){int max = left, mini = left;for (int i = left + 1; i <= right; i++){if (a[i] < a[mini]){mini = i;}if (a[i] > a[max]){max = i;}}Swap(&a[left], &a[mini]);if (left == max){max = mini;}Swap(&a[right], &a[max]);++left;--right;}
}

方法2:单向遍历选择排序

//交换
void Swap(int* a, int* b)
{int temp = *a;*a = *b;*b = temp;
}void SelectSort2(int* a, int n)
{int min,i,j;for (int i = 0; i < n - 1; i++){min = i;for (int j = i + 1; j < n; j++){if (a[j] < a[min]){min = j;}}int temp = a[min];a[min] = a[i];a[i] = temp;}
}

5.归并排序

在这里插入图片描述

方法1:递归

这是一种基于分治的排序算法 - 归并排序。
思路:

  1. 将数组分成两个子数组,递归调用排序子数组
  2. 将两个有序子数组合并成一个有序数组
void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin>=end){return;}int mid = (begin + end) / 2;//[begin,mid] [mid+1,end],子区间递归排序_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);//[begin,mid] [mid+1,end]归并int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("mallco fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

方法2:非递归

思路:

  1. 使用一个gap进行划分,从1开始扩大gap,每次将相邻gap大小的两个子数组合并
  2. 合并相邻gap大小的两个子数组的方法和递归版本是一样的
  3. 每次将gap * 2,进行下一轮划分和合并
  4. 重复此过程,直到gap >= n,排序完成
//非递归
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("mallco fail");return;}int gap = 1;while (gap<n){for (int i = 0; i < n; i += 2 * gap){//[begin1,end1][begin2,end2]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;if (end1 >= n || begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}int j = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}//归并一部分拷贝一部分memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}free(tmp);
}

6.快速排序

在这里插入图片描述

方法1:随机取keyi

思路:

  1. 选择一个基准元素(使用随机数随机选择一个元素)
  2. 将所有比基准元素小的元素移动到其左边,所有比基准元素大的元素移动到其右边
  3. 对基准元素的左右两边重复第一步和第二步,直到全部排序完成
void Swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}
void QuickSort(int* a, int left, int right)
{if (left >= right)return;int begin = left;int end = right;//随机选keyint randi = left + (rand() % (right - left));Swap(&a[left], &a[randi]);int keyi = left;while (left < right){//右边找小while (left < right && a[right] >= a[keyi])--right;//左边找大while (left<right && a[left]>a[keyi])++left;Swap(&a[left], &a[right]);}
}

方法2:三数取中

思路:

  1. 首先利用中位数作为基准值,选择最稳定的基准值。
  2. 获取三个数的中位数:
    如果a[left]<a[mid],那么:如果a[mid]<a[right], mid是中位数,否则比较a[left] 和 a[right],较大值是中位数
  3. 将中位数与第一个元素交换,作为基准值
  4. 其他部分与普通快速排序相同
void Swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}
int GetMidNumi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;} else{return right;}}else//a[left]>a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}
void QuickSort2(int* a, int left, int right)
{if (left >= right)return;int begin = left;int end = right;int midi = GetMidNumi(a, left, right);if (midi != left){Swap(&a[midi], &a[left]);}int keyi = left;while (left < right){//右边找小while (left < right && a[right] >= a[keyi])--right;//左边找大while (left<right && a[left]<=a[keyi])++left;Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;QuickSort2(a, begin, keyi - 1);QuickSort2(a, keyi + 1, end);
}

方法3:挖坑法

在这里插入图片描述

思路:

  1. 使用 begin 和 end 表示子数组范围
  2. key 记录基准值,hole 记录待填充位置
  3. 把 left 和 right 指针分别指向 begin 和 end
  4. while 循环:右边第一个小于等于 key 的元素,记录其索引到 hole, right,左边第一个大于 key 的元素,记录其索引到 hole,left++
  5. hole 位置填充 key 值
  6. 对基准值左右两侧的子数组递归调用快速排序
void QuickSort3(int* a, int left, int right)
{if (left >= right)return;int begin = left;int end = right;int key = a[left];int hole = left;while (left < right){//右边找小while (left < right && a[right] >= key)--right;a[hole] = a[right];hole = right;//左边找大while (left < right && a[left] <= key)++left;a[hole] = a[left];hole = left;}a[hole] = key;QuickSort3(a, begin, hole - 1);QuickSort3(a, hole+ 1, end);
}

方法4:前后指针法

在这里插入图片描述

快速排序的前后指针法(也称为双指针法)是一种常见的划分方式。它的基本思想是,将整个序列分为小于等于主元的左半部分和大于主元的右半部分,然后递归地对左右两个部分进行排序。

思路:

  1. 依然使用left、right指针和基准值pivot。
  2. 额外引入一个prev指针,记录小于pivot的最后一个元素的位置。
  3. 当遍历到一个元素时:如果元素 < pivot,将其与prev指向的元素交换,prev后移 1 位,如果元素 = pivot,直接略过,如果元素 > pivot,继续遍历
  4. 最终prev指向的位置,就是pivot的正确位置。
int QuickSort4(int* a, int left, int right)
{int midi = GetMidNumi(a, left, right);if (midi != left){Swap(&a[midi], &a[left]);}int keyi =left;int prev = left;int cur = left + 1;while (cur <= right){if (a[cur] <a[keyi] && ++prev != cur)Swap(&a[cur], &a[prev]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}

方法5:非递归

这里使用了一个栈来模拟递归过程。首先将整个序列的左右端点入栈,之后每次取出栈顶的左右端点,进行一次划分并将新的左右端点入栈,直到栈为空为止。在划分函数中使用双指针法将小于等于主元的元素放在左边,大于主元的元素放在右边,最后把主元放在中间,并返回主元的下标。

需要注意的是,这里使用了一个结构体 Range 来表示左右端点的范围,这样可以方便地把左右端点打包在一起并压入栈中。

#include <stdio.h>
#define MAX_SIZE 100
typedef struct {int l;int r;
} Range;void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}int partition(int arr[], int l, int r) {int pivot = arr[l];while (l < r) {while (l < r && arr[r] >= pivot) r--;arr[l] = arr[r];while (l < r && arr[l] <= pivot) l++;arr[r] = arr[l];}arr[l] = pivot;return l;
}void quickSort(int arr[], int n) {Range stack[MAX_SIZE];int top = -1;stack[++top] = (Range){ 0, n - 1 };while (top >= 0) {Range range = stack[top--];if (range.l >= range.r) continue;int p = partition(arr, range.l, range.r);stack[++top] = (Range){ range.l, p - 1 };stack[++top] = (Range){ p + 1, range.r };}
}int main() {int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 };int n = sizeof(arr) / sizeof(arr[0]);quickSort(arr, n);for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

方法6:三路划分

在这里插入图片描述

思路:

  1. 跟key相等的值,往后推
  2. 比key小的甩到左边
  3. 比key大的甩到右边
  4. 跟key相等的就在中间
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}
void quicksort(int arr[], int low, int high) {if (low >= high) {return;}// 选取第一个元素为基准值int pivot = arr[low];// lt指向小于基准值的部分的最后一个元素int lt = low;// gt指向大于基准值的部分的第一个元素int gt = high;// i指向当前处理的元素int i = low + 1;// 三路划分while (i <= gt) {if (arr[i] < pivot) {// 将小于基准值的元素交换到lt部分swap(&arr[i], &arr[lt]);lt++;i++;}else if (arr[i] > pivot) {// 将大于基准值的元素交换到gt部分swap(&arr[i], &arr[gt]);gt--;}else {// 相等的元素直接跳过i++;}}// 递归排序小于基准值的部分quicksort(arr, low, lt - 1);// 递归排序大于基准值的部分quicksort(arr, gt + 1, high);
}
int main() {int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};int n = sizeof(arr) / sizeof(arr[0]);quicksort(arr, 0,n-1);for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

7.堆排序

在这里插入图片描述

思路:

使用 maxHeapify() 函数维护最大堆的属性。

使用 n/2 - 1 作为起始索引,逐层地将数组调整为最大堆。

排序部分:

  1. 将堆顶元素(最大元素)与末尾元素交换
  2. 缩小堆的范围(n范围减1),再次调用 maxHeapify() 函数,维护最大堆的属性。
  3. 重复上两步,直到堆范围为0。
// 交换函数 
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}void maxHeapify(int arr[], int n, int i) {int largest = i;int left = 2*i + 1;int right = 2*i + 2;if (left < n && arr[left] > arr[largest])largest = left;if (right < n && arr[right] > arr[largest])largest = right;if (largest != i) {swap(&arr[i], &arr[largest]);maxHeapify(arr, n, largest);}
}void heapSort(int arr[], int n) {// 构建最大堆for (int i = n / 2 - 1; i >= 0; i--)maxHeapify(arr, n, i);// 排序for (int i = n - 1; i >= 0; i--) {swap(&arr[0], &arr[i]); // 将最大值交换到数组末尾maxHeapify(arr, i, 0);}
}int main() {int arr[] = { 12, 11, 13, 5, 6, 7 };int n = sizeof(arr) / sizeof(arr[0]);heapSort(arr, n);// 打印排序后的数组for (int i = 0; i < n; i++)printf("%d ", arr[i]);return 0;
}

8.计数排序

请添加图片描述

思路:

  1. 扫描一次给定的数组,找到最大元素和最小元素
  2. 根据最大元素和最小元素,计算出需要多大的空间用于排序
  3. 创建计数数组 countA,长度为范围(max - min + 1)
  4. 遍历给定数组,对于每个元素 a[i],计数数组 countA[a[i] - min] 加 1
  5. 对计数数组进行累加求和,使每个元素表示当前元素及之前所有元素的个数
  6. 遍历给定数组,将元素放在正确位置上
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void CountSort(int* a, int n)
{int max = a[0], min = a[0];for (int i = 1; i < n; i++){if (a[i] > max){max = a[i];}if (a[i] < min){min = a[i];}}int range = max - min + 1;int* countA = (int*)malloc(sizeof(int) * range);if (countA == NULL){perror("malloc fail");return;}memset(countA, 0, sizeof(int) * range);//计数for (int i = 0; i < n; i++){countA[a[i] - min]++;}//排序int j = 0;for (int i = 0; i < range; i++){while (countA[i]--){a[j++] = i + min;}}free(countA);
}
int main()
{int arr[] = { 2,3,5,6,7,3,2,5,5,5,9,200 };int sz = sizeof(arr) / sizeof(arr[0]);CountSort(arr, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

9.基数排序

请添加图片描述

思路:

  1. 对数组按照最低阶(个位数)进行分配,然后收集。
  2. 再按照次低阶(十位数)分配和收集,依次类推到最高阶。
  3. 每次分配、收集都会让数组更加有序。

具体实现:

  1. 定义最大基数为 RADIX(10),一个辅助队列组 Q[]
  2. Getkey() 函数获取元素 value 在指定位数 k 的值
  3. Distribute() 函数将数组中元素按照 k 位分配到相应的队列中
  4. Collect() 函数收集队列中的元素,反向填充数组
  5. RadixSort() 函数依次分配、收集 K次,K为指定位数
#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
#define K 3
#define RADIX 10
queue<int> Q[RADIX];int Getkey(int value, int k)
{int key = 0;while (k >= 0){key = value % 10;value /= 10;k--;}return key;
}
//分发数据
void  Distribute(int arr[], int left, int right, int k)
{for (int i = left; i < right; i++){int key = Getkey(arr[i],k);Q[key].push(arr[i]);}
}
//回收数据
void Collect(int arr[])
{int k = 0;for (int i = 0; i < RADIX; i++){while (!Q[i].empty()){arr[k++] = Q[i].front();Q[i].pop();}}
}
void RadixSort(int* arr, int left, int right)
{for (int i = 0; i < K; i++){//分发数据Distribute(arr, left, right, i);//回收数据Collect(arr);}
}int main()
{int arr[] = { 278,109,63,930,589,184,505,269,8,83 };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");//基数排序RadixSort(arr, 0, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

9.桶排序

在这里插入图片描述

桶排序是一种线性排序算法,它的基本思想是将待排序的元素分到不同的桶中,每个桶内的元素再分别使用其他排序算法进行排序,最后合并所有桶中的元素即可得到有序序列。桶排序的时间复杂度为 O(n),但是它的空间复杂度较高,需要额外的空间来存储桶。

#include <stdio.h>
#include <stdlib.h>// 桶排序
void bucket_sort(int arr[], int n) {int max_num = arr[0];int min_num = arr[0];int i, j, k;for (i = 1; i < n; i++) {if (arr[i] > max_num) {max_num = arr[i];}if (arr[i] < min_num) {min_num = arr[i];}}int bucket_size = (max_num - min_num) / n + 1;int bucket_count = (max_num - min_num) / bucket_size + 1;int **buckets = malloc(sizeof(int*) * bucket_count);for (i = 0; i < bucket_count; i++) {buckets[i] = malloc(sizeof(int) * n);}int *count = malloc(sizeof(int) * bucket_count);for (i = 0; i < bucket_count; i++) {count[i] = 0;}for (i = 0; i < n; i++) {int index = (arr[i] - min_num) / bucket_size;buckets[index][count[index]] = arr[i];count[index]++;}k = 0;for (i = 0; i < bucket_count; i++) {for (j = 0; j < count[i]; j++) {arr[k] = buckets[i][j];k++;}}for (i = 0; i < bucket_count; i++) {free(buckets[i]);}free(buckets);free(count);
}// 测试桶排序
int main() {int arr[] = {5, 2, 8, 3, 9, 6};int n = sizeof(arr) / sizeof(int);bucket_sort(arr, n);printf("排序后的结果为:");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

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

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

相关文章

谈 Delphi 中 JSON 的简便操作(非常简单)

我曾有讲过Delphi操作JSON的方法&#xff0c;特别是这一篇【delphi】类和记录的 helpers&#xff08;助手&#xff09;。但是因为当时是主要介绍的是Delphi的Helper&#xff0c;大家可能并没注意到Delphi中JSON的简便操作方法。 早期Delphi并没有自己的JSON操作库&#xff0c;大…

了解 JVM - 认识垃圾回收机制与类加载过程

前言 本篇通过介绍JVM是什么&#xff0c;认识JVM的内存区域的划分&#xff0c;了解类加载过程&#xff0c;JVM中垃圾回收机制&#xff0c;从中了解到垃圾回收机制中如何找到存活对象的方式&#xff0c;引用计数与可达性分析的方式&#xff0c;再释放垃圾对象时使用的方式&…

Kerberos协议详解

0x01 kerberos协议的角色组成 Kerberos协议中存在三个角色&#xff1a; 客户端(Client)&#xff1a;发送请求的一方 服务端(Server)&#xff1a;接收请求的一方 密钥分发中心(Key distribution KDC) 密钥分发中心分为两个部分&#xff1a; AS(Authentication Server)&…

Nacos (2.0版本之后)状态异常集群节点状态异常

在nacos 2.0 之后正常部署后节点状态仅有一个正常,其余均为DOWN 或者SUSPICIOUS 状态 查看日志后发现 ERROR Server check fail, please check server 192.168.172.104 ,port 9849 is available , error {} 其实是nacos 相互之间不能正常通信造成的,nacos客户端升级为2.x版…

apple pencil一代的平替有哪些品牌?苹果平板的触控笔

随着苹果Pencil系列的推出&#xff0c;平替电容笔在国内市场得到了较好的发展&#xff0c;随之的销量&#xff0c;也开始暴涨&#xff0c;苹果pencil因为价格太高&#xff0c;导致很多人买不起。目前市场上&#xff0c;有不少的平替电容笔&#xff0c;可以替代苹果的Pencil&…

MySQL 主从复制与读写分离

MySQL 主从复制与读写分离 一、mysql读写分离的概念1、什么是读写分离&#xff1f;2、为什么要读写分离呢&#xff1f;3、什么时候要读写分离&#xff1f;4、主从复制与读写分离5、mysql支持的复制类型 二、主从复制的工作过程三、MySQL 读写分离原理常见的 MySQL 读写分离分为…

【Distributed】分布式ELK日志文件分析系统(二)

文章目录 一、FilebeatELK 部署1. 环境部署2. 在 Filebeat 节点上操作2.1 安装 Filebeat2.2 设置 filebeat 的主配置文件 3. 在 Apache 节点上操作3.1 在 Logstash 组件所在节点上新建一个 Logstash 配置文件 3. 启动3.1 在Logstash 组件所在节点启动3.2 在 Filebeat 节点 启动…

Stable Diffusion 用2D图片制作3D动态壁纸

如果想让我们的2D图片动起来可以使用stable-diffusion-webui-depthmap-script插件在SD中进行加工让图片动起来。 这是一个可以从单个图像创建深度图,现在也可以生成3D立体图像对的插件,无论是并排还是浮雕。生成的结果可在3D或全息设备(如VR耳机或Looking Glass显示器)上查…

大模型加速学科升级,飞桨赋能北邮“X+大模型”特色小学期

在人工智能时代&#xff0c;设计师与产品经理比以往更加需要关注一个事实&#xff1a;那就是如何利用人工智能和数据分析技术&#xff0c;打造让用户心动的信息交互产品和用户体验&#xff0c;释放人-机协同共创的巨大潜能&#xff0c;是决定设计产能和竞争力的关键。 在产业的…

RabbitMQ ---- 消息队列

RabbitMQ ---- 消息队列 1. MQ 的相关概念1.1 什么是 MQ1.2 为什么要用 MQ1.3 MQ 的分类1.4 MQ 的选择 2. RabbitMQ2.1 RabbitMQ 的概念2.2 四大核心概念2.3 RabbitMQ 核心部分2.4 各个名词介绍2.5 安装 1. MQ 的相关概念 1.1 什么是 MQ MQ(message queue)&#xff0c;从字面…

爬虫之Scrapy

一 介绍 Scrapy一个开源和协作的框架&#xff0c;其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的&#xff0c;使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛&#xff0c;可用于如数据挖掘、监测和自动化测试等领域&#x…

什么是Docker

容器技术和虚拟机 虚拟机 和一个单纯的应用程序相比&#xff0c;操作系统是一个很重的程序&#xff0c;刚装好的系统还什么都没有部署&#xff0c;单纯的操作系统其磁盘占用至少几十G起步&#xff0c;内存要几个G起步。 在这台机器上开启三个虚拟机&#xff0c;每个虚拟机上…

PSI算法经典论文算法概述

文章目录 什么是隐私求交PSIPSI协议分类PSI算法的分类基于哈希函数的PSI算法基于不经意传输&#xff08;OT&#xff09;的 PSI算法基于GC的PSI算法基于公钥加密的PSI算法基于DH的PSI算法基于RSA盲签名的PSI算法基于同态加密的PSI算法 基于差分隐私的PSI算法 总结参考文献 什么是…

为了实现上网自由,我做了一个多功能串口服务器

项目作者&#xff1a;小华的物联网嵌入式之旅 介绍&#xff1a;从事电气自动化行业&#xff0c;多次获得物联网设计竞赛&#xff0c;爱好嵌入式设计开发&#xff0c;物联网开发。 设计方案思路的由来&#xff0c;是因为我们现在的开发板基本需要通过串口与WIFI模组或以太网模…

[Linux] 最基础简单的线程池 及其 单例模式的实现

本篇文章主要用到线程相关内容, 下面是博主关于线程相关内容的文章: [Linux] 线程同步分析&#xff1a;什么是条件变量&#xff1f;生产者消费者模型是什么&#xff1f;POSIX信号量怎么用&#xff1f;阻塞队列和环形队列模拟生产者消费者模型 [Linux] 线程互斥分析: 多线程的问…

华为、阿里巴巴、字节跳动 100+ Python 面试问题总结(一)

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python面试专栏&#xff1a;《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; …

华为发布大模型时代AI存储新品

7月14日&#xff0c;华为发布大模型时代AI存储新品&#xff0c;为基础模型训练、行业模型训练&#xff0c;细分场景模型训练推理提供存储最优解&#xff0c;释放AI新动能。 企业在开发及实施大模型应用过程中&#xff0c;面临四大挑战&#xff1a; 首先&#xff0c;数据准备时…

剑指offer刷题笔记--Num41-50

1--数据流中的中位数&#xff08;41&#xff09; 主要思路&#xff1a; 维护两个优先队列&#xff0c;Q1大数优先&#xff0c;存储比中位数小的数&#xff1b;Q2小数优先&#xff0c;存储比中位数大的数&#xff1b; 当存储的数为偶数时&#xff0c;Q1.size() Q2.size(), 中位…

解决github无法拉取submodule子模块的问题

引言 当使用git clone --recursive url 拉取一个配置了子模块的仓库后&#xff0c;会卡住。 同时在使用git clone 拉去https的url时&#xff0c;同样可能会出现一直卡在cloning int reposity...本文提供一个简单的脚本来解决该问题。 前置准备 需要配置好git的相关配置&…

快速配置 Rust 开发环境并编写一个小应用

安装: curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh 更新: Rust 的升级非常频繁. 如果安装 Rustup 后已有一段时间,那么很可能 Rust 版本已经过时, 运行 rustup update 获取最新版本的 Rust rustc&#xff1a;编译Rust程序 rustc只适合简单的Rust程序&#xf…