408---必须能手搓的算法
一、快速排序
无需多言
// 2023-12-19 #include <iostream> #include <cstring> using namespace std; void debug(int A[],int n){ for(int i=0;i<n;i++) printf("%d ",A[i]); puts(""); } void Qsort(int A[],int left,int right){ if(left >= right) return ; int l,r,pivot; l=left,r=right,pivot = A[left]; while(l<r){ while(l<r && A[r] >= pivot) r--; A[l] = A[r]; while(l<r && A[l] <= pivot) l++; A[r] = A[l]; } A[l] = pivot; Qsort(A,left,l-1); Qsort(A,l+1,right); } int main(){ int A[20] = {0}; for(int i=0;i<=19;i++) A[i]=50-i+1; debug(A,20); Qsort(A,0,19); debug(A,20); return 0; }
二、归并排序
// 2023-12-19 #include <iostream> #include <cstring> using namespace std; void debug(int A[],int n){ for(int i=0;i<n;i++) printf("%d ",A[i]); puts(""); } void TMerge(int A[],int left,int mid,int right){ int i,j,k; int *B = (int *)malloc(sizeof(int) * 20); i=left,j = mid+1,k = left; while(i<=mid && j<=right){ if(A[i] < A[j]) B[k++] = A[i++]; else B[k++] = A[j++]; } while(i<=mid) B[k++] = A[i++]; while(j<=right) B[k++] = A[j++]; for(i=left;i<=right;i++){ A[i] = B[i]; B[i] = 0; } free(B); } void Merge(int A[],int left,int right){ if(left >= right) return ; int mid = left+right >> 1; Merge(A,left,mid); Merge(A,mid+1,right); TMerge(A,left,mid,right); } int main(){ int A[20]; for(int i=0;i<=19;i++) A[i] = 50-i; debug(A,20); Merge(A,0,19); debug(A,20); return 0; }
三、并查集及其优化
#include <stdio.h> #include <math.h> int find(int A[],int m); void Init(int A[],int len){ for(int i=0;i<len;i++)A[i]=-1; } void debug(int A[],int len){ for(int i=0;i<len;i++) printf("%3d ",A[i]); puts(""); } void Union(int A[],int x1,int x2){ int root1 = find(A,x1); int root2 = find(A,x2); if(root1 != root2){ A[root1] = root2; // 把x1合并到x2中 } } int find(int A[],int m){ if(A[m] < 0) return m; return find(A,A[m]); } int optfind(int A[],int m){ if(A[m] < 0) return m; int root = find(A,A[m]); A[m] = root; // 让路过的元素都指向root return root; } void opt1Union(int A[],int x1,int x2){ int root1 = optfind(A,x1); int root2 = optfind(A,x2); if(root1 != root2){ A[root1] = root2; // 把x1合并到x2中 } } void opt2Union(int A[],int x1,int x2){ // 结点小的树合并到结点多的树,俗称大树合并到小树 int root1 = optfind(A,x1); int root2 = optfind(A,x2); if(root1 == root2) return ; if(abs(A[root1]) > abs(A[root2])){ // 越大的结点越多 A[root1] += A[root2]; A[root2] = root1; }else{ A[root2] += A[root1]; A[root1] = root2; } } int main(){ int A[20] = {0}; Init(A,20); // Union(A,1,2); // debug(A,10); // Union(A,3,2); // debug(A,10); // Union(A,5,6); // debug(A,10); // Union(A,7,6); // debug(A,10); // Union(A,2,4); // debug(A,10); // Union(A,6,4); // debug(A,10); /* 4 2 6 1 3 5 7 */ /* -1 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 2 -1 2 -1 -1 -1 -1 -1 -1 -1 2 -1 2 -1 6 -1 -1 -1 -1 -1 2 -1 2 -1 6 -1 6 -1 -1 -1 2 4 2 -1 6 -1 6 -1 -1 -1 2 4 2 -1 6 4 6 -1 -1 */ // opt1Union(A,1,2); // debug(A,10); // opt1Union(A,3,2); // debug(A,10); // opt1Union(A,5,6); // debug(A,10); // opt1Union(A,7,6); // debug(A,10); // opt1Union(A,2,4); // debug(A,10); // opt1Union(A,1,6); // debug(A,10); /* 4 2 6 1 3 5 7 */ // =========> opt1Union(1,6) /* 6 1 2 4 5 7 3 */ /* -1 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 2 -1 2 -1 -1 -1 -1 -1 -1 -1 2 -1 2 -1 6 -1 -1 -1 -1 -1 2 -1 2 -1 6 -1 6 -1 -1 -1 2 4 2 -1 6 -1 6 -1 -1 -1 4 4 2 6 6 -1 6 -1 -1 */ // 在把1合并到6结点的时候可以发现,find函数并不是把1所在的子树的所有结点都连接到6结点下,而是先把1所在子树的结点都指向1所在子树的根,再把根指向6所在结点的根 // 可以看这种图: https://img2023.cnblogs.com/blog/2433096/202311/2433096-20231130194814117-562472168.png opt2Union(A,1,2); debug(A,10); opt2Union(A,3,2); debug(A,10); opt2Union(A,5,6); debug(A,10); opt2Union(A,7,6); debug(A,10); opt2Union(A,2,4); debug(A,10); opt2Union(A,1,6); debug(A,10); /* -1 2 -2 -1 -1 -1 -1 -1 -1 -1 -1 2 -3 2 -1 -1 -1 -1 -1 -1 -1 2 -3 2 -1 6 -2 -1 -1 -1 -1 2 -3 2 -1 6 -3 6 -1 -1 opt2Union(A,2,4); //我们原本想让2合并到4,但由于2是大树,故只能4合并到2 -1 2 -4 2 2 6 -3 6 -1 -1 -1 2 -7 2 2 6 2 6 -1 -1 */ return 0; }
简易版(无分析数据版
// 2023-12-19 // 估计是考前最后一次手搓并查集及其优化了 #include <iostream> #include <cstring> using namespace std; void Init(int A[],int n){ for(int i=0;i<n;i++) A[i] = -1; } int find(int A[],int x){ return A[x] > 0 ? A[x] : x; } int optfind(int A[],int x){ if(A[x] < 0) return x; int Root = optfind(A,A[x]); A[x] = Root; // 比起朴素的find操作,增添了这项代码 return Root; } void Union(int A[],int x1,int x2){ // x1的根合并到x2中 int Root1 = find(A,x1); int Root2 = find(A,x2); if (Root1 == Root2) return ; A[Root1] = Root2; return ; } void opt1Union(int A[],int x1,int x2){ int Root1 = optfind(A,x1); int Root2 = optfind(A,x2); if (Root1 == Root2) return ; A[Root1] = Root2; return ; } int abs(int m){// 取绝对值 return m>0 ? m : -m; } void opt2Union(int A[],int x1,int x2){ // 小树合并到大树上 int Root1 = optfind(A,x1); int Root2 = optfind(A,x2); if(Root1 == Root2) return ; if(abs(A[Root1]) > abs(A[Root2])){ A[Root1] += A[Root2]; A[Root2] = Root1; }else{ A[Root2] += A[Root1]; A[Root1] = Root2; } } void debug(int A[],int n){ for(int i=0;i<n;i++) printf("%3d ",A[i]); puts(""); } int main(){ int A[20] = {0}; Init(A,20); // debug(A,20); // 朴素算法 // 一次Find 最坏时间复杂度 : O(n) // 总体是O(n^2)的时间复杂度 // Union(A,1,2); // debug(A,10); // Union(A,3,2); // debug(A,10); // Union(A,5,6); // debug(A,10); // Union(A,7,6); // debug(A,10); // Union(A,2,4); // debug(A,10); // Union(A,6,4); // debug(A,10); // 优化一---find路径压缩 // 一次Find 最坏时间复杂度 : O(α(n)) // 总体是 : O(n · α(n)) // opt1Union(A,1,2); // debug(A,10); // opt1Union(A,3,2); // debug(A,10); // opt1Union(A,5,6); // debug(A,10); // opt1Union(A,7,6); // debug(A,10); // opt1Union(A,2,4); // debug(A,10); // opt1Union(A,1,6); // debug(A,10); // 优化二---大树合并小树 // 一次Find 最坏时间复杂度 : $O(log_2n)$ // 总体是 : $O(log_2n)$ opt2Union(A,1,2); debug(A,10); opt2Union(A,3,2); debug(A,10); opt2Union(A,5,6); debug(A,10); opt2Union(A,7,6); debug(A,10); opt2Union(A,2,4); debug(A,10); opt2Union(A,1,6); debug(A,10); return 0; }