2016 组成原理 快速排序快速排序的单向递归算法 解答题
第 43 题

已知由n(n≥2)个正整数构成的集合A=ak∣0≤k<n,将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2,A1和A2中元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足∣n1−n2∣最小且∣S1−S2∣最大。要求:

(1) 给出算法的基本设计思想。

(2) 根据设计思想,采用 C 或 C++ 语言描述算法,关键之处给出注释。

(3) 说明你所设计算法的时间复杂度和空间复杂度。

快速排序

1)算法的基本设计思想由题意知,将最小的⌊n/2⌋个元素放在A1中,其余的元素放在A2中,分组结果即可满足题目要求。仿照快速排序的思想,基于枢轴将个整数划分为两个子集。根据划分后枢轴所处的位置i分别处理:

  • 若i=⌊n/2⌋,则分组完成,算法结束;
  • 若i<⌊n/2⌋,则枢轴及之前的所有元素均属于A1,继续对 i 之后的元素进行划分;
  • 若i>⌊n/2⌋,则枢轴及之后的所有元素均属于A2,继续对i之前的元素进行划分;基于该设计思想实现的算法,无须对全部元素进行全排序,其平均时间复杂度是O(n),空间复杂度是O(1)。

2)算法实现实现

[tag_link]

参考 快速排序的单向递归算法

int partition(int a[], int low, int high) {
  int l = low;
  int r = high;
  int pivot = a[l];
  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;
  return l;
}

// 按照第 k 个元素进行分区
void quickSelect(int a[], int low, int high, int k) {
  if (low < high) {
    int pivotIndex = partition(a, low, high);
    if (pivotIndex == k) {
      return
    } else if (pivotIndex < k) {
      quickSelect(a, pivotIndex+1, high, k);
    } else {
      quickSelect(a, low, pivotIndex-1, k);
    }
  }
}

// 空间复杂度:O(1)
// 时间复杂度:O(n)
void solve(int a[], int n) {
  quickSelect(a, 0, n-1, n/2);
  int S1 = 0;
  int S2 = 0;
  for (int i = 0; i < n/2; i++) {
    S1 += a[i];
  }
  for (int i = n/2; i < n; i++) {
    S2 += a[i];
  }
  return S2 - S1;
}

3)本参考答案给出的算法平均时间复杂度是O(n), 空间复杂度是O(1)。