算法设计1_分治

递归的概念

  • 递归算法:一个直接或间接地调用自身的算法
  • 递归函数:使用函数自身给出定义的函数
  • 递归方程:对于递归算法,一般可把时间代价表示为一个递归方程
  • 解递归方程最常用的方法是进行递归扩展 阶乘函数
    1. 边界条件
    2. 递归关系

n ! = { 1 , n = 1 n ( n − 1 ) ! , n > 1 n!=\begin{cases} 1,n=1\\ n(n-1)!,n>1\end{cases} n!={1,n=1n(n1)!,n>1

Fibonacci数列 F ( n ) = { 1 , n = 0 1 , n = 1 F ( n − 1 ) + F ( n − 2 ) , n > 1 F(n)=\begin{cases} 1,n=0\\ 1,n=1\\F(n-1)+F(n-2),n>1\end{cases} F(n)= 1,n=01,n=1F(n1)+F(n2),n>1

初始条件递归方程是递归函数的两个要素

Ackerman函数 { A ( 1 , 0 ) = 2 A ( 0 , m ) = 1 , m > = 0 A ( n , 0 ) = n + 2 , n > = 2 A ( n , m ) = A ( A ( n − 1 , m ) , m − 1 ) , n 、 m > = 1 \begin{cases} A(1,0)=2\\ A(0,m)=1,m>=0\\A(n,0)=n+2,n>=2\\A(n,m)=A(A(n-1,m),m-1),n、m>=1\end{cases} A(1,0)=2A(0,m)=1,m>=0A(n,0)=n+2,n>=2A(n,m)=A(A(n1,m),m1),nm>=1

  • Ackerman函数为双递归函数
  • 双递归函数:一个函数及它的一个变量由函数自身定义
  • A(n,m)的自变量m的每一个值都定义了一个单变量函数

注:需推一遍证明

排列问题

设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R-{ri}。集合X中的元素的全排列记为Perm(X)。

(ri)Perm(X)表示在全排列Perm(X)的每个排列前加上前缀ri得到的排列。

R的全排列定义如下:

当n=1时,Perm®=®,其中r是集合R中唯一的元素;

当n>1时,Perm®由(r1)Perm(R1),(r2)Perm(R2),…,(rn)Perm(Rn)构成。

为便于理解,以{1,2,3,4,5,6}为例:

1 2 3 4 5 6 –> 1 2 3 4 5 6 –> 1 2 3 4 5 6 –> 1 2 3 4 5 6 –> 1 2 3 4 5 6 –> 1 2 3 4 5 6

–> 1 2 3 4 6 5 –> 1 2 3 4 6 5

template<class Type>
void Perm(Type list[],int k, int m){			//产生list[k:m]的所有排列if(k==m){									//只剩下一个元素,到达递归的最底层for (int i=0;i<=m;i++)cout << list[i];cout << endl;}else{										//还有多个元素待排列,递归产生排列for (int i=k;i<=m;i++){Swap(list[k],list[i]);Perm(list,k+1,m);Swap(list[k],list[i]);				//复位,保证所有元素都能依次做前缀}}
}template<class Type>
inline void Swap(Type &a, Type &b)
{Type temp=a;a = b;b = temp;
}

整数划分问题

将正整数n表示成一系列正整数之和,n=n1+n2+…+nk(n1≥n2≥…≥nk,k≥1)。

正整数n的不同的划分个数称为正整数n的划分数,记为p(n)。以正整数6为例:

6;

5+1;

4+2;4+1+1;

3+3;3+2+1;3+1+1+1;

2+2+2;2+2+1+1;2+1+1+1+1;

1+1+1+1+1+1。

将最大加数n1不大于m的划分个数记作q(n,m),建立如下递归关系:
q ( n . m ) = { 1 , n = 1 、 m = 1 q ( n , n ) , n < m 1 + q ( n , n − 1 ) , n = m q ( n , m − 1 ) + q ( n − m , m ) , n > m > 1 q(n.m)=\begin{cases} 1,n=1、m=1\\q(n,n),n<m\\1+q(n,n-1),n=m\\q(n,m-1)+q(n-m,m),n>m>1\end{cases} q(n.m)= 1,n=1m=1q(n,n),nm1+q(n,n1),n=mq(n,m1)+q(nm,m),nm1

  1. q(n,1)=1,n≥1。当最大加数n1不大于1时,任何正整数n只有一种划分形式,即n=1+1+…+1
  2. q(n,m)=q(n,n),m≥n。最大加数n1不能大于n。
  3. q(n,n)=1+q(n,n-1)。正整数n的划分由n1=n的划分和n1≤n-1的划分组成;n1=n时,划分仅有一种。
  4. q(n,m-1)+q(n-m,m),n>m>1。正整数n的最大加数n1不大于m的划分由n1=m的划分和n1≤m-1的划分组成。

Hanoi塔问题

设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置。在移动圆盘时遵守:

  1. 每次只移动1个圆盘
  2. 任何时刻都不允许将较大的圆盘压到较小的圆盘之上
  3. 在满足1和2的前提下,可将圆盘移动至a,b,c中任一塔座上

T ( n ) = { 2 T ( n − 1 ) + 1 , n > 1 1 , n = 1 T(n)=\begin{cases}2T(n-1)+1,n>1\\1,n=1\end{cases} T(n)={2T(n1)+1,n>11,n=1

void hanoi(int n,a,b,c)				//a上n个盘借助c移到b
{if (n==1)						//将第1个盘从a移到bmove(1,a,b);else{hanoi(n-1,a,c,b);			//将第1至n-1个盘,从a移到cmove(n,a,b);				//将第n个盘从a移到bhanoi(n-1,c,b,a);			//将第1至n-1个盘从c移到b}
}

使用这种递归调用的关键在于建立递归调用工作栈

递归程序逐层调用需要分配存储空间,一旦某一层被启用,就要为之开辟新的空间。当一层执行完毕,释放相应空间,退到上一层。

递归优点:结构清晰,可读性强

递归缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多

解决方法:在递归算法中消除递归调用,使其转化为非递归算法。可采用一个用户定义的栈来模拟系统的递归调用工作栈。

分治策略

基本思想

将问题分解成若干个子问题,然后求解子问题。分治策略可以递归地进行,即子问题仍然可以用分值策略来处理,但最后的问题要非常基本而简单。

divide-and-conquer(P){if (|P| <= n0)									//直接解决小规模问题adhoc(P);divide P into smaller subinstances P1,P2,…,Pk	//分解问题for (i = 1; i<=k; i++)							//递归解各子问题yi=divide-and-conquer(Pi);					//将格子问题的解合并为原问题的解return merge(y1,y2,…,yk)
}

代价分析
T ( n ) = { O ( 1 ) , n = 1 k T ( n / m ) + f ( n ) , n > 1 T(n)=\begin{cases}O(1),n=1\\kT(n/m)+f(n),n>1\end{cases} T(n)={O(1),n=1kT(n/m)+f(n),n>1

T ( N ) = a T ( N / b ) + N k , a ≥ 1 , b > 1 T(N)=aT(N/b)+N^k,a≥1,b>1 T(N)=aT(N/b)+Nk,a1,b>1

T ( n ) = { O ( N t ) , t = l o g b a , a > b k O ( N k l o g N ) , a = b k O ( N k ) , a < b k T(n)=\begin{cases}O(N^t),t=log_ba,a>b^k\\O(N^klogN),a=b^k\\O(N^k),a<b^k\end{cases} T(n)= O(Nt),t=logba,a>bkO(NklogN),a=bkO(Nk),a<bk

二分搜索技术

给定已按升序排好序的n个元素a[0:n-1],现要在这n个元素中找出一特定元素x。

  1. 顺序搜索方法:最好时1次,最坏时n次

  2. 二分搜索方法

    基本思想

    将n个元素分成个数大致相同的两半,取a[n/2]与x作比较。

    • 如果x=a[n/2],则找到x,算法终止;
    • 如果x<a[n/2],则在数组a的左半部继续搜索x;
    • 如果x>a[n/2],则在数组a的右半部继续搜索x。
template<class Type>
int BinarySearch(Type a[],const Type& x, int l, int r)	//l=0,r=n-1
{//找到x时返回其在数组中的位置,否则返回-1while(r >= l){int m = (l+r)/2;if(x==a[m])return m;if (x<a[m])r=m-1;elsel=m+1;}return -1;
}

T ( n ) = { T ( n / 2 ) + O ( 1 ) , n > 1 O ( 1 ) , n = 1 T(n)=\begin{cases}T(n/2)+O(1),n>1\\O(1),n=1\end{cases} Tn={T(n/2)+O(1),n>1O(1),n=1

求解: T ( n ) = l o g n T(n)=logn T(n)=logn

复杂性: O ( l o g n ) O(logn) O(logn)

大整数的乘法

设X和Y都是n位二进制整数,计算它们的乘积 X Y XY XY

小学方法: O ( n 2 ) O(n^2) O(n2)

将n位二进制整数 X X X Y Y Y分为2段,每段的长为n/2位,由此 X = A × 2 t + B , t = n / 2 X=A×2^t+B,t=n/2 X=A×2t+B,t=n/2 Y = C × 2 t + D , t = n / 2 Y=C×2^t+D,t=n/2 Y=C×2t+D,t=n/2

由此, X Y = ( A × 2 t + B ) ( C × 2 t + D ) = A C × 2 t + ( A D + B C ) × 2 t + B D , t = n / 2 XY=(A×2^t+B)(C×2^t+D)=AC×2^t+(AD+BC)×2^t+BD,t=n/2 XY=(A×2t+B)(C×2t+D)=AC×2t+(AD+BC)×2t+BD,t=n/2

分治法: X Y = A C × 2 n + ( ( A − B ) ( D − C ) + A C + B D ) × 2 t + B D , t = n / 2 XY=AC×2^n+((A-B)(D-C)+AC+BD)×2^t+BD,t=n/2 XY=AC×2n+((AB)(DC)+AC+BD)×2t+BD,t=n/2

仅需做3次n/2位整数的乘法(AC、BD和(A-B)(D-C))、6次加减法和2次移位。

$ T(n)=\begin{cases}O(1),n=1\3T(n/2)+O(n),n>1\end{cases}$ 易求得,其解为 T ( n ) = O ( n t ) , t = l o g 3 ≈ 1.59 T(n)=O(n^t),t=log3≈1.59 T(n)=O(nt),t=log31.59

Strassen矩阵乘法

A A A B B B是两个 n × n n×n n×n矩阵,乘积 A B AB AB同样是一个 n × n n×n n×n矩阵, A A A B B B乘积矩阵 C C C中元素定 c [ i ] [ j ] c[i][j] c[i][j]定义为 (C[i][j]=\sum_{k=1}^{n}{A[i][k]B[k][j]}) 传统方法: O ( n 3 ) O(n^3) O(n3)

分治法:

KaTeX parse error: Undefined control sequence: \matrix at position 8: \left[ \̲m̲a̲t̲r̲i̲x̲{C[1][1] & C[1]…

由此可得

C [ 1 ] [ 1 ] = A [ 1 ] [ 1 ] B [ 1 ] [ 1 ] + A [ 1 ] [ 2 ] B [ 2 ] [ 1 ] C[1][1]=A[1][1]B[1][1]+A[1][2]B[2][1] C[1][1]=A[1][1]B[1][1]+A[1][2]B[2][1] C [ 1 ] [ 2 ] = A [ 1 ] [ 1 ] B [ 1 ] [ 2 ] + A [ 1 ] [ 2 ] B [ 2 ] [ 2 ] C[1][2]=A[1][1]B[1][2]+A[1][2]B[2][2] C[1][2]=A[1][1]B[1][2]+A[1][2]B[2][2] C [ 2 ] [ 1 ] = A [ 2 ] [ 1 ] B [ 1 ] [ 1 ] + A [ 2 ] [ 2 ] B [ 2 ] [ 1 ] C[2][1]=A[2][1]B[1][1]+A[2][2]B[2][1] C[2][1]=A[2][1]B[1][1]+A[2][2]B[2][1] C [ 2 ] [ 2 ] = A [ 2 ] [ 1 ] B [ 1 ] [ 2 ] + A [ 2 ] [ 2 ] B [ 2 ] [ 2 ] C[2][2]=A[2][1]B[1][2]+A[2][2]B[2][2] C[2][2]=A[2][1]B[1][2]+A[2][2]B[2][2]

根据上述算法可得,计算2个n阶方阵的乘积转化为计算8个n/2阶方阵的乘积和4个n/2阶方阵的加法。

故分治法的计算时间耗费 T ( n ) T(n) T(n)应满足$ T(n)=\begin{cases}O(1),n=2\8T(n/2)+O(n^2),n>2\end{cases}$ T ( n ) = O ( n 3 ) T(n)=O(n^3) T(n)=O(n3)

改进后变成了7次乘法(详情见书上P20),此时计算时间耗费 T ( n ) T(n) T(n)满足

T ( n ) = { O ( 1 ) , n = 2 7 T ( n / 2 ) + O ( n 2 ) , n > 2 T(n)=\begin{cases}O(1),n=2\\7T(n/2)+O(n^2),n>2\end{cases} Tn={O(1),n=27T(n/2)+O(n2),n>2解此递归方程得 T ( n ) = O ( n t ) , t = l o g 7 ≈ 2.81 T(n)=O(n^t),t=log7≈2.81 T(n)=O(nt),t=log72.81

棋盘覆盖

在一个 2 k × 2 k 2^k×2^k 2k×2k个方格组成的棋盘中,恰有一个方格与其它方格不同,称为特殊方格,且称该棋盘为一特殊棋盘。在该问题中,要用图示的4中不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

棋盘覆盖

当k>0时,将 2 k × 2 k 2^k×2^k 2k×2k个棋盘分割为4个2k-1×2k-1子棋盘。特殊方格必定位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,用一个L型骨牌覆盖这3个较小棋盘的会合处,将原问题转化为4个较小规模的棋盘覆盖问题,递归地使用这种分割,直至棋盘简化为1×1。

棋盘覆盖2

/*
tr:棋盘左上角方格的行号			tc:棋盘左上角方格的列号
dr:特殊方格所在的行号			 dc:特殊方格所在的列号
size:size=2^k,棋盘规格为2^k×2^k
*/
void ChessBoard(int tr,int tc,int dr,int dc,int size)
{if (size==1) return;int t = tile++;							//L型骨牌号s = size/2;								//分割棋盘//覆盖左上角子棋盘if (dr<tr+s && dc<tc+s)					//特殊方格在此棋盘中ChessBoard(tr,tc,dr,dc,s);else{									//此棋盘无特殊方格Board[tr+s-1][tc+s-1]=t;			//用t号L型骨牌覆盖右下角ChessBoard(tr,tc,tr+s-1,tc+s-1,s);	//覆盖其余方格}//覆盖右上角子棋盘if (dr<tr+s && dc>=tc+s)				//特殊方格在此棋盘中ChessBoard(tr,tc+s,dr,dc,s);else{									//此棋盘中无特殊方格Board[tr+s-1][tc+s]=t;				//用t号L型骨牌覆盖左下角ChessBoard(tr,tc+s,tr+s-1,tc+s,s);	//覆盖其余方格}//覆盖左下角子棋盘if (dr>=tr+s && dc<tc+s)ChessBoard(tr+s,tc,dr,dc,s);else{Board[tr+s][tc+s-1]=t;ChessBoard(tr+s.tc,tr+s,tc+s-1,s);}//覆盖右下角子棋盘if (dr>=tr+s && dc>=tc+s)ChessBoard(tr+s,tc+s,dr,dc,s);else{Board[tr+s][tc+s]=t;ChessBoard(tr+s,tc+s,tr+s,tc+s,s);}
}

由此可得该时间复杂度的递推公式为 T ( n ) = { 4 T ( n / 2 ) + O ( 1 ) , n > 1 O ( 1 ) , n = 1 T(n)=\begin{cases}4T(n/2)+O(1),n>1\\O(1),n=1\end{cases} T(n)={4T(n/2)+O(1),n>1O(1),n=1 解得 T ( n ) = O ( n 2 ) = O ( 4 k ) T(n)=O(n^2)=O(4^k) T(n)=O(n2)=O(4k)

合并排序

  • 合并是将两个或多个有序表合并成一个有序表
  • 二路合并:对两个已排好序的表进行合并
  • 合并排序算法是用分治策略实现对n个元素进行排序的算法

基本思想

将待排序元素分成大小大致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成要求的排好序的集合。

将待排序集合一分为二,直至待排序集合只剩下一个元素为止,然后不断合并两个排好序的数组段。

template<class Type>
void MergeSort(Type a[],int left,int right)
{if (left<right){						//至少有2个元素int i = (left+right)/2;				//取中点MergeSort(a, left, i);MergeSort(a, i+1, right);Merge(a, b, left, i, right);		//合并到数组bCopy(a,b,left,right);				//复制回数组a}
}

在最坏情况下所需的计算时间 T ( n ) T(n) T(n)满足 T ( n ) = { O ( 1 ) , n ≤ 1 2 T ( n / 2 ) + O ( n ) , n > 1 T(n)=\begin{cases}O(1),n≤1\\2T(n/2)+O(n),n>1\end{cases} Tn={O(1),n12T(n/2)+O(n),n>1 解此递归方程可得 T ( n ) = O ( n l o g n ) T(n)=O(nlogn) T(n)=O(nlogn)

两组归并算法

作用:将两组有序文件合并成一组有序文件。

void Merge(Type c[],Type d[], int l, int m, int r)
{//c[l]到c[m]、c[m+1]到c[r]是两有序文件合并到dint i,j,k;i = l;j = m+1;k = l;while((i<=m)&&(j<=r)){if (c[i]<=c[j])			//从两个有序文件中的第一个记录中选出小的记录d[k++]=c[i++];elsed[k++]=c[j++]}while(i<=m)					//复制第一个文件的剩余记录d[k++]=c[i++];while(j<=r)					//复制第二个文件的剩余记录d[k++]=c[j++];
}

无递归形式算法

void MergeSort(Type a[], int n)
{Type *b = new Type[n];int s = 1;while (s<n){MergePass(a,b,s,n);		//一趟归并结果放在b中s*=2;MergePass(b,a,s,n);		//一趟归并结果放在a中s+=s;}
}//调用两组归并算法,对数组进行一趟归并,将长为n的有序文件归并成长为2n的有序数组
void MergePass(Type x[], Type y[], int s, int n)
{//对x做一趟归并,结果放在y中int i = 0,j;						//n为本趟归并的有序子数组的长度while(i+2*s-1<n){Merge(x,y,i,i+s-1,i+2*s-1);		//归并长度为s的两子数组i = i+2*s;}if (i+s<n)							//剩下两个子数组,其中一个长度小于sMerge(x,y,i,i+s-1,n-1);elsefor (j=i;j<n;j++)				//将最后一个子数组复制到数组y中y[j]=x[j];
}

最好情况: T ( n ) = 2 T ( n / 2 ) + n / 2 T(n)=2T(n/2)+n/2 T(n)=2T(n/2)+n/2 T ( 1 ) = 0 T(1)=0 T(1)=0 T ( 2 ) = 1 T(2)=1 T(2)=1 T ( 4 ) = 4 T(4)=4 T(4)=4

最坏情况: T ( n ) = 2 T ( n / 2 ) + n − 1 T(n)=2T(n/2)+n-1 T(n)=2T(n/2)+n1 T ( 1 ) = 0 T(1)=0 T(1)=0 T ( 2 ) = 1 T(2)=1 T(2)=1 T ( 4 ) = 5 T(4)=5 T(4)=5

最坏时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

最好时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

平均时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

辅助空间: O ( n ) O(n) O(n)

快速排序

基本思想

  1. 设定基准值

    使序列中的每个元素与基准值比较

  2. 基于基准值划分子序列

    序列中比基准值小的放在基准值的左边,形成左部;大的放在右边,形成右部。

  3. 递归执行

    左部和右部分别递归地执行上面的过程:选基准值,小的放在左边,大的放在右边,直到排序结束。

/*
p:序列的左边界(最左元素下标)
r:序列的右边界(最右元素下标)
q:下一次左右两边子序列的分区位置
*/
template<class Type>
void QuickSort(Type a[], int p, int r)
{if (p<r){int q = Partition(a,p,r);QuickSort(a,p,q-1);				//对左半段排序QuickSort(a,q+1,r);				//对右半段排序}
}template<class Type>
int Partition(Type a[],int p,int r)
{int i = p, j = r+1;Type x=a[p];				//设定基准值//将小于x的元素交换到左边区域,将大于x的元素交换到右边区域while(true){while(a[++i]<x);while(a[--j]>x);if (i>=j)break;Swap(a[i],a[j]);}a[p]=a[j];a[j]=x;return j;
}

快速排序

最坏时间复杂度: O ( n 2 ) O(n^2) O(n2)

最好/平均时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

最坏辅助空间: O ( n ) O(n) O(n)

最好/平均辅助空间: O ( l o g 2 n ) O(log_2n) O(log2n)

最坏情况

序列的n个元素已经排好序,每次划分都恰好把序列分为1,n-1两部分,那么总共需要n-1次划分,每次比较的次数分别为n-1,n-2,…,1,所以整个比较次数约为 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2~ n 2 / 2 n^2/2 n2/2 ( T ( n ) = { O ( 1 ) , n ≤ 1 T ( n − 1 ) + O ( n ) , n > 1 求解递归方程可得 T(n)=\begin{cases}O(1),n≤1\\T(n-1)+O(n),n>1\end{cases}\ 求解递归方程可得 T(n)={O(1),n1T(n1)+O(n),n>1 求解递归方程可得T(n)=O(n^2) ,相应的, n − 1 次划分形成的子序列共需要 ,相应的,n-1次划分形成的子序列共需要 ,相应的,n1次划分形成的子序列共需要O(n)辅助空间

最好情况

每次划分所取的基准都恰好为序列中值,可将序列从中间均衡划分为等长子序列,因此需要log2n次划分即可完成排序。 T ( n ) = { O ( 1 ) , n ≤ 1 2 T ( n / 2 ) + O ( n ) , n > 1 T(n)=\begin{cases}O(1),n≤1\\2T(n/2)+O(n),n>1\end{cases} T(n)={O(1),n12T(n/2)+O(n),n>1 求解递归方程可得 T ( n ) = O ( n l o g 2 n ) T(n)=O(nlog_2n) T(n)=O(nlog2n),形成的子序列共需要 O ( l o g 2 n ) O(log_2n) O(log2n)辅助空间。

平均情况

设分划的数x=a[p]在最后排序的第k位,第1轮比较n-1次,前有k-1个元素,后有n-k个元素。

快速排序是不稳定的排序算法

通过修改算法partition,可以设计采用随机选择策略的快速排序算法,在快速排序算法的每一次partition中,可在a[p:r]中随机选出一个元素作为划分基准,从概率角度使得划分的期望达到对称效果。

template<class Type>
int RandomizedPartition(Type a[],int p, int r)
{int i = Random(p,r);Swap(a[i],a[p]);return Partition(a,p,r);
}

线性时间选择

给定线性序集中n个元素和一个整数k(1≤k≤n),要求找出这n个元素中第k小的元素,即如果将这个元素依其线性序排列时,排在第k个位置的元素即为要找的元素。

复杂性是O(n2)随机化算法

template<class Type>
Type RandomizedSelect(Type a[],int p, int r, int k)
{if (p==r)return a[p];int i = RandomizedPartition(a,p,r);j = i-p+1;if(k<=j)return RandomizedSelect(a,p,i,k);elsereturn RandomizedSelect(a,i+1,r,k-j);
}

算法时间复杂性: O ( n ) O(n) O(n)

按以下步骤找到满足要求的划分标准:

  1. 将n个输入元素划分成[n/5]个组,每组5个元素,除可能有一个组不是5个元素外。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共[n/5]个。
  2. 递归调用Select找出这[n/5]个元素的中位数。如果[n/5]是偶数,就找它的两个中位数中较大的一个。
template<class Type>
Type Select(Type a[], int p, int r, int k)
{if (r-p<cn)用简单的排序算法对数组a[p:r]排序;return a[p+k-1];for (int i = 0; i <= (r-p-4)/5;i++){//将a[p+5*i]至a[p+5*i+4]的第3小元素与a[p+i]交换位置Type x = Select(a,p,p+(r-p-4)/5,(r-p-4)/10);int i = Partition(a,p,r,x);if (k<=j)return Select(a,p,i,k);elsereturn Select(a,i+1,r,k-j);}
}

关于 T ( n ) T(n) T(n)的递归式
T ( n ) ≤ { T ( 1 5 n ) + T ( 3 4 n ) + c n , n > > 5 c n , n ≤ 5 T(n)≤\begin{cases}T(\frac{1}{5}n)+T(\frac{3}{4}n)+cn,n>>5\\cn,n≤5\end{cases} T(n){T(51n)+T(43n)+cn,n>>5cn,n5

最接近点对问题

给定平面上 n n n个点,找其中的一对点,使得在 n n n个点组成的所有点对中,该点对间的距离最小。

将所给的平面上 n n n个点的集合 S S S分成两个子集 S 1 S_1 S1 S 2 S_2 S2,每个子集中约有n/2个点,然后在每个子集中递归地求其最接近的点对。

一维点集S

bool Cpair(S,d){n = |S|;if (n<2){d=∞;return false;}m = S中各点坐标的中位数;构造S1和S2;					//S1={x∈S|x<=m},S2={x∈S|x>m}Cpair(S1,d1);Cpair(S2,d2);p=max(S1);q=min(S2);d=min(d1,d2,q-p);return true;
}

算法耗费的计算时间 T ( n ) T(n) T(n)满足递归方程 T ( n ) = { 2 T ( n / 2 ) + O ( n ) , n > 4 O ( 1 ) , n = 4 T(n)=\begin{cases}2T(n/2)+O(n),n>4\\O(1),n=4\end{cases} T(n)={2T(n/2)+O(n),n>4O(1),n=4 求得 T ( n ) = O ( n l o g n ) T(n)=O(nlogn) T(n)=O(nlogn)

二维点集

S={p1,p2,…,pn}

作一条垂直线 l : x = m l:x=m l:x=m将平面分为两部分,设(p,q)为S的最接近点对情况:

  • p,q∈S1
  • p,q∈S2
  • p∈S1,q∈S2
    • p∈S1,q∈S2的情形
    • P1、P2有稀疏 性质:对P1中任意一点p,在P2中最多只有6个S中的点使与p的距离不超过d(鸽舍原理
bool Cpair2(S,d)
{n=|S|;if (n<2){d=∞;return false;}m=S中各点x间坐标的中位数;构造S1和S2;//S1={x∈S|x<=m},S2={x∈S|x>m}Cpair2(S1,d1);Cpair2(S2,d2);dm = min(d1,d2);设P1是S1中距垂直分割线l的距离在dm之内的所有点组成的集合;P2是S2中距分割线l的距离在dm之内所有点组成的集合;将P1和P2中点依其y坐标值排序;并设X和Y是相应的已排好序的点列;通过扫描X以及对于X中每个点检查Y中与其距离在dm之内的所有点(最多6个)可以完成合并;当X中的扫描指针逐次向上移动时,Y中的扫描指针可在宽为2dm的一个区间内移动;设dl是按这种扫描方式找到的点对间的最小距离;d=min(dm,dl);return true;
}

计算时间 T ( n ) T(n) T(n)满足递归方程 T ( n ) = { 2 T ( n / 2 ) + O ( n ) , n > 4 O ( 1 ) , n ≤ 4 T(n)=\begin{cases}2T(n/2)+O(n),n>4\\O(1),n≤4\end{cases} T(n)={2T(n/2)+O(n),n>4O(1),n4 可解得 T ( n ) = O ( n l o g n ) T(n)=O(nlogn) T(n)=O(nlogn)

循环赛日程表

设有 n = 2 k n=2^k n=2k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:

  1. 每个选手必须与其他n-1个选手各赛一次
  2. 每个选手一天只能赛一次
  3. 循环赛一共进行n-1天

分治策略:

将所有选手对分为两组,n个选手的比赛日程表可通过为n/2个选手设计的比赛日程表来决定。

递归地用这种一分为二的策略对选手进行分割,直到只剩下2个选手时,比赛日程表的制定就变得很简单,只需让这2个选手进行比赛即可。

void Table(int k, int **a)
{int n = 1;for (int i=1;i<=k;i++)n*=2;for (int i=1;i<=n;i++)a[1][i]=i;int m = 1;for (int s=1;s<=k;s++){for (int i=m+1;i<=2*m;i++){for (int j=m+1;j<=2*m;j++){a[i][j+(t-1)*m*2]=a[i-m][j+(t-1)*m*2-m];a[i][j+(t-1)*m*2-m]=a[i-m][j+(t-1)*m*2];}}m*=2;}
}

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

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

相关文章

基于yolov8的SAR影像目标检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】

更多目标检测、图像分类识别、目标追踪等项目可看我主页其他文章 功能演示&#xff1a; 基于yolov8的SAR影像目标检测系统&#xff0c;支持图像、视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov8的SAR影像目标…

uni-app 设置缓存过期时间【跨端开发系列】

&#x1f517; uniapp 跨端开发系列文章&#xff1a;&#x1f380;&#x1f380;&#x1f380; uni-app 组成和跨端原理 【跨端开发系列】 uni-app 各端差异注意事项 【跨端开发系列】uni-app 离线本地存储方案 【跨端开发系列】uni-app UI库、框架、组件选型指南 【跨端开…

复现论文:PromptTA: Prompt-driven Text Adapter for Source-freeDomain Generalization

github&#xff1a;zhanghr2001/PromptTA: Source-free Domain Generalization 论文&#xff1a;[2409.14163] PromptTA: Prompt-driven Text Adapter for Source-free Domain Generalization 自己标注&#xff1a;PromptTA: Prompt-driven Text Adapter for Source-free Domai…

Dos脚本中的start命令

0 Preface/Foreword 1 Start介绍 start是用来启动一个应用或者一个bat脚本文件。 1.1 %*传递参数 %*&#xff1a;表示运行命令时传入的所有参数。 1.2 %processor_architecture% 系统处理器架构&#xff0c;内置变量。 echo %processor_architecture% 1.3 示例 echo He…

HTML笔记()蜘蛛纸牌之卡牌拖拽

效果 代码 <!DOCTYPE html> <html><head><style>body{display: flex;justify-content: center;align-items: center;height: 100vh;background-color: #2b2b2b;position: relative;}.card{/*设置卡牌的外观*/width: 150px;height: 200px;background-…

基于SSM的线上考试系统的设计与实现(计算机毕业设计)+万字说明文档

系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境&#xff1a; Tomcat 7.x,8.x,9.x版本均可 操作系统…

vue 封装全局方法及使用

1.找到项目中的utils定义js&#xff0c;这个js存放全局可使用的方法 2.去项目中main.js中引入注册 import publicFun from ./utils/test Vue.prototype.$publicFun publicFun;3.项目使用 ddd(){this.$publicFun.testwen()},

微信小程序中使用miniprogram-sm-crypto实现SM4加密攻略

在微信小程序开发过程中&#xff0c;数据安全至关重要。本文将为大家介绍如何在微信小程序中使用miniprogram-sm-crypto插件进行SM4加密&#xff0c;确保数据传输的安全性。 一、SM4加密简介 SM4是一种对称加密算法&#xff0c;由国家密码管理局发布&#xff0c;适用于商密领…

【论文阅读】相似误差订正方法在风电短期风速预报中的应用研究

文章目录 概述&#xff1a;摘要1. 引言2. 相似误差订正算法&#xff08;核心&#xff09;3. 订正实验3.1 相似因子选取3.2 相似样本数试验3.3 时间窗时长实验 4. 订正结果分析4.1 评估指标对比4.2 风速曲线对比4.3 分风速段订正效果评估4.4 风速频率统计 5. 结论与讨论 概述&am…

高中数学:计数原理-二项式定理

文章目录 一、二项式定理与通项公式二、二项式系数的性质 一、二项式定理与通项公式 我们先来看完全平方公式 二、二项式系数的性质

javaweb-Mybaits

1.Mybaits入门 &#xff08;1&#xff09;介绍 &#xff08;2&#xff09; 2.Mybaits VS JDBC 3.数据库连接池 &#xff08;1&#xff09;SpringBoot默认连接池为hikari&#xff0c;切换为Druid有两种方式 方式一&#xff1a;加依赖 方式二&#xff1a;直接修改配置文件 …

节点流和处理流

1. 基本介绍 节点流可以从一个特定的数据源读写数据&#xff0c;如FileReader、FileWriter 处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上&#xff0c;为程序提供更为强大的读写功能&#xff0c;也更加灵活&#xff0c;如BufferedReader、BufferedWriter 2. …

21个Python脚本自动执行日常任务(2)

引言 作为编程领域摸爬滚打超过十年的老手&#xff0c;我深刻体会到&#xff0c;自动化那些重复性工作能大大节省我们的时间和精力。 Python以其简洁的语法和功能强大的库支持&#xff0c;成为了编写自动化脚本的首选语言。无论你是专业的程序员&#xff0c;还是希望简化日常工…

数据结构--树和二叉树

树和二叉树的定义 树的定义 树是一种非线性的数据结构&#xff0c;它模拟了具有层次关系的数据的集合。在树结构中&#xff0c;存在以下基本概念&#xff1a; 节点&#xff08;Node&#xff09;&#xff1a;树的每个元素被称为节点。根节点&#xff08;Root Node&#xff09…

RabbitMQ七种工作模式之 RPC通信模式, 发布确认模式

文章目录 六. RPC(RPC通信模式)客户端服务端 七. Publisher Confirms(发布确认模式)1. Publishing Messages Individually(单独确认)2. Publishing Messages in Batches(批量确认)3. Handling Publisher Confirms Asynchronously(异步确认) 六. RPC(RPC通信模式) 客⼾端发送消息…

推送(push)项目到gitlab

文章目录 1、git init1.1、在当前目录中显示隐藏文件&#xff1a;1.2、查看已有的远程仓库1.3、确保你的本地机器已经生成了 SSH 密钥&#xff1a;1.4、将生成的公钥文件&#xff08;通常位于 ~/.ssh/id_rsa.pub&#xff09;复制到 GitLab 的 SSH 设置中&#xff1a;1.5、测试 …

前端-使用vue-cli脚手架创建项目

1.下载node&#xff1a;2.下载完检查是否安装成功 在cmd中输入&#xff1a;node --version或node -v 再在cmd中输入: npm -v npm默认的仓库地址是在国外&#xff0c;速度慢&#xff0c;所以设置淘宝镜像&#xff0c;速度就提升了&#xff0c;不设淘宝镜像也可以。 3.设置…

win11 安装低版本vmware导致频繁蓝屏原因及解决方法

环境 win11 vmware 16 安装完后&#xff0c;配置完虚拟机win10 ,开始频繁出现蓝屏&#xff0c;蓝屏界面如下 解决方案&#xff1a; 低版本vmware与win11不兼容&#xff0c;安装最新的vmware 17.5 使用ddu卸载集成显卡驱动&#xff0c;重新安装最新的显卡驱动&#xff0c…

算法日记 45 day 图论(并查集基础)

并查集解决什么问题 并查集常用来解决连通性问题。 大白话就是当我们需要判断两个元素是否在同一个集合里的时候&#xff0c;我们就要想到用并查集。 原理 既然是查找是否在同一个集合中&#xff0c;那么这个集合是怎么构建的呢&#xff1f;用一维数组来表示一个有向图&…

硬件选型规则

光源选型: 先用型号中带H的&#xff0c;没有的选标准的. 光源和光源控制器的搭配需要确保接口一致。 根据型号表中的最佳工作距离和相机的尺寸。 光源控制器选型&#xff1a; 首先选择海康风格系列光源控制器考虑与光源的接口匹配。功率应该满足接近光源功率。检查是否退市…