zoukankan      html  css  js  c++  java
  • HDU-1425-sort(计数排序以及快速排序和堆排序的变种)

    • 计数排序
      Accepted 1425 483MS 5276K 997 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      typedef pair<int, int> PII;
      const int INF = 0x3f3f3f3f;
      const int MAXN = 1e6 + 5;
      const int MIN_NUM = -5e5;
      const int MAX_NUM = 5e5;
      // MAX_NUM - MIN_NUM 表示输入数字的范围
      int num[MAX_NUM - MIN_NUM + 10];
      int main() {
          int n, m, k;
          while (~scanf("%d%d", &n, &m)) {
              while (n--) {
                  scanf("%d", &k);
                  // 因为存在负数而没有负下标,所以将所有输入的数减去MIN_NUM然后再存入对应下标
                  num[k - MIN_NUM]++;
              }
              k = MAX_NUM - MIN_NUM;
              while (m--) {
                  while (num[k] == 0) {
                      k--;
                  }
                  num[k]--;
                  if (m != 0) {
                      printf("%d ", k + MIN_NUM);
                  } else {
                      printf("%d
      ", k + MIN_NUM);
                  }
              }
              // k后面的都已经清空了所以,这里只要清空k前面这段就好了;
              memset(num, 0, sizeof(int) * (k + 1));
          }
          return 0;
      }

      计数排序适用于排序范围已知且排序数量大,排序范围小的情况。复杂度为Ο(n+k)。n表示排序数量,k表示排序范围;计数排序还有个优点在于不用记录原数,所有只要排序范围比较小是比较省空间的;

    • 堆排序变种
      Accepted 1425 499MS 2156K 1290 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      typedef pair<int, int> PII;
      const int INF = 0x3f3f3f3f;
      const int MAXN = 1e6 + 5;
      int arr[MAXN];
      int n, m, k;
      void minHeapFix (int id) {
          int mn = id << 1;
          if (mn > m) {
              return;
          }
          if (mn < m && arr[mn | 1] < arr[mn]) {
              mn |= 1;
          }
          if (arr[mn] < arr[id]) {
              swap(arr[mn], arr[id]);
              minHeapFix(mn);
          }
      }
      int main() {
          while (~scanf("%d%d", &n, &m)) {
              // 因为题目要求前m大,所以这里的堆只维护前m大的数
              for (int i = 1; i <= m; i++) {
                  scanf("%d", &arr[i]);
              }
              for (int i = m >> 1; i; i--) {
                  minHeapFix(i);
              }
              for (int i = m + 1; i <= n; i++) {
                  scanf("%d", &k);
                  // 这里维护的是小顶堆,堆顶最小,如果k比前m大中最小的数要大,用k替换堆顶,再维护小顶堆
                  if (k > arr[1]) {
                      arr[1] = k;
                      minHeapFix(1);
                  }
              }
              k = m;
              for (int i = 1; i < k; i++) {
                  swap(arr[1], arr[m--]);
                  minHeapFix(1);
              }
              for (int i = 1; i < k; i++) {
                  printf("%d ", arr[i]);
              }
              printf("%d
      ", arr[k]);
          }
          return 0;
      }

      这题用堆排好在于他不用维护n个数只要维护前m大的数就够了,但是题目中说(0<n,m<1000000)可见m也不小了,所以比计排还要慢些,如果m比较小还是很快的。

    • 快速排序变种
      Accepted 1425 468MS 3332K 1460 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      const int MAXN = 1e6 + 5;
      int arr[MAXN];
      int n, m;
      void quickSort(int l, int r) {
          if (l >= r) {
              return;
          }
          int mid = l + r >> 1, id;
          if (arr[l] <= arr[mid] && arr[l] <= arr[r]) {
              id = arr[mid] <= arr[r] ? mid : r;
          } else {
              if (arr[mid] <= arr[r]) {
                  id = arr[l] <= arr[r] ? l : r;
              } else {
                  id = arr[l] <= arr[mid] ? l : mid;
              }
          }
          swap(arr[l], arr[id]);
          int ge = l + 1, lt = r;
          while (ge < lt) {
              if (arr[ge] >= arr[l]) {
                  ge++;
              } else if (arr[lt] >= arr[l]){
                  swap(arr[ge], arr[lt]);
                  ge++;
                  lt--;
              } else {
                  while (arr[lt] < arr[l] && ge < lt) {
                      lt--;
                  }
              }
          }
          if (arr[ge] < arr[l]) {
              ge--;
          }
          swap(arr[ge], arr[l]);
          quickSort(l, ge - 1);
          // 变种就变在这个if, 因为只要前m大的数有序,所以当ge >= m时就不用排后面的数了
          if (ge < m) {
              quickSort(ge + 1, r);
          }
      }
      int main() {
          while (~scanf("%d%d", &n, &m)) {
              for (int i = 1; i <= n; i++) {
                  scanf("%d", &arr[i]);
              }
              quickSort(1, n);
              for (int i = 1; i < m; i++) {
                  printf("%d ", arr[i]);
              }
              printf("%d
      ", arr[m]);
          }
          return 0;
      }
    • 快速排序再变种
      Accepted 1425 468MS 3332K 1264 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      const int MAXN = 1e6 + 5;
      int arr[MAXN];
      int n, m;
      void quickSort(int l, int r, int mn, int mx) {
          if (l >= r || mn >= mx) {
              return;
          }
          // 取排序范围的中值作为mid
          int mid = mn + mx >> 1;
          /* 
          下面的if很重要,原先没加导致
          5 5
          1 2 3 4 5
          这组数据死循环。
          */
          if (mid == mn) {
              mid++;
          }
          int ge = l, lt = r;
          while (ge < lt) {
              if (arr[ge] >= mid) {
                  ge++;
              } else if (arr[lt] >= mid){
                  swap(arr[ge], arr[lt]);
                  ge++;
                  lt--;
              } else {
                  while (arr[lt] < mid && ge < lt) {
                      lt--;
                  }
              }
          }
          if (arr[ge] < mid) {
              ge--;
          }
          quickSort(l, ge, mid, mx);
          if (ge < m) {
              quickSort(ge + 1, r, mn, mid - 1);
          }
      }
      int main() {
          while (~scanf("%d%d", &n, &m)) {
              for (int i = 1; i <= n; i++) {
                  scanf("%d", &arr[i]);
              }
              quickSort(1, n, -5e5, 5e5);
              for (int i = 1; i < m; i++) {
                  printf("%d ", arr[i]);
              }
              printf("%d
      ", arr[m]);
          }
          return 0;
      }

      一般的快排都会采用三点取中法来找mid,但是这个mid并不是数组真正的中位数,所以这里采用数据范围的中值作为mid。本来以为会快一点的,但是好像和上面的快排差不多,看样子三点取中法还是很靠谱的。另外写二分一定注意死循环,快被二分死循环搞疯了。

  • 相关阅读:
    第十三节:实际开发中使用最多的监视锁Monitor、lock语法糖的扩展、混合锁的使用(ManualResetEvent、SemaphoreSlim、ReaderWriterLockSlim)
    第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)
    第十一节:深究用户模式锁的使用场景(异变结构、互锁、旋转锁)
    第十节:利用async和await简化异步编程模式的几种写法
    第九节:深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)
    C# DataTable列名不区分大小写
    如何很好的使用Linq的Distinct方法
    Java读写记事本文件
    c# 获取方法所在的命名空间 类名 方法名
    C#中类的序列化和反序列化
  • 原文地址:https://www.cnblogs.com/Angel-Demon/p/10297768.html
Copyright © 2011-2022 走看看