当前位置: 首页 > news >正文

【C语言数据结构——————排序(1万字)】

文章目录

  • 排序的概念
    •  常见排序算法
    • 分类
  • 冒泡排序
    • 时间复杂度
    • 稳定性
    •  原理
    • 实现
  • 插入排序
    • 时间复杂度
    • 稳定性
    • 实现
  • 选择排序
    •  时间复杂度
    • 稳定性
    • 实现
  • 希尔排序
    •  时间复杂度
    • 稳定性
    • 希尔排序的算法思想
    • 实现
      •  优化
  • 快速排序
    •  时间复杂度
    • 空间复杂度
    • 稳定性
    • 实现
      •  三数取中优化
  • 归并排序
    •  时间复杂度
    • 空间复杂度
    • 稳定性
    • 实现
      •  递归实现归并排序
  • 堆排序
    •  时间复杂度
    • 实现
      •  大顶堆和小顶堆的介绍
      • 向上调整算法
      • 向下调整算法
  • 计数排序
    •  时间复杂度
    • 空间复杂度
    • 稳定性
    • 实现
  • 总结

欢迎阅读新一期的c语言数据结构模块————排序

✒️个人主页:-_Joker_-

🏷️专栏:C语言数据结构

📜代码仓库:c_code

🌹🌹欢迎大佬们的阅读和三连关注,顺着评论回访🌹🌹


排序的概念

排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程叫做排序。

常见排序算法

快速排序、希尔排序、堆排序、直接选择排序、基数排序、计数排序、冒泡排序、插入排序、归并排序等。

分类

◆稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在

用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法

是稳定的。其中冒泡排序,插入排序,基数排序,归并排序、基数排序都属于稳定排序。

◆就地排序:若排序算法所需的辅助空间并不依赖于问题的规模n,即辅助空间为O(1)

◆不稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在

用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法

是不稳定的,其中选择排序,快速排序,希尔排序,堆排序都属于不稳定排序。


冒泡排序

冒泡排序是我们学习编程接触的第一个排序方式,它的优点是比较稳定,而且比较容易掌握。作为我们第一个接触到排序,它的缺点也非常明显,它的时间复杂度是O(n²),这就导致冒泡排序会非常慢,并不适用于排序,只适合用于教学,是一个具有教学意义的启蒙排序方式。

时间复杂度

冒泡排序的时间复杂度为O(n²)

稳定性

由于冒泡排序并没有改变元素的相对次序,所以冒泡排序是一个稳定的排序

原理
  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 

  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 
  3. 针对所有的元素重复以上的步骤,除了最后一个。 
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
冒泡排序的实现:

冒泡排序的算法思路主要就是两层嵌套的循环语句。外层循环控制第1到第 n-1 趟排序,而内层循环则控制每一趟排序中对前面未排好序的元素进行比较和交换。

#include<stdio.h>int main()
{int a[6]={20,40,10,5,50,30};int i = 0;for(j=0;j<5;j++)//外部总共要遍历n-1次(n为元素个数){for(i=0;i<5-j;i++)//每一次将最大值放在最后,前面的数比较次数-1,只需要和最大值前面的元素比较{if(a[i]>a[i+1])//比较相邻两者大小关系,如果前面元素大则需要改变位置{trmp = a[i];//通过中间量交换两者位置a[i]=a[i+1];a[i+1]=temp;}}}return 0;
}

插入排序

插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序 。

时间复杂度

在插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较N- 1次,时间复杂度为O(N)。

最坏的情况是待排序数组是逆序的,此时需要比较次数最多,总次数记为:1+2+3+…+N-1,所以,插入排序最坏情况下的时间复杂度为O(N²)。

稳定性

如果待排序的序列中存在两个或两个以上具有相同关键词的数据,排序后这些数据的相对次序保持不变,即它们的位置保持不变,通俗地讲,就是两个相同的数的相对顺序不会发生改变,则该算法是稳定的;如果排序后,数据的相对次序发生了变化,则该算法是不稳定的。关键词相同的数据元素将保持原有位置不变,所以该算法是稳定的 。

实现
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if ( tmp < a[end])//和前一个值比较大小{a[end + 1] = a[end];//把位置往后挪插入tmp}else{break;}--end;}a[end + 1] = tmp;}}

选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。 

时间复杂度

选择排序的交换操作介于 0 和 (n - 1)次之间。选择排序的比较操作为 n (n - 1) / 2 次之间。选择排序的赋值操作介于 0 和 3 (n - 1) 次之间。比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;最坏情况交换n-1次,逆序交换n/2次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。 

