分治策略
递归与分治策略:
- 是将规模比较大的问题可分割成规模较小的相同问题。问题不变,规模变小。 这自然导致递归过程的产生。
- 分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
- 递归:若一个函数直接地或间接(最好不用间接)地调用自己,则称这个函数是递归的函数。(简单地描述为 “自己调用自己”) .
分治法所能解决的问题-般具有以下四个特征:
- 该问题的规模缩小到 定的程度就可以容易地解决。
- 该问题可以分解为若干个规模较小的相同问题。
- 使用小规模的解 可以合并成,该问题原规模的解。
- 该问题所分解出的各个子规模是相互独立的。
分治法步骤:
在分治策略中递归地求解一 个问题, 在每层递归中应用如下三个步骤: .
- 分解:将问题划分成一些子问题,子问题的形式与原问题一样,只是规模更小。
- 解决:递归地求解子问题。如果子问题的规模足够小,则停止递归,直接求解。
- 合并:将小规模的解组合成原规模问题的解。
1.求阶乘
普通求法
int fun(int n)
{
int sum = 1;
for (int i = 1; i <= n; ++i)
{
sum = sum * i;
}
return sum;
}// O(n) , S(1)
递归求法
int fac(int n)
{
if (n <= 1)
return 1;
else
return fac(n - 1) * n;
}
// O(n) S(n) // o(n)
// 1m 10m;
2.斐波那契数列
普通求法
int fun(int n)
{
int a = 1, b = 1, c = 1;
for (int i = 3; i <= n; ++i)
{
c = a + b;
b = a;
a = c;
}
return c;
} // O(n). S(1)
递归求法
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}// O(2^n) // S(n)
改良方法
int fib(int n, int a, int b)
{
if (n <= 2) return a;
else
return fib(n - 1, a + b, a);
}
int fib(int n)
{
int a = 1, b = 1;
return fib(n, a, b);
}
//O(n),S(n)
3.二分查找
普通求法
int BinaryFind(const vector<int>& ar, int val)
{
int left = 0, right = ar.size() - 1;
int pos = -1;
while (left <= right)
{
int mid = (right - left) / 2 + left;
//如果写成 int mid = (right + left)/2 ,则可能越界
if (val < ar[mid])
{
right = mid - 1;
}
else if (val > ar[mid])
{
left = mid + 1;
}
else
{
//while (mid > left && ar[mid - 1] == val) --mid; //left 找到最左边的相同值
//while (mid < right && ar[mid + 1] == val) ++mid; // right 找到最右边的相同值
if (mid > left && ar[mid - 1] == val)
{
right = mid - 1;
}
else
{
pos = mid;
break;
}
}
}
return pos;
}
int main()
{
std::vector<int> ar = { 12,23,34,45,56,67,78,89,90,100 };
BinaryFind(ar,23);
return 0;
}
递归求法
int BinaryFind(const vector<int>&arr,int low,int high,int val)
{
if (low > high) return -1;
int mid = (high - low) / 2 + low;
if (val == arr[mid])
return mid;
else if (val < mid)
{
high = mid - 1;
return BinaryFind(arr, low, high, val);
}
else
{
low = mid + 1;
return BinaryFind(arr, low, high, val);
}
}
4.快排
递归方法
int Parition(int* ar, int left, int right)
{
assert(ar != nullptr);
int tmp = ar[left];
while (left < right)
{
while (left < right && ar[right] > tmp) --right;
ar[left] = ar[right];
while (left < right && ar[left] <= tmp) ++left;
ar[right] = ar[left];
}
ar[left] = tmp;
return left;
}
void QuickPass(int* ar, int left, int right)
{
if (left < right) //
{
int pos = Parition(ar, left, right);
QuickPass(ar, left, pos - 1);
QuickPass(ar, pos + 1, right);
}
}
int main()
{
int ar[] = { 56,12,89,90,23,34,100,56,45,78,67 };
int n = sizeof(ar) / sizeof(ar[0]);
for (auto& x : ar) { cout << x << " "; }
cout << endl;
QuickSort(ar, n);
for (auto& x : ar) { cout << x << " "; }
return 0;
}
非递归方法
void NiceQuickPass(int* ar, int left, int right)
{
std::queue<int> qu;
qu.push(left); qu.push(right);
while (!qu.empty())
{
left = qu.front(); qu.pop();
right = qu.front(); qu.pop();
int pos = Parition(ar, left, right);
if (left < pos - 1)
{
qu.push(left);
qu.push(pos - 1);
}
if (pos + 1 < right)
{
qu.push(pos + 1);
qu.push(right);
}
}
}
void QuickSort(int* ar, int n)
{
NiceQuickPass(ar, 0, n - 1);
}