算法设计与分析基础1 递归与分治

说明

留个纪念,记录部分大连理工大学算法设计与分析课程源代码。
本人代码水平有限,可能会有错误,请评论区交流讨论。
给学弟学妹一些方便,或者是有基础算法需求的朋友。编程语言c++。

本系列总目录

注意:参考书:王晓东《算法设计与分析第四版》

  1. 递归与分治算法
  2. 动态规划
  3. 贪心算法
  4. 回溯算法

本部分目录

1.递归与分治理解

递归就是直接或间接的调用自身。

分治就是将一个规模为n的问题分解成k个规模较小的子问题,子问题相互独立且与原问题相同。

2.递归例子:汉诺塔

// An highlighted block
#include<iostream>
#include<algorithm>
using namespace std;

void hanuo(int n,int a,int b,int c) //将1-n层从a移动到b借助c
{
	if(n>0)
	{
		hanuo(n-1,a,c,b);//先把上面的全部移动到辅助c上面 
		cout<<a<<"移动到"<<b<<endl;//移动最大的一块到达最终目的b 
		hanuo(n-1,c,b,a);//把不在目的b上的恢复到目的b上 
	}
 } 

int main()
{
	int n;//输入层数记为:n 
	cin>>n;
	
	hanuo(n,1,2,3);//123在这里是柱子的名字
	 
	return 0;
 } 

3.递归例子:全排列

// An highlighted block
#include<iostream>
#include<algorithm>
using namespace std;

//const int N=10;

int a[11]; 
 
void p(int k,int n)//p(k,n)代表从k到n进行全排列的个数 
{
	if(k==n)//递归截止条件 
	{
		for(int i=1;i<=n;i++)
		{
			cout<<a[i]<<' ';
		}
		cout<<endl;
	}
	else
	{
		for(int i=k;i<=n;i++)
		{
			swap(a[i],a[k]);
			p(k+1,n);
			swap(a[i],a[k]);
		}
	}
 } 

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	 } 
	
	p(1,n);
	
	return 0;
 } 

4.递归例子:整数划分

//整数划分问题就是用>=1的数表示一个整数有多少种不同的方法 
//写出递归表达式后非常容易程序实现
 
#include<iostream>
#include<algorithm>
using namespace std;

//const int N=10e9+7;
int p(int n,int m)
{
	if(n<1||m<1) return 0;
	else if(n==1||m==1) return 1;
	else if(n<=m) return p(n,n-1)+1;
	else return p(n,m-1)+p(n-m,m);
}

int main()
{
	int n;//输入整数记为:n 
	cin>>n;
	
	cout<<p(n,n);
	return 0;
 } 

5.分治例子:二分搜索

// An highlighted block
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

int binS(int a[],int x,int n)
{
	int left=0,right=n-1;
	
	while(left<=right)
	{
		int mid=left+(right-left)/2;
		if(a[mid]==x) return mid;
		if(x<a[mid]) right=mid-1;
		else left=mid+1; 
	}
	return -1;
}


int main()
{
	int a[10];
	cout<<"请有序输入10个数"<<endl;
	for(int i=0;i<10;i++)
	{
		cin>>a[i];
	}
	
	int t=binS(a,8,10);
	if(t==-1) cout<<"完犊子,弟弟"<<endl;
	else cout<<"找到了,老铁,在数组的"<<t<<"号位置"<<endl; 
	
	return 0;
 } 

6.分治例子:归并排序

//归并排序:典型的分治思想 
//假定子块是排好序的,额外写一个如何把有序子块合并为有序大块的函数 

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

const int N=1010;
int a[N],temp[N];
int n;

void mergeSort(int left,int right)
{
	if(left>=right) return ;
	
	int mid=left+(right-left)/2;
	mergeSort(left,mid);
	mergeSort(mid+1,right);
	
	//现在通过递归假设我们已经得到了两个有序的子块
	//开始合并
	
	int k=0,i=left,j=mid+1;
	while(i<=mid&&j<=right)
	{
		if(a[i]<=a[j]) temp[k++]=a[i++];
		else temp[k++]=a[j++];
	}
	if(i<=mid)
	{
		for(int t=i;t<=mid;t++)
		{
			temp[k++]=a[i++];
		}
	}
	if(j<=right)
	{
		for(int t=j;t<=right;t++)
		{
			temp[k++]=a[j++];
		}
	}
	k=0;
	for(int i=left;i<=right;i++) 
	{
		a[i]=temp[k++];
	}
	return ;
}
 
int main()
{
	cin>>n;//一共n个数
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	
	mergeSort(0,n-1);//将下标从0到n-1进行归并排序 
	
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<' ';
	 } 
	 cout<<endl;
	
	return 0;
 } 

7.分治例子:快速排序

/*
   理解: 先在所在小组中找一个基准,比如第一个,或者中间那个
	然后从两侧遍历该小组,以便找到基准的正确位置,完成本次剖分
	这是一种模板写法,结构比较清晰,稍微比较难以理解其中边界问题的处理 
*/ 
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

const int N=1010;
int a[N];
int n;

void qSort(int left,int right) 
{
	//最后只剩一个元素就要直接返回,快排不是递归,递归要求出子问题返回到原问题,而快排在求解问题的过程中就在解决问题,到最后只剩一个元素时证明问题已经解决
	if(left==right) return ;
	
	//确定基准元素 
	int x=a[left+(right-left)/2];//比起直接取第一个元素为基准元素,这样取更随机
	int i=left-1,j=right+1;//从两侧开始比较和交换,这是一种模板写法,为后续 do while服务
	
	while(i<j)
	{
		do i++; while(x>a[i]);
		do j--; while(x<a[j]);
		if(i<j) swap(a[i],a[j]);
	}
	
	qSort(left,j);
	qSort(j+1,right);
	
 } 
 
int main()
{
	cin>>n;//一共n个数
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	
	qSort(0,n-1);//将下标从0到n-1进行排序 
	
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<' ';
	 } 
	 cout<<endl;
	
	return 0;
 } 

8.分治例子:线性时间选择

// An highlighted block
//本题是基于快排的变种,根据快排的特性,每次排序确定基准元素在最终排序结果中的位置
//所以要找第k个元素就变得简单了起来,可以缩短快排的查找范围
#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e5+10;
int a[N];

int qSort(int left,int right,int k)//这里的意思是去找在left与right之间的第k小个元素,我们的函数可以保证k一定是在left与right之间的
{
    if(left==right) return a[left];//此时k一定就是等于left和right的
    
    int x=a[left+(right-left)/2];
    int i=left-1,j=right+1;
    
    while(i<j)
    {
        do i++; while(a[i]<x);
        do j--; while(a[j]>x);
        if(i<j) swap(a[i],a[j]);
    }
    
    int t=j+1-left;
    if(k<=t) return qSort(left,j,k);//最后如何结束,
    else return qSort(j+1,right,k-t);
}

int main()
{
    int n;
    cin>>n;
    int k;
    cin>>k;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    
    int res=qSort(0,n-1,k);
    cout<<res;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值