稳定性

选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。所以选择排序是一个不稳定的排序算法。

实现
void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){int min = begin, max = begin;//定义max和min用于寻找最大值和最小值for (int i = begin + 1; i <= end; i++){if (a[i] > a[max])//比较最大值{max = i;}if (a[i] < a[min])//比较最小值{min = i;}}Swap(&a[begin], &a[max]);//交换if (max == begin)//如果首元素就是最大值,将最小值赋给最大值然后交换{max = min;}Swap(&a[begin], &a[min]);begin++;end--;}
}

希尔排序

希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。

时间复杂度

希尔排序的时间复杂度在最好和最坏情况下都是O(nlog²n),平均时间复杂度是O(nlogn),但是实际上希尔排序的时间复杂度是O(n^1.5),所以对于中小规模的排序希尔排序都是非常好用的,但是对于大型数据要稍微吃力。

稳定性

由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。

希尔排序的算法思想

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行插入排序;然后,取第二个增量d2 = …

该方法实质上是一种分组插入方法。

比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。

一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

实现
void ShelltSort(int* a, int n)
{int gap = 3;//给定跳过的步数for (int i = 0; i < gap; i++){for (int j = 0; j < n - gap; j += gap){int end = j;int tmp = a[end + gap];//按步数进行插入排序while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}}

此方法还可以进一步优化

优化
void ShelltSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 2;//gap > 1时是预排序//gap == 1时就是插入排序for (int j = 0; j < n - gap; ++j){int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

快速排序

人如其名,快速排序简称快排,快速排序采用的是分治思想,即在一个无序的序列中选取一个任意的基准元素pivot,利用pivot将待排序的序列分成两部分,前面部分元素均小于或等于基准元素,后面部分均大于或等于基准元素,然后采用递归的方法分别对前后两部分重复上述操作,直到将无序序列排列成有序序列。 

快速排序算法通过多次比较和交换来实现排序,其排序流程如下: 

  • 首先设定一个分界值,通过该分界值将数组分成左右两部分。
  • 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
  • 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  • 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
时间复杂度

快排在理想的条件下的排序速度是非常快的,可以达到O(n log n)。但是在最坏情况下,快排的时间复杂度为O(n²),平均时间复杂度为O(n log n)。

空间复杂度

由于快排需要使用递归进行实现,递归需要消耗栈空间,所以空间复杂度为O(log n)

稳定性

由于快排在进行交换的过程中,会将元素的相对位置给打乱,所以快排属于不稳定排序

实现
void QuickSort(int* a, int begin, int end) {int i = begin; int j = end;if(i >= j) {return;}int tmp = a[begin];while(i != j) {while(a[j] >= tmp && i < j) {j--;}while(a[i] <= tmp && i < j) {i++;}if(i < j) {swap(a[i], a[j]);}}swap(a[begin], array[high]);//递归QuickSort(a, begin, i - 1);QuickSort(a, i + 1, end);
}

三数取中优化

快排同样也可以进行优化,我们可以添加一个三数取中来提高快排的效率。

int GetMiddle(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;elsereturn right;}else{if (a[mid] > a[right])return mid;else if (a[left] < a[left])return left;elsereturn right;}
}

通过三个值寻找中间值的方法,可以极大程度减少快排的时间复杂度,可以使某些极端情况下的快排由O(n²)优化成O(nlongn),这对于快排无疑是非常重要的优化。

下面是优化后的代码

//三数取中
int GetMiddle(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;elsereturn right;}else{if (a[mid] > a[right])return mid;else if (a[left] < a[left])return left;elsereturn right;}
}//部分排序
int partsort1(int* a, int left, int right)
{int mid = GetMiddle(a, left, right);Swap(&a[left], &a[mid]);int key = left;while (left < right){while (left < right && a[right] >= a[key]){--right;}while (left < right && a[left] <= a[key]){++left;}Swap(&a[left], &a[right]);}		Swap(&a[key], &a[left]);return left;
}//递归实现快排
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int key = partsort3(a, begin, end);QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}

归并排序

归并排序是建立在归并操作上的一种有效、稳定的排序算法,简称归排,该算法采用非常经典的分治法(分治法可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化),归并排序的思路很简单,速度呢,也仅此于快速排序,归并排序由于它出色的稳定性和稳定的时间复杂度,使得它的效率比快排要更高。

时间复杂度

