归并排序
又称合并排序,其核心是分治思想。分治法详细请看百度百科
《算导》p20:分治法中的递归式是基于基本模式中的三个步骤的。如先前一样,设T(n)为一个规模为n的问题的运行时间。如果问题的规模足够地小,如n≤c(c为一个常量),则得到它的直接解的时间为常量,写作Θ(1)。假设我们把原问题分解成a个子问题,每一个的大小是原问题的1/b。(对于合并排序,a和b都是2,但在许多分治法中,a≠b。)如果分解该问题和合并解的时间各为D(n)和C(n),则得到递归式:
我们则知道,归并时间复杂度是Θ(n lg n)的了(具体看算导p20-p22)
上我自己的代码
#define WHI(i,a,n) for(i=(a);i<(n);++i) #define REP(i,a,n) for(int i=(a);i<(n);++i) const int MAXi = 1e5+10, oo = ~0u>>1; int L[MAXi], R[MAXi]; void merge_sort(int* arr, int f, int l){ if(f < l-1){ int m = (f+l) >> 1, i = 0, j = 0; merge_sort(arr, f, m); merge_sort(arr, m, l); WHI(i, 0, m-f) L[i] = arr[f+i]; WHI(j, 0, l-m) R[j] = arr[m+j]; L[i] = R[j] = oo; i = j = 0; while(f < l) arr[f++] = L[i] < R[j] ? L[i++] : R[j++]; } }
霍纳规则(法则)
即“秦九韶方法” 用来计算多项式我们用D(i)来表示可推出
顺推: 答案是D(n)
逆推: 答案是D(0)
很容易看出可以用滚动求值,即D = a(n-1) + x * D 或 D = a(i) + x * D
代码
//顺推 for(int i = 0, D = 0; i <= n; ++i) D = a[n-i] + x * D; //逆推 for(int i = n, D = 0; i >= 0; ++i) D = a[i] + x * D;
统计逆序对
含义:对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。
很快就能用插入排序来统计,但O(n^2)咱们不采用,咱们用O(n lg n)的归并啦 >_<
代码
#define WHI(i,a,n) for(i=(a);i<(n);++i) #define REP(i,a,n) for(int i=(a);i<(n);++i) const int MAXi = 1e5+10, oo = (~0u>>1)+1; typedef unsigned long long ull; ull cnt; int L[MAXi], R[MAXi]; void mi_cnt(int* arr, int f, int l){ if(f < l-1){ int m = (f+l) >> 1, i = 0, j = 0; mi_cnt(arr, f, m); mi_cnt(arr, m, l); WHI(i, 0, m-f) L[i] = arr[f+i]; WHI(j, 0, l-m) R[j] = arr[m+j]; L[i] = R[j] = oo; i = j = 0; while(f < l) if(L[i] > R[j]){ arr[f++] = L[i++]; cnt += l-m-j; } else arr[f++] = R[j++]; } }
OK。写完文章了,欢迎大家指点~