前奏:
分治法可能是最著名的通用算法设计技术了:很多非常有效的算法实际上就是这个通用算法的特殊实现。
分治法主要按以下方案工作:
1.将一个问题划分为同一类型的若干子问题,子问题的规模最好相同。
2.对这些子问题求解(一般使用递归)
3.有必要的话,合并这些子问题的解,以得到原始答案。
分治法的流程见下图,该图描述的是将】一个问题分解成两个较小子问题的例子,也是最常见的情况。
对于分治法效率上的分析,有下列递推式(通用分治递推式)
T(n) = aT(n / b) + f(n)
n表示这个实例规模,a表示其中有a个实例需要求解,b表示n个实例可以划分为b个规模为(n/b)的实例。f(n)是一个函数,表示将问题分解为小问题和将结果合并起来所消耗的时间。
在分析分治算法的效率时,我们可通过下列定理大大缩减分析的时间
分治法主要算法有合并排序,快排,二叉树的遍历及其相关特性,大整数乘法和Strassen矩阵乘法等等。
1.合并排序:对于一个需要排序的数组A【0 ~ n - 1】,合并排序把它一分为2:A【0 ~ [n/2] - 1】和A【[n/2] ~ n - 1】,并对每个子数组递归排序,然后把这两个排好序的数组合并成一个有序的数组。
上代码:
#include<iostream>
using namespace std;
const int maxn = 1001;
int i,j;
void Merge(int r[],int r1[],int s,int m,int t)
{
i = s;j = m + 1;int k = s;
while(i <= m&&j <= t)
{
if(r[i] <= r[j])r1[k ++] = r[i ++];
else r1[k ++] = r[j ++];
}
if(i <= m)while(i <= m)
r1[k ++] = r[i ++];
else while(j <= t)
r1[k ++] = r[j ++];
}
void MergeSort(int r[],int r1[],int s,int t)
{
if(s == t)r1[s] = r[s];
else {
int m = (s + t) / 2;
MergeSort(r,r1,s,m);
MergeSort(r,r1,m + 1,t);
Merge(r,r1,s,m,t);
}
}
main()
{
int r[maxn],r1[maxn];
int n;
cin>>n;
for(i = 0;i < n;i ++)
{
cin>>r[i];
}
MergeSort(r,r1,0,n);
for(i = 0;i < n;i ++)
cout<<r1[i]<<" ";
}
2.快排:
#include<bits/stdc++.h>
using namespace std;
int scan(int i,int j);
void quickSort(int i,int j);
int num[10] = {1,32,4,12,4,5,63,21,2,6};
main()
{
//num[20] = {1,32,4,12,4,5,63,21,2,6};
//int len = strlen(num);
quickSort(0,10);
for(int i = 1;i <= 10;i ++)
cout<<num[i]<<" ";
}
int scan(int i,int j)
{
int tem;
while(i < j)
{
while(i < j&&num[i] <= num[j])j --;
if(i < j)
{
tem = num[j];
num[j] = num[i];
num[i] = tem;
i ++;
}
while(i < j&&num[i] <= num[j])i ++;
if(i < j)
{
tem = num[j];
num[j] = num[i];
num[i] = tem;
j --;
}
}
return i;
}
void quickSort(int i,int j)
{
int mid;
if(i < j)
{
mid = scan(i,j);
quickSort(i,mid - 1);
quickSort(mid + 1,j);
}
}
3.二叉树的遍历:
#include<stdio.h>
#include<queue>
#include<bits/stdc++.h>//万能头文件,不建议用,太慢
#include<iostream>
using namespace std;
struct tree{
char data;
struct tree *lchild,*rchild;
};
int depth(tree*);//求二叉树的深度
void create(tree* &T);//创建树
int numNode(tree *T);//寻找有多少个节点
queue<tree>q;//层序遍历需要用的队列
void create(tree* &T)//创建二叉树
{
char c;
scanf("%c",&c);
if(c=='/') return;
T=new tree;
T->data=c;
T->lchild=NULL;
T->rchild=NULL;
create(T->lchild);
create(T->rchild);
}
void priorOrder(tree* T)//先序遍历二叉树
{
if(T)
{
cout<<T->data<<endl;
priorOrder(T->lchild);
priorOrder(T->rchild);
}
}
void behindOrder(tree* T)//后序遍历二叉树
{
if(T)
{
behindOrder(T->lchild);
behindOrder(T->rchild);
cout<<T->data<<endl;
}
}
void midOrder(tree *T)//中序遍历二叉树
{
if(T)
{
midOrder(T->lchild);
cout<<T->data<<endl;
midOrder(T->rchild);
}
}
void cengOrder(tree *T) //层序遍历二叉树
{
if(T)
{
q.push(*T);
}
while(!q.empty())
{
cout<<q.front().data;
if (q.front().lchild != NULL) //如果有左孩子,lchild入队列
{
q.push(*q.front().lchild);
}
if (q.front().rchild != NULL) //如果有右孩子,rchild入队列
{
q.push(*q.front().rchild);
}
q.pop();
if(!q.empty()){
cout << " → ";
}
}
}
int numNode(tree* T)//计算节点个数
{
int count=0;
if(T)
{
++count;
count+=numNode(T->lchild);
count+=numNode(T->rchild);
}
return count;
}
int depth(tree* T)//计算树的深度
{
int leftLen,rightLen;
if(T==NULL)return 0;
else {
leftLen=depth(T->lchild)+1;
rightLen=depth(T->rchild)+1;
}
if(leftLen>rightLen)return leftLen;
else return rightLen;
}
int numLeaf(tree *T)//计算叶子节点的个数
{
int num=0;
if(T)
{
if(T->lchild==NULL&&T->rchild==NULL){
++num;
}
num+=numLeaf(T->lchild);
num+=numLeaf(T->rchild);
}
return num;
}
int main()
{
struct tree *T=new tree;
T=NULL;
cout<<"输入你要创建的二叉树:"<<endl;
create(T);
cout<<"节点的个数是:"<<numNode(T)<<endl;
cout<<"树的节点深度:"<<depth(T)<<endl;
cout<<"树的叶子节点数:"<<numLeaf(T)<<endl;
cout<<"先序遍历:"<<endl; priorOrder(T);
cout<<endl;
cout<<"后续遍历:"<<endl; behindOrder(T);
cout<<endl;
cout<<"中序遍历:"<<endl; midOrder(T);
cout<<endl;
cout<<"层序遍历:"<<endl; cengOrder(T);
cout<<endl;
return 0;
}
4.大整数加法
# include <stdio.h>
# include <string.h>
# define N 205
int main ()
{
char char1[205] = {0};
char char2[205] = {0};
int num1[N] = {0},num2[N] = {0},result[N] = {0},m,n,p,j;
gets(char1);
gets(char2);//输入两个字符串
m = strlen(char1);
n = strlen(char2);//求出两个字符串的长度
for(int i = m - 1;i >= 0;i --){
num1[m - 1 - i] = char1[i] - '0';
}
for(int i = n - 1;i >= 0;i --){
num2[n - 1 - i] = char2[i] - '0';//将两个字符串的值赋给数组
}
for(int i = 0;i < 205;i ++){
p = num1[i] + num2[i] + result[i + 1];
result[i] = p % 10;
result[i + 1] = p / 10;//将数组进行相加,%q表示这一位的数值大小,/q表示下一位的进位
}
j = 200;
while(result[j] == 0&&j > 0){
j --;//得到数组e[i]不为0的第一位
}
while(j > -1){
printf("%d",result[j]);
j --;//将e[i]逆序输出,最后一位即为最后结果的第一位
}
return 0;
}