归并排序的时间复杂度为O(n log n),是速度仅次于快排的排序

空间复杂度

由于归并排序需要使用到一个临时空间用于存放合并后的结果,所以它的空间复杂度为O(n)

稳定性

由于归并排序的算法是通过将序列中待排序数字分为若干组,每个数字分为一组,然后将若干组两两合并,保证合并的组都是有序的,之后再重复第二步的操作,直到剩下最后一组即为有序数列,所以这就保证了相对顺序不会改变,所以是稳定的。

实现
void MergeSortNonR(int* a, int n)
{int gap = 1;int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//[begin1,end1] [begin2,end2]两组归并int index = i;if (begin2 >= n){break;}//修正越界情况if (end2 >= n){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}//归并一组后,将数据拷贝回原数组memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));}gap *= 2;}
}

归并排序同样也可以通过递归来实现,下面是通过递归方法所实现的归并排序


递归实现归并排序
void _MergeSort(int* a, int* tmp, int begin, int end)
{if (end >= begin){return;}int mid = (end + begin) / 2;_MergeSort(a, tmp, begin, mid);_MergeSort(a, tmp, mid + 1, end);//归并到tmp数组中,再拷贝回去//a->[begin, mid-1] [mid, end]->tmpint begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int index = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);
}

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它也是不稳定排序。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。
每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆

时间复杂度

堆排序的最坏,最好还有平均时间复杂度均为O(nlogn)

实现

堆排序的基本思想是:

  1. 将待排序序列构造成一个大顶堆
  2. 此时,整个序列的最大值就是堆顶的根节点。
  3. 将其与末尾元素进行交换,此时末尾就为最大值。
  4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
大顶堆和小顶堆的介绍:
  • 大顶堆:每个节点的值都大于或者等于它的左右子节点的值: arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]

  • 小顶堆:每个节点的值都小于或者等于它的左右子节点的值: arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]

我们在实现归排的时候,如果需要排成升序,我们需要建大堆,若是要排成降序则需要建小堆

简称升序建大堆,降序建小堆

实现
//向上调整
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){// 找出小的那个孩子if (child + 1 < n && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);// 继续往下调整parent = child;child = parent * 2 + 1;}else{break;}}
}
void HeapSort(int* a, int n)
{//建堆(升序建大堆)for (int i = 1; i < n; i++){AdjustUp(a, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end ,0);--end;}
}

这里再介绍一下向上调整和向下调整

向上调整算法

所谓向上调整,字面意思就是把数不断的和上面的数做调整。

如果已经存在堆,再插入一个结点,从插入的结点开始,向上依次调整。

调整:如果插入的结点不需要调整,即插入该结点,依旧是堆。

如果插入的结点不满足堆结构,就要对该结点的父亲结点调整,调整完因为改变了父亲结点,就要迭代调整新的孩子结点。

类似下图

那我们在看图片下面的情况可以看到我们在我们在堆的末尾空白位置插入了一个数9,那么此时为了保证堆维持一个大根堆,我们必须要把我们这个新插入的数和它的父亲节点作比较,如果这个新插入的数大于父亲,那么就和父亲交换位置。
那么我们怎么找到它的父亲节点呢?用孩子节点下标求父亲节点下标:Parent = (Child - 1) / 2;
此时9是大于8的,所以我们交换两数的位置,交换完毕后继续用9和他的父亲进行比较,10>9所以不需要调整了

向下调整算法

同样的,向下调整算法字面意思就是把数据和它的孩子节点作比较,然后做出相应的调整。我们这里一样的用大根堆做示例。

注意:向下调整算法有一个前提(左右子树必须是一个堆,才能调整)
也就是说要执行向下调整必须要保证根节点的左右子树都是小根堆或大根堆我们才能执行向下调整。
此时根节点是2,2小于它的的孩子,那么我们就要和它的孩子交换位置,但是具体和哪个孩子交换位置呢?我们需要和两个孩子中大的那个孩子交换,那么我们就需要找到孩子节点的下标,还记得我们上面提到的通过父亲节点找孩子节点的公式吗。
LeftChild = Parent * 2 + 1; //左孩子的节点下标
RightChild = Parent * 2 + 2; //右孩子的节点下标

然后依次向下交换调整


计数排序

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,快于任何比较排序算法。

时间复杂度

时间复杂度是O(n+k):通过上面的代码可知最终的计数算法花费的时间是3n+k,则时间复杂度是O(n+k)。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序。

空间复杂度

