开个新坑( ・´ω`・ ),算法基础系列用来记录自己在Acwing上学习和刷题的过程。共勉。
第一章  基础算法
快速排序
算法1:快速排序
快排用到了分治的思想,对一个下标左边界为$l$,下标右边界为$r$的数组,进行快速排序一般可以分为三个步骤:

其中最关键的是第二步-调整区间。暴力做法虽然时间复杂度是常数,但空间占用比较多(需要开额外的数组)。下面是优化后的方法:

在解题中为了避免在处理边界问题上浪费太多时间,可以记一些快排的模板。
- 快排模板题:给定你一个长度为$n$的整数数列。请你使用快速排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | #include<iostream> using namespace std;
  const int N = 1e6 + 10; int n; int q[N];
  void quick_sort(int q[], int l, int r) {     if(l >= r) return;          int x = q[l + r >> 1];        int i = l - 1, j = r + 1;        while(i < j)     {         do i++; while(q[i] < x);         do j--; while(q[j] > x);         if(i < j)  swap(q[i], q[j]);     }          quick_sort(q, l, j);     quick_sort(q, j+1, r); }
  int main() {     scanf("%d", &n);     for(int i = 0; i < n; i++)  scanf("%d", &q[i]);          quick_sort(q, 0, n-1);          for(int i = 0; i < n; i++)  printf("%d ", q[i]);     return 0;      }
   | 
 
快速排序的时间复杂度和空间复杂度:
分析和证明过程可以参考:【算法】快速排序
 | 
时间复杂度 | 
空间复杂度 | 
| 最优 | 
$O(n\log n)$ | 
$O(\log n)$ | 
| 最坏 | 
$O(n^2)$ | 
$O(n)$ | 
| 平均 | 
$O(n\log n)$ | 
$O(\log n)$ | 
 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
   | #include<iostream> using namespace std;
  const int N = 100010; int n, k; int q[N];
 
  int quick_sort(int l, int r, int k) {     if(l >= r) return q[l];     int x = q[l + r >> 1], i = l - 1, j = r + 1;     while(i < j)     {         while( q[ ++ i] < x);         while( q[ -- j] > x);         if( i < j )  swap(q[i], q[j]);     }     int sl = j - l + 1;     if( k <= sl ) return quick_sort(l, j, k);          return quick_sort(j + 1, r, k - sl); }
  int main() {     scanf("%d%d", &n, &k);     for(int i = 0; i < n; i++)  scanf("%d", &q[i]);          cout << quick_sort(0, n - 1, k);     return 0; }
   | 
 
归并排序
算法2:归并排序
归并排序也用到了分治的思想,通常有三个步骤:

归并排序中最关键的是第三步—归并,可以使用双指针,使时间复杂度为$O(n)$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | #include<iostream> using namespace std; const int N = 100010; int n; int q[N], tmp[N];
  void merge_sort(int q[], int l, int r) {     if(l >= r) return;          int mid = l + r >> 1;     merge_sort(q, l, mid);     merge_sort(q, mid+1, r);          int k = 0, i = l, j = mid+1;     while(i <= mid && j <= r)         if(q[i] < q[j])  tmp[k++] = q[i++];         else tmp[k++] = q[j++];     while(i <= mid) tmp[k++] = q[i++];     while(j <= r) tmp[k++] = q[j++];          for(i = l, j = 0; i <= r; i++, j++)         q[i] = tmp[j]; }
  int main() {     scanf("%d", &n);     for(int i = 0; i < n; i++) scanf("%d", &q[i]);          merge_sort(q, 0, n-1);          for(int i = 0; i < n; i++) printf("%d ", q[i]);     return 0; }
   | 
 


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | #include<iostream> using namespace std;
  typedef long long LL;
  const int N = 100010; int n; int q[N], tmp[N];
  LL merge_sort(int q[], int l, int r) {     if(l >= r)  return;     int mid = l + r >> 1;     LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);
           int k = 0, i = l, j = mid + 1;     while(i <= mid && j <= r)     {         if(q[i] <= q[j])    tmp[K ++] = q[i ++];         else         {             tmp[k ++] = q[j ++];             res += mid - i + 1;         }     }     while(i <= mid) tmp[k++] = q[i++];     while(j <= r) tmp[k++] = q[j++];
      for(i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];     return res; }
  int main() {     scanf("%d", &n);     for(int i = 0; i < n; i++)  scanf("%d", &q[i]);
      cout << merge_sort(q, 0, n - 1) << endl;     return 0; }
   | 
 
二分查找
算法3:二分查找


例题:给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。如果数组中不存在该元素,则返回“-1 -1”。
数据范围:$1 \le n \le 100000$,$1 \le q \le 10000$,$1 \le k \le 10000$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | #include<iostream> using namespace std;
  const int N = 100010; int n, m; int q[N];
  int main() {     scanf("%d%d", &n, &m);     for(int i = 0; i < n; i++) scanf("%d", &q[i]);     while(m--)     {         int x;         scanf("%d", &x);                           int l = 0, r = n-1;         while(l < r)         {             int mid = l + r >> 1;             if(q[mid] >= x)  r = mid;             else l = mid + 1;         }         if(q[l] != x)  cout << "-1 -1" << endl;            else         {             cout << l << " ";                              int l = 0, r = n - 1;             while(l < r)             {                 int mid = l + r + 1 >> 1;                 if(q[mid] <= x) l = mid;                 else r = mid - 1;             }             cout << l << endl;         }     }     return 0; }
   | 
 

例题:给定一个浮点数n,求它的三次方根。结果保留6位小数。
数据范围:$-10000 \le n \le 10000$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | #include<cstdio>
  int main() {     double x;     scanf("%lf", &x);               double l = -100, r = 100;     while(r - l > 1e-8)       {         double mid = (l + r) / 2;         if(mid * mid * mid >= x) r = mid;         else l = mid;     }          printf("%.6lf\n", l);     return 0; }
   |