数据结构与算法(持续更新)

线性表

单链表

单链表的定义
由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。

单链表的特点
1.单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此不需要连续的存储空间。
2.单链表是非随机的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。

**单链表操作**#include<iostream>
using namespace std;typedef struct LNode{int data;struct LNode *next;
}LNode,*LinkList;//初始化 
void InitList(LinkList &L){L=(LNode *)malloc(sizeof(LinkList));L->next=NULL;
}//头插法建表
LinkList HeadInsert(LinkList &L){InitList(L);int x;while(cin>>x){LNode *s=(LNode *)malloc(sizeof(LNode));s->data=x;s->next=L->next;L->next=s;}return L;
}//尾插法建表
LinkList TailInsert(LinkList &L){InitList(L);LNode *s,*r=L;int x;while(cin>>x){s=(LNode *)malloc(sizeof(LNode));s->data=x;r->next=s;r=s;}r->next=NULL;return L;
} //输出表中元素
void PrintList(LinkList L){LNode *p=L->next;while(p!=NULL){cout<<p->data<<" ";p=p->next;}
} //求单链表的长度
int Length(LinkList L){LNode *p = L->next;int len = 0;while(p!=NULL){len++;p = p->next;}return len;
}//按值查找:查找x在L中的位置
LNode *LocateElem(LinkList L, int x){LNode *p = L->next;while(p!=NULL&&p->data!=x){p = p->next;}return p;
}//按位查找:查找在单链表L中第i个位置的结点
LNode *GetElem(LinkList L, int i){int j=1;LNode *p = L->next;if(i==0){return L;}if(i<1){return NULL;}while(p!=NULL&&j<i){p = p->next;j++;}return p;
}//判空操作
void Empty(LinkList L){if(L->next == NULL){cout<<"L is null"<<endl;}else{cout<<"L is not null"<<endl;}
}//将x插入到单链表L的第i个位置上
void Insert(LinkList &L, int i, int x){LNode *p = GetElem(L,i-1);LNode *s = (LNode *)malloc(sizeof(LNode));s->data = x;s->next = p->next;p->next = s;
}//删除操作:将单链表中的第i个结点删除
void Delete(LinkList &L, int i){if(i<0||i>Length(L)){cout<<"数据有误"; return ;}LNode *p = GetElem(L,i-1);LNode *q = p->next;p->next = q->next;free(q);
}int main(){**自定义补充**return 0;
}

双链表

循环链表

栈、队列、数组

查找

排序

排序算法可以分为内部排序和外部排序,其中内部排序有:插入排序(直接插入排序、折半插入排序、希尔排序)、交换排序(冒泡排序、快速排序)、选择排序(简单选择排序、堆排序)、归并排序、基数排序;外部排序有:多路归并排序。

插入排序

插入排序是一种简单直观的排序方法,其基本思想是每次将一个待排序的记录按其关键字大小插入前面已排好序的子序列,直到全部记录插入完成。由插入排序的思想可以引申出三个重要的排序算法:直接插入排序、折半插入排序和希尔排序。

直接插入排序

要将元素 L(i)插入已有序的子序列 L[1…i-1],需要执行以下操作(为避免混,下面用L[ ]表示一个表,而用 L( )表示一个元素):
1)查找出L(i)在L[l…i-1]中的插入位置k。
2)将L[k…i-1]中的所有元素依次后移一个位置。
3)将L(i)复制到L(k)。

#include<iostream>
using namespace std;void InsertSort(int a[],int len){int temp,i,j;for(i=1;i<len;i++){if(a[i]<a[i-1]){temp=a[i];for(j=i-1;temp<a[j];j--){a[j+1]=a[j];	}a[j+1]=temp;}}
} int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);InsertSort(a,l);for(int i=0;i<l;i++){cout<<a[i]<<" ";} return 0;
}

时间复杂度:O(n^2) 最好情况下:O(n)
空间复杂度:O(1)
稳定性:由于每次插入元素时总是从后向前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序方法。
适用性:直接插入排序算法适用于顺序存储和链式存储的线性表。为链式存储时,可以从前往后查找指定元素的位置。