空间复杂度是O(k):如果出去最后的返回数组,则空间复杂度是2k,则空间复杂度是O(k)

稳定性

由于统计数组可以知道该索引在原数组中排第几位,相同的元素其在原数组中排列在后面,其从原数组的后面遍历,其在最终数组中的索引也在后面,所以相同的元素其相对位置不会改变。计数排序是稳定排序

实现
void CountSort(int* a, int n)
{//寻找最大值和最小值int min = a[0], max = a[0];for (int i = 0; i < n; i++){if (a[i] < min){min = a[i];}if (a[i] > max){max = a[i];}}int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);if (count == NULL){perror("malloc fail");return;}//映射数据 统计个数memset(count, 0, sizeof(int) * range);for (int i = 0; i < n; i++){count[a[i] - min]++;}//排序int j = 0;for (int i = 0; i < range; i++){while (count[i]--){a[j++] = i + min;}}
}

计数排序最大的好处就是它的稳定性,在某些情况下时间复杂度可以做到惊人的O(1),比快速排序还要更快,能想出这种算法的人可谓是真正的佬。


最后放一张图供各位参考,可以保存起来方便以后快速查找


总结

创作不易,花了几个小时才写出来,感谢各位的三连关注,顺着评论回访🌹🌹

相关文章:

【C语言数据结构——————排序(1万字)】

文章目录 排序的概念 常见排序算法分类冒泡排序 时间复杂度稳定性 原理实现插入排序 时间复杂度稳定性实现选择排序 时间复杂度稳定性实现希尔排序 时间复杂度稳定性希尔排序的算法思想实现 优化快速排序 时间复杂度空间复杂度稳定性实现 三数取中优化归并排序 时间复杂度空间复…...

PyTorch基础(18)-- torch.stack()方法

一、方法详解 首先&#xff0c;看一下stack的直观解释&#xff0c;动词可以简单理解为&#xff1a;把……放成一堆、把……放成一摞。 有了对stack方法的直观感受&#xff0c;接下来&#xff0c;我们正式解析torch.stack方法。 PyTorch torch.stack() method joins (concaten…...

从lc560“和为 K 的子数组“带你认识“前缀和+哈希表“的解题思路

1 前缀和哈希表解题的几道题目&#xff1a;建议集中练习 560. 和为 K 的子数组&#xff1a;https://leetcode.cn/problems/subarray-sum-equals-k/ 1248. 统计「优美子数组」: https://leetcode.cn/problems/count-number-of-nice-subarrays/ 1249. 和可被 K 整除的子数组(利用…...

c:变参函数:汇编解析;va_list;marco 宏:__VA_ARGS__

文章目录 参考gcc 内部的宏定义代码汇编调用在 SEI CERT C Coding Standard 这个标准里示例实例宏里的使用 参考 https://git.sr.ht/~gregkh/presentation-security/blob/3547183843399d693c35b502cf4a313e256d0dd8/security-stuff.pdf gcc 内部的宏定义 宏定义&#xff1a;…...

eclipse安装教程(2021版)

第一步&#xff1a;下载JDK &#xff08;下载地址&#xff09; Java SE - Downloads 第二步 根据自己电脑的系统&#xff0c;选择相应的版本x64代表64位&#xff0c;x86代表32位。点击相应的JDK进行下载 点击之后会出现一个对话框 同意之后下载。(记住下载到哪&#xff0c;打…...

计算机网络重点概念整理-第二章 物理层【期末复习|考研复习】

第二章 物理层 【期末复习|考研复习】 计算机网络系列文章传送门&#xff1a; 第一章 计算机网络概述 第二章 物理层 第三章 数据链路层 第四章 网络层 第五章 传输层 第六章 应用层 第七章 网络安全 计算机网络整理-简称&缩写 文章目录 第二章 物理层 【期末复习|考研复习…...

【计算机网络】从输入URL到页面都显示经历了什么??

文字总结 ① DNS 解析&#xff1a;当用户输入一个网址并按下回车键的时候&#xff0c;浏览器获得一个域名&#xff0c;而在实际通信过程中&#xff0c;我们需要的是一个 IP 地址&#xff0c;因此我们需要先把域名转换成相应 IP 地址。浏览器会首先从缓存中找是否存在域名&…...

[C++]——带你学习类和对象

