算法与分析课程复习之分治法

算法与分析课程复习之分治法
一、基础知识
1.递归简单定义:
将一个问题分割成一个或多个的子问题,这些子问题与原问题具有相同的结构,然后组合这些子问题的解决方案,以获得原来的问题的解决方案的过程。
2.递归本质定义:
从本质上说,给出一个带有参数n的问题,用归纳法设计一个算法是基于这样一个事实,如果我们知道如何求解带有参数小于n的同样问题(它被称为归纳假设),那么我们的任务就化为如何把解法扩展到带有参数n的实例。
3.分治法的优点:
算法的正确性证明已自然地嵌入了算法的描述中了
4.归纳法的基本思想:
将规模为n的问题递减为规模为n-1或n/2的子问题,反复递减后对子问题分别求解,再建立子问题的解与原问题的解的关系。
5.归纳法的两个变形:

  • 减常数(如1) :每次迭代规模减1,即n→n-1
  • 减因子(如1/2):每次迭代规模减半,即n→ n/2

二、经典问题
1.阶乘函数
当n=1时,n!= 1
当n>1时,n!= n*(n-1)!

int factorial(int n)
{
   if(n==1)return 1;
   else return n * factorial(n-1);
}

2.Fibonacci数列
当n=0,1时,f = 1
当n>1时,f = f(n-1)+f(n-1)

int fibonacci(int n)
{
  if (n <= 1) return 1;
  return fibonacci(n-1)+fibonacci(n-2);
}

3.选择排序
设 A[1…n]为包含n个元素的数组
首先,我们找到数组中的最小元素,将它存在A[1].
接着,我们从剩下的n-1个元素中找到最小的元素,将它存在 A[2].
重复以上过程,知道整个数组中第二大的元素存在A[n-1],则算法停止.
时间复杂度为:O(n^2)

void SelectSort(int a[],int i,int n) {
	//递归的方法简单选择排序
	//i n排序数组的起始和终止端
	int j,k;
	if(i==n-1)//递归结束条件
		return ;
	else {
		k=i;//记录当前a[i]~a[n-1]最小值的下标
		for(j=i+1; j<n; j++)
			if(a[j]<a[k])//在a[i]~a[n-1]中找到最小值
				k=j;
		if(k!=i)
		 {
			//如果最小值不是a[i]交换
			swap(a[i),a[k]);
		}
		SelectSort(a,i+1,n);
	}
}

4.插入排序
假设我们知道如何对前n-1个元素,也就是数组A[1…n-1]排序。
那么在对A[1…n-1]排序后,我们只要把A[n]插入它的适当位置,假设是位置j,1≤j ≤n。
这个插入可能导致A[j+1], A[j+2] ,… ,A[n-1]移到位置j+2,j+3,…,n.
时间复杂度为:O(n^2)

void rec_insertion(int arr[], int n)
{
    if (n <= 1) return;
    //对剩余的n-1个元素排序
    rec_insertion(arr, n - 1);//此语句位置不可更改
 
    int last = arr[n - 1];
    int j = n - 2;
 
    while (j >= 0 && last < arr[j]) {
        arr[j + 1] = arr[j];
        j--;
    }
    arr[j + 1] = last;
}
//举例arr[] = { 10, 14, 3, 8, 5, 12 };
//排列顺序为
//10 14
//3 10 14
//3 8 10 14
//3 5 8 10 14
//3 5 8 10 12 14

注意递归的层次与顺序

5.合并排序

int temp[];//辅助空间
void merge_sort(int q[],int l,int r)
{
    if(l>=r)return;
    int mid=(l+r)>>1;
    merge_sort(q, l, mid);
    merge_sort(q,mid+1,r);
    int i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r)
    {
        //将小的数放在temp左边
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else temp[k++]=q[j++];
    }
    while(i<=mid)temp[k++]=q[i++];
    while(j<=r)temp[k++]=q[j++];
    for(i=l,j=0;i<=r;i++,j++)//把辅助空间的数据放到原数据
        q[i]=temp[j];
}

排序算法的递归关系式都大同小异,即Sort(low,mid),Sort(mid+1,high)重点掌握不同排序算法的思想即可

6.多项式求值(Homer规则)
在这里插入图片描述

double Horner(double a[], int n, double x)  
{  
    double res = 0.0;  
   
    for(int i=n; i>=0; --i)  
        res = x*res + a[i];  
   
    return res;  
}  

7.输出n个整数的全排列

void permutations(int array[], int k, int m){
	if(k == m){
		for(int i=0; i<=m; i++){
			cout <<array[i]<<" ";
		}
		cout <<endl;
	}
	else{
		for(int i=k; i<=m; i++){
			swap(array[k], array[i]);
			permutations(array, k+1, m);
			swap(array[k], array[i]);
		}
	}
} 

8.输出n个整数的所有子集

//tag数组标记数字是否使用过 
void subset(int array[], int tag[], int k, int n){
	if(k == n+1){
		cout <<"{"<<" ";
		for(int i=0; i<=n; i++){
			if(tag[i] == 1){
				cout <<array[i]<<",";
			}
		}
		cout <<"}"<<endl;
	}
	else{
		tag[k] = 0;
		subset(array, tag, k+1, n);
		tag[k] = 1;//类似回溯
		subset(array, tag, k+1, n);
	}
}

9.假币问题