折半插入排序

从直接插入排序算法中,不难看出每趟插入的过程中都进行了两项工作:1.从前面的有序子表中查找出待插入元素应该被插入的位置;2.给插入位置腾出空间,将待插入元素复制到表中的插入位置。注意到在该算法中,总是边比较边移动元素。下面将比较和移动操作分离,即先折半查找出元素的待插入位置,然后统一地移动待插入位置之后的所有元素。当排序表为顺序表时,可以对直接插入排序算法做如下改进:由于是顺序存储的线性表,所以查找有序子表时可以用折半查找来实现。确定待插入位置后,就可统一地向后移动元素。

#include<iostream>
using namespace std;void InsertSort(int a[],int len){int temp,low,high,mid;for(int i=1;i<len;i++){temp=a[i];low=0,high=i-1;while(low<=high){mid=(low+high)/2;if(a[mid]>temp){high=mid-1;}else{low=mid+1;}}for(int j=i-1;j>=high+1;j--){a[j+1]=a[j];}a[high+1]=temp;}
}int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);InsertSort(a,l);for(int i=0;i<l;i++){cout<<a[i]<<" ";}return 0;
}

从上述算法中,不难看出折半插入排序仅减少了比较元素的次数,约为 O(nlogn),该比较次数与待排序表的初始状态无关,仅取决于表中的元素个数n;而元素的移动次数并未改变,它依赖于待排序表的初始状态。因此,折半插入排序的时间复杂度仍为 O(n^2),但对于数据量不很大的排序表,折半插入排序往往能表现出很好的性能。折半插入排序是一种稳定的排序方法。

希尔排序

希尔排序的基本思想是:先将待排序表分割成若干形如L[i,i+d,i+2d,i+3d,…,i+kd]的“特殊”子表,即把相隔某个“增量”的记录组成一个子表,对各个子表分别进行直接插入排序,当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。
希尔排序的过程如下:先取一个小于n 的步长 d1,把表中的全部记录分成d1组,所有距离为d1的倍数的记录放在同一组,在各组内进行直接插入排序;然后取第二个步长 d2<d1,重复上述过程,直到所取到的di=1,即所有记录已放在同一组中,再进行直接插入排序,由于此时已经具有较好的局部有序性,故可以很快得到最终结果。

#include<iostream>
using namespace std;void InsertSort(int a[],int len){for(int d=len/2;d>=1;d=d/2){int temp,i,j;for(i=d+1;i<len;i++){if(a[i]<a[i-d]){temp=a[i];for(j=i-d;j>0&&temp<a[j];j-=d){a[j+d]=a[j];	}a[j+d]=temp;}}}
} int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);		InsertSort(a,l);for(int i=0;i<l;i++){cout<<a[i]<<" ";}return 0;
}

空间效率:仅使用了常数个辅助单元,因而空间复杂度为 O(1)。
时间效率:由于希尔排序的时间复杂度依赖于增量序列的函数,这涉及数学上尚未解决的难题,所以其时间复杂度分析比较困难。当n在某个特定范围时,希尔排序的时间复杂度约为O(n^1.3)。在最坏情况下希尔排序的时间复杂度为 O(n^2)。
稳定性:当相同关键字的记录被划分到不同的子表时,可能会改变它之间的相对次序,因此希尔排序是一种不稳定的排序方法。
适用性:希尔排序算法仅适用于线性表为顺序存储的情况。

交换排序

所谓交换,是指根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。基于交换的排序算法很多,主要有冒泡排序和快速排序。

冒泡排序

冒泡排序的基本思想是:从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),则交换它们,直到序列比较完。我们称它为第一趟冒泡,结果是将最小的元素交换到待排序列的第一个位置(或将最大的元素交换到待排序列的最后一个位置),关键字最小的元素如气泡一般逐渐往上“漂浮”直至“水面”(或关键字最大的元素如石头一般下沉至水底)。下一趟冒泡时,前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列中的最小元素(或最大元素)放到了序列的最终位置…这样最多做n-1趟冒泡就能把所有元素排好序。

