递归

来源:互联网 发布:供应商考核表数据 编辑:程序博客网 时间:2024/06/13 13:34

递归的本质是描述问题:描述要干什么,而不是怎么做。另外注意,留意是否能终止。

递归分为两步,递,以树的形式展开,但是问题的规模减小了;归,最后还要能够返回,就是能够终止。

快排是递归和分治的经典例子

通过一趟排序将要排序的数据分割成前后两部分,其中前边部分的所有数据都比后边部分的所有数据都要小,然后再按此方法对这两部分数据分别进行一次分割,这样进行下去直到不能再分,以此达到整个数据变成有序序列。

这个过程用递归来描述如下:

void QuickSort(int array[], int low, int high)   //low 和 high是每次分组的数组的前后两个下标   到 low和high相等时 不能再分  说明已经排序好{if (low < high){int n = Partition(array, low, high);     //n即是通过某种算法,这里也就是Partition 函数,使索引为n的元素左边的元素都小于它  右边的元素都大于它QuickSort(array, low, n - 1);    //经过第一次分组  索引为n的元素刚好在正确的位置 不用再排  然后前后两部分  这里的n-1  和 n+1QuickSort(array, n + 1, high);      //分别对前后部分重复进行这个过程   }}
这里的快排的描述已经完毕,还有一个问题就是怎么每次分成前后两组的算法Partition() 函数的实现:

具体可以这样做:选择一个枢纽元素,比如每次分组的第一个元素,从分组的后边还是寻找第一个比枢纽元素大的元素,与枢纽交换位置;然后从前开始,寻找第一个比枢纽元素小的元素,与枢纽元素交换位置;如此这般,每次从后找一个比枢纽元素大的元素,与枢纽元素交换位置,然后从前往后寻找一个比枢纽元素小的元素,再与被换到后边的枢纽元素交换(每次交换实际上都是和枢纽元素交换位置),最后枢纽元素便被换到一个前边的都比其小,后边比起大(或等于)的正确位置上。

int Partition(int array[], int low, int high){// 采用子序列的第一个元素为枢纽元素int pivot = array[low];while (low < high){// 从后往前在后半部分中寻找第一个小于枢纽元素的元素while (low < high && array[high] >= pivot){--high;}// 将这个比枢纽元素小的元素交换到前半部分swap(array[low], array[high]);// 从前往后在前半部分中寻找第一个大于枢纽元素的元素while (low < high && array[low] <= pivot){++low;}// 将这个比枢纽元素大的元素交换到后半部分swap(array[low], array[high]);}// 返回枢纽元素所在的位置return low;}

或者如果每次枢纽元素都选择中间的那个:  保证每次都是同枢纽元素交换交换即可,函数的返回值为枢纽元素的索引

int Partition(int array[], int low, int high){// 采用子序列的第一个元素为枢纽元素int index = (low + high)/2;     //这里index用来追踪枢纽元素的索引int pivot = array[index];while (low < high){// 从后往前在后半部分中寻找第一个小于枢纽元素的元素while (index < high && array[high] >= pivot)      //这里是index索引的右边{--high;}// 将这个比枢纽元素小的元素交换到前半部分swap(array[index], array[high]);index = high;// 从前往后在前半部分中寻找第一个大于枢纽元素的元素while (low < index && array[low] <= pivot)   这里是index的左边{++low;}// 将这个比枢纽元素大的元素交换到后半部分swap(array[low], array[index]);index = low;}// 返回枢纽元素所在的位置return low;}

归并排序也是一个递归分治

void MergeSort(int a[], int left, int right){if (left < right){int center = (left + right) / 2;          //取得中点用来分组//将原来序列分为两段MergeSort(a, left, center);     //对前边一组进行这种操作MergeSort(a, center + 1, right);    //对后边一组进行这种操作                                     //前后两组已经排序号,进行合并Merge(a, left, center, right);    //合并的算法}}

现在的问题转化为怎么合并两个已经排序好的序列:

void Merge(int a[], int left, int center, int right){int *t = new int[right - left + 1];//存放中间值结果int i = left;   //前边段的起始int j = center + 1;   //后边段的起始int k = 0;   //中间结果的起始//合并数组,用插入排序,如果左边大就插入左边的数;右边大就插入右边的数while (i <= center && j <= right){if (a[i] <= a[j])t[k++] = a[i++];elset[k++] = a[j++];}//上面的步骤在执行完后,左或右边都有可能剩余若干个元素,把剩下的元素都赋值过去if (i == center + 1){while (j <= right)t[k++] = a[j++];}else{while (i <= center)t[k++] = a[i++];}//把t[]的元素复制回a[]中left到right段for (i = left, k = 0; i <= right; i++, k++)a[i] = t[k];//释放内存delete[]t;}

尾递归

一个求和的例子如下,尾递归通过参数来记录上下两次函数调用之间的关系,尾递归f() 的嵌套中, 仍然是f() ,而不是由f() 组成的一个表达式,仅仅在f() 的参数上做文章。

int tail_recursive(int n, int a)   //尾递归 {                                           //返回值也是f()的形式  而不是包含f() 的一个运算表达式if (0 == n)                        //递归到最底层 直接return 返回了  不用一层层的返回return a;elsereturn tail_recursive(n - 1, n + a);}

f(n, a) = f (n-1 , n + a) = f(n - 2, n-1 + n + a)....     = f(1, 2 + 3 + ... + n + a) = f(0, 1 + 2 +3+ ... n-1 + n)   , 递归到最底层,直接return 得到最外层的值,而不用一层层的返回。

实际上,尾递归是通过一个额外的参数将函数结果保存起来,进入到最内存之后,将这个参数返回即可。

斐波那契数列的尾递归

int feibolaqie_tail(int n, int first, int second){if (n == 1 || n == 2)return second;return feibolaqie_tail(n - 1, second, first + second);}





0 0
原创粉丝点击