zoukankan      html  css  js  c++  java
  • HDU-1040-As Easy As A+B(各种排序)

    • 希尔排序
      Accepted 1040 0MS 1224K 564 B G++
      #include "cstdio"
      using namespace std;
      int arr[5000];
      int main() {
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              for (int i = 0; i < n; i++) {
                  scanf("%d", &arr[i]);
              }
              // 一开始将增量设置为n的一半,将n个数分成n/2份进行插排,每份大概只有两个数,每次增量减半 
              for (int i = n >> 1; i > 0; i >>= 1) {
                  for (int j = i; j < n; j++) {
                      int u = arr[j];
                      int id = j - i;
                      while (id >= 0 && arr[id] > u) {
                          arr[id + i] = arr[id];
                           id -= i;    
                      }
                      arr[id + i] = u;
                  }
              }
              printf("%d", arr[0]);
              for (int i = 1; i < n; i++) {
                  printf(" %d", arr[i]);
              }
              puts("");
          }
          return 0;
      }

      希尔排序是优化版的插入排序。

    • 快速排序
      Accepted 1040 0MS 1372K 1736 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      int arr[1005];
      void quickSort(int l, int r) {
          /*
          注意一种错误写法:if (l == r)
          在排序9 8这样的数据时会造成无限循环
          */
          if (l >= r) {
              return;
          }
          int mid = l + r >> 1, id;
          // 找arr[l]、arr[r]、arr[mid]三个点中的中位数
          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 le = l + 1, gt = r;
          // 在下面的循环过程中,arr[l + 1]到arr[le]小于等于arr[l],arr[gt]到arr[r]大于arr[l];
          while (le < gt) {
              if (arr[le] <= arr[l]) {
                  le++;
              } else if (arr[gt] <= arr[l]){
                  swap(arr[le], arr[gt]);
                  le++;
                  gt--;
              } else {
                  while (arr[gt] > arr[l] && le < gt) {
                      gt--;
                  }
              }
          }
          if (arr[le] > arr[l]) {
              le--;
          }
          swap(arr[le], arr[l]);
          /*
          注意一个错误写法:quickSort(l, le);
          在排序如4 0 2 4这样的数据时会造成无限循环
          */
          quickSort(l, le - 1);
          quickSort(le + 1, r);
      }
      int main() {
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              for (int i = 1; i <= n; i++) {
                  scanf("%d", &arr[i]);
              }
              quickSort(1, n);
              for (int i = 1; i < n; i++) {
                  printf("%d ", arr[i]);
              }
              printf("%d
      ", arr[n]);
          }
          return 0;
      }

      这是最常用的一种排序方式,Java里面的Array.sort用的就是这种方式进行排序的,还有就是之前一直说这种排序方式的复杂度为nlogn,但是要注意nlgn是最好情况。一般达不到的,在排序个数小于等于8时用n^2复杂度的插入排序反而更快,所以quickSort函数里还可以进行一个判断if (r - l + 1 <= 8){插排}来进行优化。

    • 插入排序
      Accepted 1040 0MS 1380K 705 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      int arr[1005];
      int main() {
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              for (int i = 0; i < n; i++) {
                  scanf("%d", &arr[i]);
              }
              for (int i = 1; i < n; i++) {
                  int temp = arr[i], j = i - 1;
                  while (j >= 0 && arr[j] > temp) {
                      arr[j + 1] = arr[j];
                      j--;
                  }
                  arr[j + 1] = temp;
              }
              printf("%d", arr[0]);
              for (int i = 1; i < n; i++) {
                  printf(" %d", arr[i]);
              }
              puts("");
          }
          return 0;
      }

      就是让arr[0]到arr[i - 1]保持有序之后把arr[i]插入到前面的有序数列中去从而使arr[0]到arr[i]有序,逆序转正序的情况下复杂度最高,算法复杂度为O(N^2);

    • 归并排序
      Accepted 1040 0MS 1376K 1270B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      int arr[1005], help[1005];
      void mergeSort(int l, int r) {
          if (l >= r) {
              return;
          }
          int mid = l + r >> 1;
          // 递归排序l到mid和mid + 1到r
          mergeSort(l, mid);
          mergeSort(mid + 1, r);
          // 将l到r拷贝到help数组对应位置,此时可将help[l]到help[mid]看成一个数组,help[mid + 1]到help[r]看成另一个数组
          memmove(help + l, arr + l, sizeof(int) * (r - l + 1));
          int L = l, R = mid + 1, id = l;
          // 每次取出help[L]和help[R]中值小的填入arr[id]直到一边被取完为止
          while (L <= mid && R <= r) {
              if (help[L] <= help[R]) {
                  arr[id++] = help[L++];
              } else {
                  arr[id++] = help[R++];
              }
          }
          // 如果被取完的是help[l],那么help[R]后面的数已经存在于arr中对应位置不用移动,否则将help[L]后面的数移到arr数组后面;
          while (L <= mid) {
              arr[id++] = help[L++];
          }
      }
      int main() {
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              for (int i = 1; i <= n; i++) {
                  scanf("%d", &arr[i]);
              }
              mergeSort(1, n);
              for (int i = 1; i < n; i++) {
                  printf("%d ", arr[i]);
              }
              printf("%d
      ", arr[n]);
          }
          return 0;
      }

      和快速排序一样是O(nlogn)的排序方法,这个方法需要借助辅助空间。

    • 堆排序
      Accepted 1040 0MS 1372K 1117 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      typedef pair<int, int> PII;
      const int INF = 0x3f3f3f3f;
      int arr[1005], m;
      void maxHeapFix(int id) {
          // mx存左右儿子中比较大的,一开始假设左儿子比右儿子大
          int mx = id << 1;
          // 如果左儿子越界,右儿子肯定越界,id为叶子节点,直接返回
          if (mx > m) {
              return;
          }
          // 否则如果右儿子没越界并且右儿子大于左儿子,则mx = 右儿子
          if (mx < m && arr[mx | 1] > arr[mx]) {
              mx |= 1;
          }
          if (arr[mx] > arr[id]) {
              swap(arr[mx], arr[id]);
              maxHeapFix(mx);
          }
      }
      int main() {
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              for (int i = 1; i <= n; i++) {
                  scanf("%d", &arr[i]);
              }
              m = n;
              for (int i = n >> 1; i; i--) {
                  maxHeapFix(i);
              }
              while (m != 1) {
                  swap(arr[1], arr[m--]);
                  maxHeapFix(1);
              }
              for (int i = 1; i < n; i++) {
                  printf("%d ", arr[i]);
              }
              printf("%d
      ", arr[n]);
          }
          return 0;
      }

      就是不断维护大顶堆,大顶堆的堆顶是堆中最大,和最后一个元素交换后将堆的大小减1后继续维护大顶堆,复杂度O(nlgn)

    • 基数排序
      Accepted 1040 0MS 1416K 1160 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      // 选取基数,不一定非要选10
      const int BASE = 10;
      vector<int> arr;
      // 因为涉及负数排序,bucket开基数的两倍,前一半用来存负数
      vector<int> bucket[BASE << 1];
      int main() {
          int t, n , m;
          scanf("%d", &t);
          while (t--) {
              arr.clear();
              scanf("%d", &n);
              for (int i = 0; i < n; i++) {
                  scanf("%d", &m);
                  arr.push_back(m);
              }
              // 因为int中绝对值最大为 1LL << 31, 所以上限设为1LL << 31
              for (LL bit = 1; bit <= 1LL << 31; bit *= BASE) {
                  for (int i : arr) {
                      // 这里可以想象成进制转换,将i转换成BASE进制后求某一位
                      bucket[i / bit % BASE + BASE].push_back(i);
                  }
                  arr.clear();
                  for (int i = 1; i < BASE << 1; i++) {
                      arr.insert(arr.end(), bucket[i].begin(), bucket[i].end());
                      bucket[i].clear();
                  }
              }
              printf("%d", arr[0]);
              for (int i = 1; i < n; i++) {
                  printf(" %d", arr[i]);
              }
              puts("");
          }
          return 0;
      }

      基数排序原理有点像字典序,从右边往左一遍遍扫描,高位补0。其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数。

    • 桶排序
      Accepted 1040 0MS 1412K 1142 B G++
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int INF = 0x3f3f3f3f;
      int t, n, m;
      vector<int> bucket[1005];
      int getHash(LL m) {
          // 减INT_MIN的作用是保证m为非负数,减完后的最大值为(1LL << 32) - 1,为保证哈希值在[0,n)的范围内除1LL << 32
          return (m - INT_MIN) * n / (1LL << 32);
      }
      void insert(int id, int m) {
          int j = bucket[id].size();
          bucket[id].push_back(m);
          while (j > 0 && bucket[id][j - 1] > m) {
              bucket[id][j] = bucket[id][j - 1];
              j--;
          }
          bucket[id][j] = m;
      }
      int main() {
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              for (int i = 0; i < n; i++) {
                  scanf("%d", &m);
                  insert(getHash(m), m);
              }
              bool flag = true;
              for (int i = 0; i < n; i++) {
                  for (auto j : bucket[i]) {
                      if (flag) {
                          printf("%d", j);
                          flag = false;
                      } else {
                          printf(" %d", j);
                      }
                  }
                  bucket[i].clear();
              }
              puts("");
          }
          return 0;
      }

      桶排序的适用范围是数据平均分布的情况,这种情况下复杂度很低可达到O(N),所以考虑在一个桶内的数不会太多,用插入排序。但是分布极不均匀的情况下(所有数据在一个桶中)将退化成插入排序;

  • 相关阅读:
    Java
    Java
    Java
    Java
    运算问题
    Idea常用快捷键
    java变量和变量命名规范
    java常用数据类型和基本数据类型转换和进制和大数运算
    java注释和标识符规范
    使用命令行生成的第一个java程序
  • 原文地址:https://www.cnblogs.com/Angel-Demon/p/10272864.html
Copyright © 2011-2022 走看看