#include<iostream>
using namespace std;void BubbleSort(int a[],int len){for(int i=0;i<len-1;i++){for(int j=0;j<len-i;j++){if(a[j+1]<a[j]){swap(a[j+1],a[j]);}}}
} int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);		BubbleSort(a,l);for(int i=0;i<l;i++){cout<<a[i]<<" ";}return 0;
}

算法改进:由上述的示例(数组元素为:1,3,4,2,6,5,9,8,7)可知,其第二趟的结果(1,2,3,4,5,6,7,8,9)已经有序,后面再进行排序其实是重复动作。因此,当某一趟的结果已经有序时,表示所给序列已经有序,不必要再进行下一趟排序。

void BubbleSort(int a[],int len){for(int i=0;i<len-1;i++){bool flag=false; for(int j=0;j<len-i;j++){if(a[j+1]<a[j]){swap(a[j+1],a[j]);flag=true;}}if(flag==false){return ;}}
} 

空间效率:仅使用了常数个辅助单元,因而空间复杂度为 O(1)。
时间效率:当初始序列有序时,显然第一趟冒泡后flag依然为false(本趟冒泡没有元素交换),从而直接跳出循环,比较次数为n-1,移动次数为0,从而最好情况下的时间复杂度为O(n);当初始序列为逆序时,需要进行n-1趟排序,第i趟排序要进行n-i次关键字的比较,而且每次比较后都必须移动元素3次来交换元素位置。这种情况下的时间复杂度为 O(n^2),其平均时间复杂度也为 O(n^2)。
稳定性:由于i>j且a[i]=a[j]时,不会发生交换,因此冒泡排序是一种稳定的排序方法。

快速排序

快速排序的基本思想是基于分治法的:在待排序表 L[l…n]中任取一个元素pivot作为枢轴(或基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分L[1…k-1]和L[k+l…n],使得L[l…k-1]中的所有元素小于pivot,L[k+l…n]中的所有元素大于等于pivot,则pivot放在了其最终位置 L(k)上,这个过程称为一趟快速排序(或一次划分)。然后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

#include<iostream>
using namespace std;void QuickSort(int a[],int low,int high){int i=low,j=high,pivot=a[low];if(low>=high){return ;}while(i<j){while(a[j]>=pivot&&i<j){j--;}a[i]=a[j];while(a[i]<=pivot&&i<j){i++;}a[j]=a[i];}a[i]=pivot;QuickSort(a,low,i-1);QuickSort(a,i+1,high);
}int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);		QuickSort(a,0,l-1);for(int i=0;i<l;i++){cout<<a[i]<<" ";}return 0;
}
**快速排序另一种写法:**int Partition(int a[],int low,int high){int pivot=a[low];while(low<high){while(a[high]>pivot&&low<high){high--;}a[low]=a[high];while(a[low]<pivot&&low<high){low++;}a[high]=a[low];}a[low]=pivot;return low;
}void QuickSort(int a[],int low,int high){if(low<high){int pivotpos = Partition(a,low,high);QuickSort(a,low,pivotpos-1);QuickSort(a,pivotpos+1,high);}
}

选择排序

选择排序的基本思想是:每一趟(如第i趟)在后面n-i+1(i=1,2,…,n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第n-1趟做完,待排序元素只剩下1个,就不用再选了。选择排序主要有简单选择排序和堆排序。

简单选择排序

简单选择排序算法的思想:假设排序表为L[1…n],第i趟排序即从L[i…n]中选择关键字最小的元素与L(i)交换,每一趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可使得整个排序表有序。

#include<iostream>
using namespace std;void SelectSort(int a[],int len){for(int i=0;i<len-1;i++){int min=i;for(int j=i+1;j<len;j++){if(a[j]<a[min]){min=j;}}if(min!=i){swap(a[i],a[min]);}}
} int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);		SelectSort(a,l);for(int i=0;i<l;i++){cout<<a[i]<<" ";}return 0;
}