int falseCoin(int weight[], int lhs, int rhs)
{
    if (lhs == rhs)
        return lhs + 1;
    //如果只剩下两个银币,则较轻的那个便是假币
    else if (lhs == (rhs - 1))
    {
        return weight[lhs] < weight[rhs] ? lhs + 1 : rhs + 1;
    }

    int lsum = 0, rsum = 0;

    //如果偶数个银币,则比较两等份
    if ((rhs - lhs + 1) % 2 == 0)
    {
        for (int i = lhs; i < (lhs + (rhs - lhs + 1) / 2); i++)
        {
            lsum += weight[i];
        }

        for (int j = lhs + (rhs - lhs + 1) / 2; j <= rhs; j++)
        {
            rsum += weight[j];
        }

        //左右两份等重,则无假币
        if (lsum == rsum)
            return -1;
        else
            return (lsum < rsum) ? falseCoin(weight, lhs, lhs + (rhs - lhs) / 2) : falseCoin(weight, lhs + (rhs - lhs) / 2 + 1, rhs);
    }

    //如果奇数个银币,则比较除中间银币外的两等份
    else if ((rhs - lhs + 1) % 2 != 0)
    {
        for (int i = lhs; i < (lhs + (rhs - lhs) / 2); i++)
        {
            lsum += weight[i];
        }

        for (int j = (lhs + (rhs - lhs) / 2 + 1); j <= rhs; j++)
        {
            rsum += weight[j];
        }

        //左右两份等重,则无假币
        if (lsum == rsum && weight[lhs] == weight[lhs + (rhs - lhs) / 2])
            return -1;

        //如果两份等重,中间银币较轻,则中间银币为假币
        else if (lsum == rsum && weight[lhs] > weight[lhs + (rhs - lhs) / 2])
            return lhs + (rhs - lhs) / 2 + 1;

        //否则,返回较轻那份中的假币
        else
            return (lsum < rsum) ? falseCoin(weight, lhs, lhs + (rhs - lhs) / 2 - 1) : falseCoin(weight, lhs + (rhs - lhs) / 2 + 1, rhs);
    }
}

10.寻找最大值(最小值)

将数组分割成两半, A[1…n/2] 和A[(n/2) + 1…n];
在每一半中找到最大值;

int findMax(int a[],int low,int high)
{
    if(high==low){
        return a[high];
    }
    else
    {
        int mid = (low+high)/2;
        int left = findMax(a, low, mid);//找到左边的最大值
        int right = findMax(a, mid+1, high);//找到右边的最大值
        return max(left,right);
    }
}

11.二分搜索

将一个给定的元素x与一个已排序数组A[low…high].的中间元素做比较
如果x < A[mid],这里mid =(low + high)/2,则不考虑 A[mid…high] ,而对A[low…mid-1]重复实施相同的方法。
类似地,如果 x > A[mid],则放弃A[low…mid],而对A[mid+1…high]重复实施相同的方法.

int binarySearch(int a[],int low,int high,int x)
{
    if(low>=high)return 0;
    else
    {
        int mid = (low+high)/2;
        if(x==a[mid])return mid;
        else if(x<a[mid])return binarySearch(a, low, mid-1, x);
        else return binarySearch(a, mid+1, high, x);
    }
}

12.拓扑排序
对给定的无环有向图,要求按照某种顺序列出它的顶点序列,使图的每一条边的起点总在结束顶点之前。

bool topsort()
{
    int hh = 0, tt = -1;

    // d[i] 存储点i的入度
    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[ ++ tt] = i;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
                q[ ++ tt] = j;
        }
    }
    // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
    return tt == n - 1;
}

13.求整数幂问题(减常因子法)
对求实数x的n次幂设计一个有效的算法

int power(int x,int n)
{
    int y;
    if(n==0)return 1;
    else
    {
        y = power(x, n/2);
        y = y*y;
        if(n%2!=0)
            y=y*x;
    }
    return y;
}

14.STRASSEN算法(解决矩阵乘法问题)
增加加减法的次数来减少乘法次数

15.最近点对问题
设S是平面上n个点的集合,在这一节中,我们考虑在S中找到一个点对p和q的问题,使其相互距离最短。

16.快速排序

void Quick_Sort(int arr[], int begin, int end){
    if(begin > end)
        return;
    int tmp = arr[begin];
    int i = begin;
    int j = end;
    while(i != j){
        while(arr[j] >= tmp && j > i)
            j--;
        while(arr[i] <= tmp && j > i)
            i++;
        if(j > i){
            swap(arr[i],arr[j]);
        }
    }
    swap(arr[begin],arr[i]);
    Quick_Sort(arr, begin, i-1);
    Quick_Sort(arr, i+1, end);
}

17.众数问题
问题描述:
  给定含有n 个元素的多重集合S,每个元素在S 中出现的次数称为该元素的重数。多重集S 中重数最大的元素称为众数。
例如,S={1,2,2,2,3,5}。
多重集S 的众数是2,其重数为3。
编程任务:
  对于给定的由n 个自然数组成的多重集S,编程计算S 的众数及其重数。

#include <iostream>
#include <algorithm>
using namespace std;
const int N =1e5+10;
int MaxNum;//记录众数
int Num;//记录重数
int n;
int a[N];
void solve(int low,int high)
{
    if(low>=high)return;
    int mid = (low+high)/2;
    int i = mid,j = mid;
    //首先确定中间数的个数
    while(a[i]==a[mid]&&i>=1)
        i--;
    while(a[j]==a[mid]&&j<=n)
        j++;
    if(j-i-1>=Num)//更新
    {
        Num = j-i-1;
        MaxNum = a[mid];
    }
    if(i-low+1>=Num)//左边的数大于Num,则众数可能出现在左边
    {
        solve(low, i);
    }
    if(high-j+1>=Num)//右边的数大于Num,则众数可能出现在右边
    {
        solve(j, high);
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+n+1);//排序
    solve(1, n);
    cout<<MaxNum<<endl<<Num<<endl;
    return 0;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值