分治算法小结
我的理解:
分治算法我的理解就是看人下菜碟,我们要解决的问题就好像一群人构成的集体,要我们解决这个问题,那我们就要满足这群人里面每个人不同的需求,也就是写出解决的代码,把每个人都解决了,这个群体也就解决了;
例题一:归并排序;
注意:
归并排序的重点在合并的时候要将已经排过数不在进行排序,否则会进行多次重复的比较,浪费时间
代码
#include<iostream>
#include<vector>
using namespace std;
void sortnum(vector<int>& num, int start, int end)
{
int mid = -1;
vector<int> temp;
int i = start;
int j = 0;
mid = (start + end) / 2;
j = mid + 1;
int nb = 0;
while (i <= mid && j<= end)
{
if (num[j] < num[i])
{
nb = num[j];
temp.push_back(num[j]);
j++;
}
else
{
nb = num[i];
temp.push_back(num[i]);
i++;
}
}
while (i<=mid)
{
temp.push_back(num[i]);
i++;
}
while (j <= end)
{
nb = num[j];
temp.push_back(num[j]);
j++;
}
int k = start;
int l = 0;;
for (; k <= end; k++)
{
num[k] = temp[l];
l++;
}
}
void dividenum(vector<int>& arr, int frist, int end) {
int mid=-100;
if (end>frist)
{
mid = (frist + end) / 2;
dividenum(arr, frist, mid);
dividenum(arr, mid + 1, end);
sortnum(arr, frist, end);
}
}
int main()
{
vector<int> num;
cout << "输入要排序的数组,输入-1结束:" << endl;
int nu = -1;
cin >> nu;
while (nu != -1)
{
num.push_back(nu);
cin >> nu;
}
dividenum(num, 0, num.size() - 1);
//sortnum(num, 0, num.size - 1);
int i = 0;
while (i<num.size()-1)
{
cout << num[i] << " ";
i++;
}
}
例题2 快速插入排序:
#include<iostream>
#include<vector>
using namespace std;
void quitsort(vector<int>& num, int start, int end)
{
int temp = 0;
temp=num[start];
vector<int> num2,num3;
num3.push_back(temp);
int i = 0;
int count = 0;
if (end > start)
{
for (i = start + 1; i <= end; i++)
{
if (num[i] < temp)
{
num2.push_back(num[i]);
count = count + 1;
}
else
{
num3.push_back(num[i]);
}
}
//for (int j = 0; j < num2.size(); j++)
//{
// cout << num2[j];
//}
////cout << "\n";
//for (i = 0; i < num3.size(); i++)
//{
// cout << num3[i];
//}
////cout << "\n";
int mid = 0;
if (count != 0)
{
mid = start + count - 1;
}
else
{
mid = start ;
}
int k = 0;
if (num2.size() != 0)
{
for (i = start; i <= mid; i++)
{
num[i] = num2[k];
k++;
}
}
k = 0;
if(num3.size()!=0)
{
if (count != 0)
{
for (i = mid + 1; i <= end; i++)
{
num[i] = num3[k];
k++;
}
}
else
{
for (i = mid; i <= end; i++)
{
num[i] = num3[k];
k++;
}
}
}
if (mid > -1)
{
quitsort(num, start, mid);
quitsort(num, mid + 1, end);
}
}
//for (i = start; i <=end; i++)
//{
// cout << num[i] << " ";
//}
//int k = start;
//for (; k <= end; k++)
//{
// num2.push_back(num[k]);
//}
/*for (i = start; i <= end; i++)
{
num[i] = num2[i];
}*/
//quitsort(num, start, mid);
//quitsort(num,mid+1)
}
int main()
{
vector<int> num;
cout << "输入要排序的数组,输入-1结束:" << endl;
int n = -1;
cin >> n;
while (n!=- 1)
{
num.push_back(n);
cin >> n;
}
quitsort(num, 0, num.size() - 1);
cout << "排序后的结果为:\n";
for (int i = 0; i < num.size(); i++)
{
cout << num[i]<<" ";
}
}
例题3四叉树问题:
四叉树编码的基本思想是:首先将把一副图像或栅格地图( ,k>1,不足则补网)等分成四个一级字块,顺序为左上,右上,左下,右下;然后逐块检查其中所有格网属性值(或灰度值),若相同,则该字块不再分;若不同,则将该子块进一步分成四个二级子块;如此递归地分割,直到每个子块的属性或灰度均相等为止。
如下图:
规则:
1:图像所有像素为黑色,无论大小是多少,压缩结果全为b;
2:图像所有像素为白色,无论大小,压缩结果为w;
3图像的像素不都是相同颜色,整个图像的压缩结果为先把图像一分为4,然后对4个小图像进行压缩,整个的图像压缩结果为x,如:下图a的左上角4个小图像压缩结果为:xwwwwb.
下图的构成的树是对上图进行的压缩结果;
所以图a的压缩结果为:
xxwww bxwxw bbbww xxxww bbbww wwbb
我们要解决的就是将压缩后的图片进行上下翻转;
说明:
可能晓得图像可以直接解压然后翻转,但是像素特别大的图像是不可能在规定时间内直接解压完成的。
输入
xbwxwbbwb
输出
xxbwwbbbw
代码:
#include<iostream>
using namespace std;
string change(string::iterator& it)
{
char head = *it;
it++;
if (head == 'b' || head == 'w')
{
return string (1, head);
}
string upleft = change(it);
string upright = change(it);
string lowleft = change(it);
string lowrigiht = change(it);
return string("x") + lowleft + lowrigiht + upleft + upright;
}
int main()
{
string s="xbwxwbbwb";
string::iterator it;
it = s.begin();
string a;
a = change(it);
cout << a << endl;
}
/*
这题结构很简单,但是有着很深的递归结构
注意一点:在本题中迭代器的使用很重要,迭代器的作用类似于指针,
对于迭代器:
定义:迭代器是一种检查容器内元素并遍历元素的数据类型。
迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围
*/
例题3切割篱笆
在一段高度不相同的篱笆木板中切割出面积最大的长方形,篱笆的宽度都为1;
不允许斜向切割。
此题是中等难度的分治问题,关键的地方都在代码注释里写了;
输入
木板数量:7
木板高度:1 4 4 4 4 1 1
输出
最大面积: 16
代码:
#include<iostream>
#include <algorithm>
using namespace std;
/*
本题分为三个子问题:
1 当最大面积在左边时如何求解;
2 当最大面积在右边时如何求解;
3 当最大面积在中间时如何求解;
注意:当第三种情况时,可以将板向两边一格一格延伸,每次的
延伸方向总是向着高度更高的木板延伸,每次计算出来的面积和当前最大面积进行对比较,
这样可以减少计算量。
*/
int height[100];
int slove( int frist, int end)
{
//第一个递归出口,只剩下一块板;
if (frist == end)
{
return height[frist];
}
int mid = (frist + end) / 2;
int permax = 0;//记录当前最大的面积;
//第二种情况的解决,不断调用自身求解最大面积
permax = max(slove(frist, mid), slove(mid + 1, end));
//解决第三个问题,横跨中间时面积的解
int left = 0;
int right = 0;
left = mid;
right = mid + 1;
int hei = min(height[left], height[right]);
permax = max(hei * 2,permax);
while (frist<left || end>right)
{
if (frist<left && right == end || height[left-1]>height[right+1])
{
left--;
hei = min(height[left],hei);
}
else
{
right++;
hei = min(height[right], hei);
}
permax =max(permax,hei * (right - left + 1));
}
return permax;
}
int main()
{
cout << "输入木板数量" << endl;
int n = 0;
cin >> n;
cout << "输入木板高度:" << endl;
int i = 0;
for (i = 0; i < n; i++)
{
cin >> height[i];
}
int permax = 0;
permax=slove(0, n - 1);
cout << permax;
}
/*
分治算法小结:
1:要理解子问题的概念,分治中的子问题是把原问题分解成多个不同性质的子问题,
和穷举递归时不同;
2在设计递归函数是,要先找好递归的出口,将所有可能的递归出口想全面,
我的方法是先递归到最小单元,将可能遇到的情况和解决的函数写好,在去想递进和回溯的过程。
因为递进和回溯涉及到的是过程,也就是参数的传递和值得共享,而真正解决问题的是递归出口的
代码,有时候可能我们知道解题的方法和思路,但是在设计递归函数的时候总是会思路混乱。
所以就将整个递归函数分开看,一个是传递参数和共享的值部分,另一个是递归出口部分。
*/