空间效率:仅使用常数个辅助单元,故空间效率为0(1)。
时间效率:从上述代码中不难看出,在简单选择排序过程中,元素移动的操作次数很少,不会超过3(n-1)次,最好的情况是移动0次,此时对应的表已经有序,但元素间比较的次数与序列的初始状态无关,始终是 n(n-1)/2 次,因此时间复杂度始终是 O(n^2)。
稳定性:在第i趟找到最小元素后,和第 i个元素交换,可能会导致第i个元素与其含有相同关键字元素的相对位置发生改变。例如,表L={2,2,1},经过一趟排序后L={1,2,2},最终排序序列也是L={1,2,2},显然,2与2的相对次序已发生变化。因此,简单选择排序是一种不稳定的排序方法。

堆排序

的定义如下,n个关键字序列L[1…n]称为堆,当且仅当该序列满足:
1.L(i)>=L(2i)且L(i)>=L(2i+1)或
2.L(i)<=L(2i)且L(i)<=(2i+1) (1 <= i <= n/2)
堆排序的思路很简单:首先将存放在L[1…n]中的n个元素建成初始堆,由于堆本身的特点(以大顶堆为例),堆顶元素就是最大值。输出堆顶元素后,通常将堆底元素送入堆顶,此时根结点已不满足大顶堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大顶堆的性质,再输出堆顶元素。如此重复,直到堆中仅剩一个元素为止。可见堆排序需要解决两个问题:1.如何将无序序列构造成初始堆?2.输出堆顶元素后,如何将剩余元素调整成新的堆?
堆排序的关键是构造初始堆。n个结点的完全二叉树,最后一个结点是第n/2个结点的孩子。对第n/2个结点为根的子树筛选(对于大根堆,若根结点的关键字小于左右孩子中关键字较大者,则交换),使该子树成为堆。之后向前依次对各结点(n/2-1,…,1)为根的子树进行筛选,看该结点值是否大于其左右子结点的值,若不大于,则将左右子结点中的较大值与之交换,交换后可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到以该结点为根的子树构成堆为止。反复利用上述调整堆的方法建堆,直到根结点。

#include<iostream>
using namespace std;void HeapAdjust(int a[],int k,int len){a[0]=a[k];for(int i=2*k;i<=len;i*=2){if(i<len&&a[i]<a[i+1]){i++;	}if(a[0]>=a[i]){break;}else{a[k]=a[i];k=i;}}a[k]=a[0];
}void BuildMaxHeap(int a[],int len){for(int i=len/2;i>0;i--){HeapAdjust(a,i,len);}
}void HeapSort(int a[],int len){BuildMaxHeap(a,len);for(int i=len;i>1;i--){swap(a[i],a[1]);HeapAdjust(a,1,i-1);}
} int main(){int b[]={1,3,4,2,6,5,9,8,7};int a[100],l=sizeof(b)/sizeof(int);for(int i=0;i<l;i++){a[i+1]=b[i];}HeapSort(a,l);for(int i=1;i<=l;i++){cout<<a[i]<<" ";}return 0;
} 

空间效率:仅使用了常数个辅助单元,所以空间复杂度为 O(1)。
时间效率:建堆时间为 O(n),之后有 n-1次向下调整操作,每次调整的时间复杂度为 O(h),故在最好、最坏和平均情况下,堆排序的时间复杂度为O(nlogn)。
稳定性:进行筛选时,有可能把后面相同关键字的元素调整到前面,所以堆排序算法是一种不稳定的排序方法。例如,表L={1,2,2},构造初始堆时可能将2交换到堆顶,此时L={2,1,2},最终排序序列为 L={1,2,2},显然,2与2的相对次序已发生变化。

归并排序

归并排序是比较稳定的排序方法。它的基本思想是把待排序的元素分解成两个规模大致相等的子序列。如果不易分解,将得到的子序列继续分解,直到子序列中包含的元素个数为1。因为单个元素的序列本身就是有序的,此时便可以进行合并,从而得到一个完整的有序序列。