类和对象——上 目录&#xff1a;一、面向过程和面向对象二、类的概念三、类的访问限定符和封装3.1 访问限定符3.2 封装 四、类的作用域五、类的实例化六、类的对象大小的计算七、类成员函数this指针7.1 this指针的引用7.2 this 指针的特性 目录&#xff1a; 类和对象是很重要…...

Docker多平台、跨平台编译打包

大多数带有Docker官方标识的镜像都提供了多架构支持。如&#xff1a;busybox镜像支持amd64, arm32v5, arm32v6, arm32v7, arm64v8, i386, ppc64le, and s390x。当你在amd64设备上运行容器时&#xff0c;会拉取amd64镜像。 当你需要构建多平台镜像时&#xff0c;可以用 --platf…...

LLM系列 | 22 : Code Llama实战(下篇):本地部署、量化及GPT-4对比

引言 模型简介 依赖安装 模型inference 代码补全 4-bit版模型 代码填充 指令编码 Code Llama vs ChatGPT vs GPT4 小结 引言 青山隐隐水迢迢&#xff0c;秋尽江南草未凋。 小伙伴们好&#xff0c;我是《小窗幽记机器学习》的小编&#xff1a;卖热干面的小女孩。紧接…...

Nginx的进程结构实例演示

可以参考《Ubuntu 20.04使用源码安装nginx 1.14.0》安装nginx 1.14.0。 nginx.conf文件中worker_processes 2;这条语句表明启动两个worker进程。 sudo /nginx/sbin/nginx -c /nginx/conf/nginx.conf开启nginx。 ps -ef | grep nginx看一下进程情况。 sudo /nginx/sbin/ng…...

【Nginx36】Nginx学习:SSI静态文件服务器端包含模块

Nginx学习&#xff1a;SSI静态文件服务器端包含模块 这个模块让我想到了 2009 年刚刚工作的时候。最早我是做 .NET 的&#xff0c;而第一家公司其实是从 ASP 向 ASP.NET 转型中&#xff0c;因此&#xff0c;还是有不少的 ASP 做的页面。在那个时候&#xff0c;就用到了 SSI 。 …...

StripedFly恶意软件框架感染了100万台Windows和Linux主机

导语 近日&#xff0c;一款名为StripedFly的恶意软件框架在网络安全研究人员的监视之外悄然感染了超过100万台Windows和Linux系统。这款跨平台的恶意软件平台在过去的五年中一直未被察觉。在去年&#xff0c;卡巴斯基实验室发现了这个恶意框架的真实本质&#xff0c;并发现其活…...

蓝桥杯每日一题2023.10.25

乘积尾零 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 由于需要相乘的数很多&#xff0c;所以我们不能直接进行暴力模拟&#xff0c;我们知道10 2 * 5&#xff0c; 所以我们只需要找出这个数2和5的个数&#xff0c;其中2和5个数小的那个则为末尾0出现的个数 #include<bi…...

【C++】详解map和set基本接口及使用

文章目录 一、关联式容器与键值对1.1关联式容器&#xff08;之前学的都是序列容器&#xff09;1.2键值对pairmake_pair函数&#xff08;map在插入的时候会很方便&#xff09; 1.3树形结构的关联式容器 二、set2.1set的基本介绍2.1默认构造、迭代器区间构造、拷贝构造&#xff0…...

如何学习 Linux 内核内存管理

Linux内核内存管理部分是Linux内核中第二复杂的部分&#xff0c;但也非常有趣。学习它的最佳方法就是阅读代码。但在不了解术语和当前 mm 部分到底发生了什么的情况下&#xff0c;显然不能随意开始阅读代码。因此&#xff0c;我想这样开始学习比较好&#xff1a; 了解当前的 LS…...

【计算机网络】(谢希仁第八版)第一章课后习题答案

1.计算机网络可以向用户提供哪些服务&#xff1f; 答&#xff1a;例如音频&#xff0c;视频&#xff0c;游戏等&#xff0c;但本质是提供连通性和共享这两个功能。 连通性&#xff1a;计算机网络使上网用户之间可以交换信息&#xff0c;好像这些用户的计算机都可以彼此直接连…...

Operator开发之operator-sdk入门

1 operator-sdk 除了kubebuilder&#xff0c;operator-sdk是另一个常用的用于开发Operator的框架&#xff0c;不过operator-sdk还是基于kubebuilder&#xff0c;因此&#xff0c;通常还是建议使用kubebuilder开发Operator。 2 环境准备 跟kubebuilder类似&#xff0c;需要安…...

RabbitMQ生产者的可靠性

