defselect_sort(lst): l = len(lst) for i inrange(l): idx = i for j inrange(i, l): if lst[j] < lst[idx]: idx = j # print(lst) lst[i], lst[idx] = lst[idx], lst[i]
key = lst[left] lp = left rp = right while lp < rp: # 必须先移动rp,否则若不采用三数取中法会出错,测试案例[4,6,6] while lp < rp and lst[rp] >= key: rp -= 1 while lp < rp and lst[lp] <= key: lp += 1 if lp < rp: lst[lp], lst[rp] = lst[rp], lst[lp] lst[left], lst[lp] = lst[lp], lst[left] fast_sort(lst, left, lp - 1) fast_sort(lst, rp + 1, right) return lst
voidCountSort(vector<int>& arr) { int len = arr.size(); int maxVal = arr[0]; int minVal = arr[0]; for (int i = 1; i < arr.size(); i++) { if (arr[i] > maxVal) { maxVal = arr[i]; } if (arr[i] < minVal) { minVal = arr[i]; } }
vector<int> countArray(maxVal - minVal + 1, 0); for (int i = 0; i < arr.size(); i++) { countArray[arr[i] - minVal]++; }
int p = 0;
for (int i = 0; i < countArray.size(); i++) { for (int j = 0; j < countArray[i]; j++) { arr[p++] = i + minVal; } } }
} // 递推算法 intfibonacci_front(int n){ int f[2] = { 1, 1 }; if (n < 2) return f[n]; for (int i = 2; i <= n; i++) { int temp = f[0] + f[1]; f[0] = f[1]; f[1] = temp; } return f[1]; }
voidmain(){ int n = 15; for (int i = 0; i <= n; i++) { cout << "递归" << i << ":" << fibonacci_back(i) << endl; cout << "递推" << i << ":" << fibonacci_front(i) << endl; } }
1.2 集合全排列问题
对集合的元素进行排列。
c++
// 产生元素k到m的全排列,作为前k到1个元素的后缀 voidPerm(int list[], int k, int m, int& res) { // 构成一次全排列输出结果 if (k == m) { for (int i = 0; i <= m; i++) cout << list[i] << " "; cout << endl; res++; } else { // 在数组list中,产生元素k到m的全排列 for (int j = k; j <= m; j++) { swap(list[k], list[j]); Perm(list, k + 1, m, res); swap(list[k], list[j]); } } }
voidmain(){ int list[len]; int res = 0; for (int i = 0; i < len; i++) { list[i] = i + 1; } Perm(list, 0, 3, res); // 输出排列的个数 cout << res << endl; }
1.3 整数划分问题
把一个正整数划分成任意个整数相加。记正整数 n,m 是它的划分,所以可以有如下情况:
f(1, m) = 1, m >= 1:无论 m 为几,n 只能分成 1
f(n, 1) = 1, n >= 1:无论 n 为几,只有一种划分,就是 n 个 1
f(n, m) = f(n, n), m >= n:m 比 n 大时,n 也分不出 m,所以可以化简成 m = n
f(n, m) = f(n, m - 1) + f(n - m, m ),n > m > 1:由此划分成两数之和后继续划分。例如:f(6,4) = f(6,3) + f(2, 4),意思是 6=4+2,然后 4 和 2 可以递归向下分。
c++
intsplit(int n, int m) { if (n == 1 || m == 1) return1; elseif (n < m) returnsplit(n, n); elseif (n == m) returnsplit(n, n - 1) + 1; elsereturnsplit(n, m - 1) + split(n - m, m); }
2 分治
2.1 分治基础
分治步骤:
分解:将问题分解成若干个与原问题形式相同的相互独立的子问题。
解决:直接解决容易解决的小问题,否则递归地解决各个子问题。
合并:将各个子问题的解合并为原问题的解。
适用场景:
问题可以被分解,且各个子问题是独立的。
问题缩小到一定程度可以被容易解决
子问题的解可以合并为该问题的解。
c++
// 设计伪代码 Divide_and_Conquer(P) { if( |P| <= n0 ) returnadhoc(P); // divide P into smaller substances P1,P2, ... ,Pk; for(int i = 0;i <= k;k++) yi = Divide_and_Conquer(Pi); // 递归解决Pi问题 returnmerge(y1,y2, ... , yk); // 合并子问题 }
2.2 二分查找
c++
// n是数组长度 template<class Type> intBinarySearch(Type arr[], const Type& res, int len) { int lp = 0; int rp = len - 1; while (lp <= rp) { int mid = (lp + rp) / 2; if (res == arr[mid]) return mid; elseif (res > arr[mid]) lp = mid + 1; else rp = mid - 1; } return-1; }
*2.3 循环赛日程表
设有 n 个选手进行循环比赛,其中 n = pow(2,k),要求每名选手要与其他 n-1 名选手都赛一次,每名选手每天比赛一次,循环赛共进行 n-1 天,要求每天没有选手轮空。
输入:k
输出:表格形式的比赛安排表
c++
classSolution { private: int len = 100; public: vector<vector<int>> arr;
Solution() { arr.resize(len, vector<int>(len)); for (int i = 0; i < len; i++) { arr[i].resize(len); } }
voidCopy(int tox, int toy, int fromx, int fromy, int r){ for (int i = 0; i < r; i++) { for (int j = 0; j < r; j++) { arr[tox + i][toy + j] = arr[fromx + i][fromy + j]; } } }
voidTable(int k){ int i, r; int n = 1 << k;
for (int i = 0; i < n; i++) arr[0][i] = i + 1;
for (r = 1; r < n; r <<= 1) for (int i = 0; i < n; i += 2 * r) { Copy(r, r + i, 0, i, r); Copy(r, i, 0, r + i, r); } }
voidPrint(int k){ for (int i = 0; i < k; i++) { for (int j = 0; j < k; j++) { cout << arr[i][j] << " "; } cout << endl; } } };
给定一个自然数 n 给定一个自然数 n,由 n 开始可以依次产生半数集 set (n) 中的数如下:
n ∈ set(n);
在 n 的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;
按此规则进行处理,直到不能再添加自然数为止。
例如,set (6) = {6,16,26,126,36,136}。半数集 set (6) 中有 6 个元素。
注意半数集是多重集。
对于给定的自然数 n,计算半数集 set (n) 中的元素个数。
c++
classSolution { private: vector<int> temp; public: Solution() : temp(1000, 0) {} // 普通分治 intcomp(int n) { int res = 1; if (n > 1) for (int i = 1; i <= n / 2; i++) res += comp(i); return res; }
// 记忆化,添加数组保存结果,防止重复搜索 intMemoryComp(int n) { int res = 1; if (temp[n] > 0) return n; if (n > 1) for (int i = 1; i <= n / 2; i++) res += comp(i); temp[n] = res; return res; } };
2.8 整数因子分解
大于 1 的正整数 n 可以分解成:n=x1 * x2 * ... * xm
例如 12 可以分成:12=12、12=6 x 2、12=3 x 4、12=3 x 2 x 2 共 4 种。
c++
classSolution { public: voidslove(int n, int& res) { if (n == 1) res++; elsefor (int i = 2; i <= n; i++) if (n % i == 0) slove(n / i, res); } };
利用二分思想,首先判断 n 的正负,正的直接递归,负的先取正,然后最后结果用 1 除以即可。然后进行递归,当 n 为 0 表示走到尽头,返回 1。否则利用二分思想,可以将数一直开方。由递推可知,当一个 n 是偶数时,可以直接相乘,如果 n 是奇数,就在此基础上再乘一个 x。比如 x ** 4 变成 x ** 9,此时 n 是 9,向下二分得到是 (x ** 4 ) * (x ** 4 ) * x,因此奇数要多乘一个 x。
python
# 递归法 classSolution: defmyPow(self, x: float, n: int) -> float: defpow(n): if n == 0: return1 y = pow(n // 2) return y * y if (n & 1) == 0else y * y * x returnpow(n) if n >= 0else1 / pow(-n)