分治算法
归并排序,其实就是一种分治算法,那么在了解归并排序之前,我们先来看看什么是分治算法。在算法设计中,我们引入分而治之的策略,称为分治算法,其本质就是将一个大规模的问题分解为若干个规模较小的相同子问题,分而治之。
分治算法解题
1.分解:将要解决的问题分解为若干个规模较小、相互独立、与原问题形式相同的子问题。
2.治理:求解各个子问题。由于各个子问题与原问题形式相同,只是规模较小而已,而当子问题划分得足够小时,就可以用简单的方法解决。
3.合并:按原问题的要求,将子问题的解逐层合并构成原问题的解。

归并排序解题步骤
步骤一:首先将待排序的元素分成大小大致相同的两个序列。
步骤二:再把子序列分成大小大致相同的两个子序列。
步骤三:如此下去,直到分解成一个元素停止,这时含有一个元素的子序列都是有序的。
步骤四:进行合并操作,将两个有序的子序列合并为一个有序序列,如此下去,直到所有的元素都合并为一个有序序列。

数据模拟,下面我将以序列(4,9,15,24,30,2,6,18,20)进行图解

1、初始化:i = low,j = mid+1,mid = (low+high)/2 ,申请一个辅助数组 b

在这里插入图片描述

int b[high-low+1];  
int i = low, j = mid + 1, k = 0;    

2、现在比较 a[i] 和 b[j],将较小的元素放在 b 数组中,相应的指针向后移动,直到 i > mid 或者 j>hight 时结束。

while (i <= mid && j <= high) {if (a[i] <= a[j]) {b[k++] = a[i++]; } else {b[k++] = a[j++];}
}

进行第一次比较 a[i]=4 和 a[j]=2,将较小的元素 2 放入数组 b 中,j++,k++,如下图:

在这里插入图片描述

进行第二次比较 a[i]=4 和 a[j]=6,将较小的元素放 4 入数组 b 中,i++,k++,如下图:

在这里插入图片描述

进行第三次比较 a[i]=9 和 a[j]=6,将较小的元素放 6 入数组 b 中,j++,k++,如下图:

在这里插入图片描述

进行第四次比较 a[i]=9 和 a[j]=18,将较小的元素放 9 入数组 b 中,i++,k++,如下图:

在这里插入图片描述

进行第五次比较 a[i]=15 和 a[j]=18,将较小的元素放 15 入数组 b 中,i++,k++,如下图:

在这里插入图片描述

进行第六次比较 a[i]=24 和 a[j]=18,将较小的元素放 18 入数组 b 中,j++,k++,如下图:

在这里插入图片描述

进行第七次比较 a[i]=24 和 a[j]=20,将较小的元素放 20 入数组 b 中,j++,k++,如下图:

在这里插入图片描述

此时,j>hight 了,while循环结束,但 a 数组还剩下元素(i<mid)可直接放入 b 数组就可以了。如下图所示:

在这里插入图片描述

while (i <= mid){                         // j 序列结束,将剩余的 i 序列补充在 b 数组中 b[k++] = a[i++];
}
while (j <= high){                        // i 序列结束,将剩余的 j 序列补充在 b 数组中 b[k++] = a[j++];
}

现在比较 a[i] 和 b[j] ,将较小的元素放在 b 数组中,相应的指针向后移动,直到 i > mid 或者 j > hight 时结束。

while (i <= mid && j <= high) {if (a[i] <= a[j]) {b[k++] = a[i++]; } else {b[k++] = a[j++];}
}

现在将 b 数组的元素赋值给 a 数组,再将 b 数组销毁,即可。

for (int i = low; i <= hight; i++)  {          //将 b 数组的值传递给数组 aa[i] = b[k++];
}
delete[]b;               // 辅助数组用完后,将其的空间进行释放(销毁)

3、递归的形式进行归并排序

