插入排序
插入排序的工作方式像许多人排列一手扑克牌。开始时,我们的左手为空且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中的正确位置。为了找到一张牌的正确位置,我们从右到左将它与已经在手上的每张牌进行比较,如图所示。
代码实现
template<class T>
void InsertionSort(T a[], int nums) {
for (int i = 1; i < nums; i++) {
T key = a[i];
int j = i - 1;
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
}
分治法
许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题。这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再结合这些子问题的解建立原问题的解。
分治模式在每层递归时都有三个步骤:
- 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
- 解决这些子问题,递归地求解各子问题。然后,若子问题的规模足够小,则直接求解。
- 合并这些子问题的解成原问题的解。
归并排序算法完全遵循分治模式。直观上其操作如下:
- 分解:将待排序的具有 n 个元素的序列分解成各具 n/2 个元素的两个子序列。
- 解决:使用归并排序递归地排列两个子序列。
- 合并:合并两个已排序的子序列。
当待排序的序列长度为 1 时,递归“开始回升”,在这种情况下不做任何工作,因为长度为 1 的每个序列都已排好序。
代码实现
template<class T>
void Merge(T a[], int p, int q, int r) {
int n1 = q - p + 1, n2 = r - q;
T *left = new T[n1 + 1], *right = new T[n2 + 1];
for (int i = 0; i < n1; i++) {
left[i] = a[p + i];
}
left[n1] = max;
for (int i = 0; i < n2; i++) {
right[i] = a[q + i + 1];
}
right[n2] = max;
int i = 0, j = 0;
for (int k = p; k <= r; k++) {
if (left[i] < right[j]) {
a[k] = left[i];
i++;
}
else {
a[k] = right[j];
j++;
}
}
}
template<class T>
void MergeSort(T a[], int p, int r) {
if (p < r) {
int q = (p + r) / 2;
MergeSort(a, p, q);
MergeSort(a, q + 1, r);
Merge(a, p, q, r);
}
}