划分规则: 以某个元素为标准, 把顺序表中的元素分为左右两个部分, 标准元素称为枢轴.
考研中划分有三种题型(划分策略).
题型一
要求: 给一个顺序表, 以第一个元素为枢轴, 将该顺序表划分为左右两部分, 使得左边的所有元素都小于枢轴, 右边的所有元素都大于枢轴. 并且枢轴要夹在左右两部分元素之间.
图1. 以第一个元素为枢轴进行划分
如图, 以第一个元素2作为枢轴, 用temp存储枢轴的值. i 和 j 分别指向顺序表中的第一个和最后一个元素.
先比较arr[ j ]与temp的大小, 若 arr[ j ] > temp; 则说明arr[ j ] 大于枢轴且已经在枢轴的右侧了, 因此无需处理. 然后 j 左移, 即 j -- ; 当 arr[ j ] > temp时, 一直循环执行 j -- ;若 arr[ j ] < temp; 则说明arr[ j ] 小于枢轴但是却在枢轴的右侧, 需要将arr[ j ] 的元素放在枢轴的左侧, 我们之间将arr[ j ] 的值赋值给arr[ i ], 因为arr[ i ] 的值已经存储在temp中了, 因此可以直接覆盖.
然后 i 指向下一个元素, 即 i ++; 再比较arr[ i ]与temp的大小, 若 arr[ i ] < temp; 则说明arr[ i ] 小于枢轴且已经在枢轴的左侧了, 因此无需处理. 然后 i 左移, 即 j ++ ; 当 arr[ i ] < temp时, 一直循环执行 i ++ ; 若 arr[ i ] > temp; 则说明arr[ i ] 大于枢轴但是却在枢轴的左侧, 需要将arr[ i ] 的元素放在枢轴的右侧, 我们直接将 arr[ i ] 的值赋值给arr[ j ], 因为arr[ j ] 的值之前已经赋值给彼时的arr[ i ] 了, 因此可以直接覆盖.
就这样, 若 arr[ j ] > temp; 则 j 一直左移, 直到 arr[ j ] < temp, 则 arr[ i ] = arr[ j ]; i 自增1, 然后比较arr[ i ]与temp的大小, 若arr[ i ] < temp; 则 i 一直右移, 直到 arr[ i ] > temp, 则 arr[ j ] = arr[ i ], j 自减1; 如此循环往复, 直到 i == j ,则划分结束, 此时 i 与 j 所指的位置即为枢轴元素的最终位置, 则 arr[ i ] = temp; 此时得到的表即为划分之后的表, 左边的所有元素都小于枢轴, 右边的所有元素都大于枢轴. 并且枢轴夹在左右两部分元素之间. 需要注意的是, i 和 j, 每自增或自减一次, 都要比较一下 i 和 j 的大小, 当 i < j 时才继续执行, 否则跳出归并代码.
#include <iostream>
const int MAX_SIZE = 10;
/// <summary>
/// 划分
/// </summary>
/// <param name="arr">数组</param>
/// <param name="length">数组长度</param>
void partition(int* arr, int length) {
if (length <= 0) {
return;
}
int i = 0;
int j = length - 1;
int temp = arr[0];
while (i < j) {
while (i < j && arr[j] >= temp)
{
j--;
}
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && arr[i] < temp)
{
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
arr[i] = temp;
}
int main()
{
int arr[MAX_SIZE] = { 2, 1, -7, -3, 5, 6, -1 };
int length = 7;
partition(arr, length);
printf("归并后的数组:\n");
for (int i = 0; i < length; i++)
{
printf("%d ", arr[i]);
}
}
代码1: 以第一个元素为枢轴进行划分
分析一下为什么在while循环内部每操作一次之前都要比较 i 和 j 的大小. 先看第一个while循环, 这里我们需要比较arr[ j ] 和temp的大小, 进而完成 j 的自减操作, 当 j 自减到 j == i 时, 说明 j 所指位置为枢轴位置, 因此需要跳出循环, 故 i < j 时才执行循环, 第二个while循环同理. 再看第一个if()语句, 跳出了第一个while循环, 说明 i == j 或者arr[ j ] < temp, 如果是i == j 导致的跳出循环, 那么就说明归并结束了, 就不再需要赋值了, 因此需要i < j时才执行if()语句; 如果时arr[ j ] < temp导致的跳出循环, 则需要赋值, 但此时i < j成立, 因此执行if()语句需要i < j成立. 第二个if()语句成立.
题型二
要求: 以任意一个值作为比较的标准, 设该值为Y, 执行完之后整个顺序表被划分为两部分, 左边都小于Y, 右边都大于等于Y.
图2. 以任意一个值为枢轴进行划分
用temp存储数组首个元素, comp存储要比较的元素, 这里comp只参与比较, 并不会被放入数组. 最后的效果是把数组元素以Y为界限分成了前后两部分, 前半部分小于Y, 后半部分大于等于Y. i 和 j 最终指向的值是首个元素的最终位置, 而不是Y的最终位置.
下图为同一个数组以不同的值为枢轴进行划分的结果.
图3. 同一个数组以不同的值为枢轴进行划分的结果
#include <iostream>
const int MAX_SIZE = 10;
/// <summary>
/// 划分
/// </summary>
/// <param name="arr">数组</param>
/// <param name="length">数组长度</param>
/// <param name="comp">进行划分的值</param>
void partition(int* arr, int length, int comp) {
if (length <= 0) {
return;
}
int i = 0;
int j = length - 1;
int temp = arr[0];
while (i < j) {
while (i < j && arr[j] >= comp)
{
j--;
}
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && arr[i] < comp)
{
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
arr[i] = temp;
}
int main()
{
int arr[MAX_SIZE] = { 2, 1, -7, -3, 5, 6, -1 };
int length = 7;
partition(arr, length, 0);
printf("归并后的数组:\n");
for (int i = 0; i < length; i++)
{
printf("%d ", arr[i]);
}
}
代码2: 以任意一个值为枢轴进行划分
题型三
要求: 以顺序表中任何一个位置上的元素为枢轴, 把顺序表划分为两个部分, 左边都小于枢轴, 右边都大于等于枢轴.
思路: 将想要作为枢轴的元素交换到第一个位置上, 再按照题型一的方法解决.
#include <iostream>
const int MAX_SIZE = 10;
/// <summary>
/// 划分
/// </summary>
/// <param name="arr">数组</param>
/// <param name="length">数组长度</param>
/// <param name="k">取k位置上的元素为枢轴</param>
void partition(int* arr, int length, int k) {
if (length <= 0) {
return;
}
if (k > length - 1 || k < 0) {
return;
}
int i = 0;
int j = length - 1;
int temp = arr[0];
arr[0] = arr[k];
arr[k] = temp;
temp = arr[0];
while (i < j) {
while (i < j && arr[j] >= temp)
{
j--;
}
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && arr[i] < temp)
{
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
arr[i] = temp;
}
int main()
{
int arr[MAX_SIZE] = { 2, 1, -7, -3, 5, 6, -1 };
int length = 7;
partition(arr, length, 1);
printf("归并后的数组:\n");
for (int i = 0; i < length; i++)
{
printf("%d ", arr[i]);
}
}
代码3: 以顺序表中任意一个值为枢轴进行划分