void MergeSort(int a[ ],int low,int high){if(low<high){int mid=(low+high)/2;MergeSort(a,low,mid);MergeSort(a,mid+1,high);Merge(a,low,mid,high);}
}
**完整代码**
#include<iostream>
using namespace std;void Merge(int a[],int low,int mid,int high){int *b=new int[high-low+1];               //用 new 申请一个辅助函数int i = low, j = mid + 1, k = 0;          // k为 b 数组的小标while (i <= mid && j <= high){if (a[i] <= a[j]){b[k++] = a[i++];                 //按从小到大存放在 b 数组里面}else{b[k++] = a[j++];}}while (i <= mid){                        // j 序列结束,将剩余的 i 序列补充在 b 数组中 b[k++] = a[i++];}while (j <= high){                       // i 序列结束,将剩余的 j 序列补充在 b 数组中 b[k++] = a[j++];}k = 0;                                   //从小标为 0 开始传送for (int i = low; i <= high; i++){       //将 b 数组的值传递给数组 aa[i] = b[k++];}delete []b;                              //销毁 
}void MergeSort(int a[],int low,int high){if(low<high){int mid=(low+high)/2;MergeSort(a,low,mid);MergeSort(a,mid+1,high);Merge(a,low,mid,high);}
}int main(){int a[]={1,3,4,2,6,5,9,8,7};int l=sizeof(a)/sizeof(int);MergeSort(a,0,l-1);for(int i=0;i<l;i++){cout<<a[i]<<" ";}return 0;
}

基数排序

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

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

相关文章

Flink---12、状态后端(HashMapStateBackend/RocksDB)、如何选择正确的状态后端

星光下的赶路人star的个人主页 大鹏一日同风起&#xff0c;扶摇直上九万里 文章目录 1、状态后端&#xff08;State Backends&#xff09;1.1 状态后端的分类&#xff08;HashMapStateBackend/RocksDB&#xff09;1.2 如何选择正确的状态后端1.3 状态后端的配置 1、状态后端&am…

APP自动化之Poco框架

今天给大家介绍一款自动化测试框架Poco&#xff0c;其脚本写法非常简洁、高效&#xff0c;其元素定位器效率更快&#xff0c;其本质基于python的第三方库&#xff0c;调试起来也会非常方便&#xff0c;能够很好的提升自动化测试效率&#xff0c;节省时间。 (一&#xff09;背景…

Zabbix 监控系统安装和部署

Zabbix 监控系统安装和部署 一、zabbix 是什么&#xff1f;1.1、zabbix 监控原理&#xff08;重点&#xff09;1.2、Zabbix 6.0 新特性1.3、Zabbix 6.0 功能组件1.4、数据库1.5、Web 界面1.6、Zabbix Agent1.7、Zabbix Proxy1.8、Java Gateway 二、部署Zabbix 6.02.1、 解决 za…

SQL监控工具

什么是 SQL 监控 SQL 监视是跟踪和分析整个 MSSQL 生态系统的过程&#xff0c;以识别性能问题并防止依赖数据库的应用程序变慢和/或遇到中断&#xff0c;它有助于获取有关 SQL 服务器的数据库会话、查询、作业、CPU 和内存资源、群集、配置和可用性组的信息。 为什么 MSSQL 监…

Redis-缓存穿透,缓存击穿,缓存雪崩

缓存穿透&#xff0c;缓存击穿&#xff0c;缓存雪崩 缓存穿透处理方案解决方案1 缓存空数据解决方案2 布隆过滤器 缓存击穿处理方案解决方案 1 互斥锁解决方案2 逻辑过期 缓存雪崩处理方案解决方案 1 给不同的key的过期时间设置添加一个随机值&#xff0c;降低同一个时段大量ke…

cv2.split函数与cv2.merge函数

split函数用于图像BGR通道的分离 merge函数用于可将分开的图像通道合并到一起 1.split函数的使用 这是原图&#xff0c;我们使用split函数对其三个通道进行分离。 注意&#xff1a;split函数分离通道的顺序是B、G、R。 以下方法是将三个通道的值都设置为与某一个通道相同。…

基于双二阶广义积分器的三相锁相环(DSOGI-PLL)Simulink仿真

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

虚拟机模拟部署服务器

1、下载安装vmware 15 &#xff08;win7最高支持版&#xff09; 2、下载安装CentOS 配置2核2g&#xff08;最少&#xff09;磁盘100g&#xff08;不会实际占有&#xff09;选择时区配置分区 https://blog.csdn.net/qq_35363507/article/details/127390889 &#xff08;/boot …

Java 华为真题-小朋友分班

