递归的重要性不言而喻,它是很多算法实现的基础,比如含有分治思想的算法(归并排序,二分查找),有关遍历二叉树的算法,或者求解数学递推式的算法(斐波那契数列,n的阶乘),回溯法,动态规划等等, 一提到递归总有点发蒙,理论上比较好理解,但是一遇到复杂一点的递归算法,在大脑中很难想象递归在计算机中是怎么实现的。跟着一步步debug才终于搞明白,所以在这里先把过程给记录下来。
归并排序算法:就是运用分治的思想,把排序的过程变为先把数组分成左右两个部分,分别排序,再将排好序的两个数组合并成一个有序数组。
代码:
void Merge(vector<int>& inputArray, int p, int q, int r)
{
int i = p;
int j = q + 1;
vector<int> temp;
while (i <= q && j <= r)
{
if (inputArray[i] <= inputArray[j]) {
temp.push_back(inputArray[i]);
i++;
}
else
{
temp.push_back(inputArray[j]);
j++;
}
}
int start = i;
int end = q;
if (j <= r)
{
start = j;
end = r;
}
for (int jj = start; jj <= end; jj++)
{
temp.push_back(inputArray[jj]);
}
for (int ii = 0; ii < temp.size(); ii++)
{
inputArray[p+ii] = temp[ii];
}
}
void Merge_sort_c(vector<int>& inputArray, int p, int r)
{
if (p >= r) return;
int q = (p + r) / 2;
Merge_sort_c(inputArray, p, q); //左边排序 #1
Merge_sort_c(inputArray, q + 1, r);//右边排序 #2
Merge(inputArray, p, q, r);//合并两个有序数组
}
vector<int> MergeSort(vector<int>& inputArray)
{
int n = inputArray.size();
Merge_sort_c(inputArray, 0, n-1);
return inputArray;
}
int main()
{
vector<int> input = { 4,5,6,3,2,1 };
vector<int> output = MergeSort(input);
for (int i = 0; i < output.size(); i++)
{
cout << output[i] << endl;
}
}
重点分析一下代码中 Merge_sort_c这个递归函数,首先是终止条件p>=r ,递归必须要有终止条件,否则就会陷入循环最终导致栈溢出。为啥会栈溢出?递归调用在底层其实是对线程栈的压栈和出栈操作,每调用一次都会压栈一次,并记录相关的局部变量信息,线程栈的内存是非常有限的,而递归调用如果是无限的,那么很快就会消耗完所有的内存资源,最终导致内存溢出。
接下来是两个调用了Merge_sort_c 函数本身也就是递归调用,将这两个递归调用分别编号#1和#2.在本例中,待排序的数组里面有6个元素(下标0-5), 那么他们是怎么被压栈又出栈的呢?如下图所示:
merge 的过程