目录 MQ使用时会出现的问题 生产者的可靠性 1、生产者重连 2、生产者确认 3、数据持久化 交换机持久化 队列持久化 消息持久化 LazyQueue懒加载 MQ使用时会出现的问题 发送消息时丢失&#xff1a; 生产者发送消息时连接MQ失败生产者发送消息到达MQ后未找到Exchange生…...

集群节点批量执行 shell 命令

1、SSH 工具本身支持多窗口 比如 MobaXterm&#xff1a; 2、编写脚本通过 ssh 在多台机器批量执行shell命令 创建 ssh_hosts 配置文件&#xff0c;定义需要批量执行的节点&#xff08;必须能够通过 ssh 免密登录&#xff0c;且存在同名用户&#xff09; vim ssh_hostsbig…...

fl studio21.2水果软件怎么设置中文?

FL Studio编曲软件真的是个神器&#xff0c;不过一开始打开看到全是英文&#xff0c;有点头大&#xff0c;对吧&#xff1f;其实切换成中文版超级简单&#xff0c;只需要几个步骤就搞定啦&#xff01;我自己也是用中文版的&#xff0c;觉得用起来更得心应手&#xff0c;效率也提…...

.NET CORE 3.1 集成JWT鉴权和授权2

JWT&#xff1a;全称是JSON Web Token是目前最流行的跨域身份验证、分布式登录、单点登录等解决方案。 通俗地来讲&#xff0c;JWT是能代表用户身份的令牌&#xff0c;可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限。 授权&#xff1a;这是使用JWT的…...

nbcio-boot如何进行gitee第三方登录

更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 1、用户g…...

【C语言】字符函数、字符串函数与内存函数

简单不先于复杂&#xff0c;而是在复杂之后。 目录 0. 前言 1. 函数介绍 1.1 strlen 1.1.1 介绍 1.1.2 strlen 函数模拟实现 1.1.2.1 计数器方法 1.1.2.2 递归方法 1.1.2.3 指针 - 指针方法 1.2 strcpy 1.2.1 介绍 1.2.2 strcpy 函数模拟实现 1.3 strcat 1…...

生成树协议:监控 STP 端口和交换机

什么是生成树协议 生成树协议 &#xff08;STP&#xff09; 用于网络交换机&#xff0c;以防止循环和广播风暴。在局域网 &#xff08;LAN&#xff09; 中&#xff0c;两条或多条冗余路径可以连接到同一网段。当交换机或网桥从所有可用端口传输帧时&#xff0c;这些帧开始在网…...

【黑产攻防道03】利用JS参数更新检测黑产的协议破解

任何业务在运营一段时间之后都会面临黑产大量的破解。验证码和各种爬虫的关系就像猫和老鼠一样, 会永远持续地进行博弈。极验根据十一年和黑产博弈对抗的经验&#xff0c;将黑产的破解方式分为三类&#xff1a; 1.通过识别出验证码图片答案实现批量破解验证&#xff0c;即图片…...

什么是web3.0?

Web 3.0&#xff0c;也常被称为下一代互联网&#xff0c;代表着互联网的下一个重大演变。尽管关于Web 3.0的确切定义尚无共识&#xff0c;但它通常被认为是一种更分散、更开放且更智能的互联网。 以下是Web 3.0的一些主要特征和概念&#xff1a; 1. 去中心化 Web 3.0旨在减少…...

二、W5100S/W5500+RP2040树莓派Pico<DHCP>

文章目录 1 前言2 简介2 .1 什么是DHCP&#xff1f;2.2 为什么要使用DHCP&#xff1f;2.3 DHCP工作原理2.4 DHCP应用场景 3 WIZnet以太网芯片4 DHCP网络设置示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1 前言 …...

【开源】基于SpringBoot的天然气工程业务管理系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、使用角色3.1 施工人员3.2 管理员 四、数据库设计4.1 用户表4.2 分公司表4.3 角色表4.4 数据字典表4.5 工程项目表4.6 使用材料表4.7 使用材料领用表4.8 整体E-R图 五、系统展示六、核心代码6.1 查询工程项目6.2 工程物资…...

讯飞星火大模型V3.0 WebApi使用

讯飞星火大模型V3.0 WebApi使用 文档说明&#xff1a;星火认知大模型Web文档 | 讯飞开放平台文档中心 (xfyun.cn) 实现效果 初始化 首先构建一个基础脚手架项目 npm init vuelatest用到如下依赖 "dependencies": {"crypto-js": "^4.2.0",&q…...