需求&#xff1a; 题目描述 幼儿园两个班的小朋友在排队时混在了一起&#xff0c;每位小朋友都知道自己是否与前面一位小朋友同班&#xff0c;请你帮忙把同班的小朋友找出来小朋友的编号是整数&#xff0c;与前一位小朋友同班用Y表示&#xff0c;不同班用N表示学生序号范围(0&…

旁注、越权、跨库、CDN相关

旁注原理 在同一服务器上有多个站点&#xff0c;我们要攻击的这个站点假设没有漏洞&#xff0c;我们可以攻击服务器上的任意一个站点&#xff0c;这个就是旁注 多端口需要知道IP 可以用尖刀&#xff0c;fscan,goby 探测 IP逆向查询&#xff08;知道域名&#xff09; 可通过pin…

Java版 招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计

功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外部供…

基于or-tools的人员排班问题建模求解(JavaAPI)

使用Java调用or-tools实现了阿里mindopt求解器的案例&#xff08;https://opt.aliyun.com/platform/case&#xff09;人员排班问题。 这里写目录标题 人员排班问题问题描述数学建模编程求解&#xff08;ortoolsJavaAPI&#xff09;求解结果 人员排班问题 随着现在产业的发展&…

ToBeWritten之让响应团队参与并做好沟通

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

数据结构与算法-前缀树

数据结构与算法-前缀树详解 1 何为前缀树 2 前缀树的代码表示及相关操作 1 何为前缀树 前缀树 又称之为字典树,是一种多路查找树,多路树形结构,是哈希树的变种&#xff0c;和hash效率有一拼&#xff0c;是一种用于快速检索的多叉树结构。 性质&#xff1a;不同字符串的相同…

力扣 -- 5. 最长回文子串

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:string longestPalindrome(string s) {int ns.size();vector<vector<bool>> dp(n,vector<bool>(n));//最长回文串的起始位置int start0;//最长回文串的长度int len0;for(int in-1;i>…

一道经典的指针笔试题!!!!

文章目录 写在前面1. 笔试题代码2. 代码解释3. 代码执行运行结果总结 写在前面 本篇文章讲解了一道关于指针和数组的经典笔试题。 前两篇关于指针和数组的讲解&#xff0c;链接如下&#xff1a; 详解C语言指针&#xff08;一&#xff09; 详解C语言指针&#xff08;二&#xf…

紧固行业内卷严重,张友君的飞沃科技能独善其身吗?

文&#xff5c;新熔财经 作者&#xff5c;文泽 “历经转折”的飞沃科技(301232.SZ)于今年6月&#xff0c;登陆资本市场。 公开资料显示&#xff0c;飞沃科技主要从事风电类高强度紧固件业务&#xff0c;主要产品包括预埋螺套、整机螺栓、锚栓组件。公司的实际控制人是张友君…

端口隔离 MAC地址安全配置

二、知识点 目前网络中以太网技术的应用非常广泛。然而&#xff0c;各种网络攻击的存在&#xff08;例如针对ARP、DHCP等协议的攻击&#xff09;&#xff0c;不仅造成了网络合法用户无法正常访问网络资源&#xff0c;而且对网络信息安全构成严重威胁&#xff0c;因此以太网交…

美容美甲小程序商城的作用是什么

美容院往往有很高需求&#xff0c;女性悦己经济崛起&#xff0c;加之爱美化程度提升&#xff0c;无论线下环境还是线上互联网信息冲击&#xff0c;美容服务、化妆产品等市场规格一直稳增不减。 通过【雨科】平台制作美容美甲商城&#xff0c;售卖相关服务/产品&#xff0c;模块…

【多级缓存】

文章目录 1. JVM进程缓存2. Lua语法3. 实现多级缓存3.1 反向代理流程3.2 OpenResty快速入门 4. 查询Tomcat4.1 发送http请求的API4.2 封装http工具4.3 基于ID负载均衡4.4 流程小结 5. Redis缓存查询5.1 实现Redis查询 6. Nginx本地缓存6.1 本地缓存API6.2 实现本地缓存查询 7. …