问题描述:给定一个整数数组,按照升序排序,使用快速排序、归并排序、堆排序或任何时间代价O(nlogn)的算法。
Given an integer array, sort it in ascending order in place. Use quick sort, merge sort, heap sort or any O(nlogn) algorithm.
参考:殷建平、徐云、王刚、刘晓光、苏明、邹恒明、王宏志译,Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein著,《算法导论》原书第3版,第2、6、7章。
思路:
(一)归并排序
采用分而治之的策略,将原数组等分为两个小数组,分别对小数组排好序,然后归并(组合)成升序排列的大数组。归并过程中,额外存放了两个小数组,选取两个数组相比之下,当前最小的单元,存入原数组的相应位置。
代码如下:
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
void sortIntegers2(vector<int> &A) {
// write your code here
mergeSortIntegers(A,0,A.size()-1);
}
void mergeSortIntegers(vector<int> &A, int p, int r) {
if (p<r) {
int q = (p+r)/2;
mergeSortIntegers(A,p,q);
mergeSortIntegers(A,q+1,r);
merge(A,p,q,r);
}
}
void merge(vector<int> &A, int p, int q, int r) {
int n1 = q-p+1;
int n2 = r-q;
vector<int> L, R;
int i;
for (i = 0; i < n1; i++) {
L.push_back(A[p+i]);
}
for (i = 0; i < n2; i++) {
R.push_back(A[q+1+i]);
}
L.push_back(INT_MAX);
R.push_back(INT_MAX);
int j = 0, k;
i = 0;
for (k = p; k <= r; k++) {
if (L[i] < R[j]) {
A[k] = L[i];
i++;
} else {
A[k] = R[j];
j++;
}
}
}
};
(二)堆排序
思路:根据原数组创建最大堆(完全二叉树,父节点取值必定大于子节点的取值),数组元素个数为n。将堆的根节点与原数组的最后位置的元素互换,此时数组最后位置存放了整个数组中的最大值(根据最大堆的性质);最大堆元素个数减1(即为n-1),由于根节点被置换,可能不满足最大堆的性质,需重新处理下,使包含n-1个元素的数组符合最大堆性质。依此,逐渐置换最大堆根节点到数组中的合适位置。
代码如下:
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
void sortIntegers2(vector<int> &A) {
// write your code here
heapSort(A);
}
int heapSize = 0; // global variable
void heapSort(vector<int> &A) {
//global int
heapSize = A.size();
buildMaxHeap(A);
for (int i = A.size()-1; i >= 1; i--) {
// put current heap's root node(0) to the back of the sorted vector(i)
// heap size - 1
int t = A[i];
A[i] = A[0];
A[0] = t;
heapSize--;
maxHeapify(A,0);
}
}
void buildMaxHeap(vector<int> &A) {
// build max heap bottom up
// Max heap has heapSize/2 inner nodes.
for (int i = heapSize/2-1; i >= 0; i-- ) {
maxHeapify(A,i);
}
}
void maxHeapify(vector<int> &A, int i) {
// compare current node with its children.
int l = LEFT(i);
int r = RIGHT(i);
int largest = i;
if (l <= heapSize-1 && A[l] > A[i]) largest = l; //index-1
if (r <= heapSize-1 && A[r] > A[largest]) largest = r; //compare with largest
if (largest != i) {
int t = A[largest];
A[largest] = A[i];
A[i] = t;
maxHeapify(A,largest);
}
}
int LEFT(int i) {
// left child
return 2*i+1;
}
int RIGHT(int i) {
// right child
return 2*(i+1);
}
};
(三)快速排序
思路:采用分而治之的策略。需要对数组作切分,比如目标是排序数组A从下标p到下标r位置的元素,那需要找到一个中间位置q,满足A[q]大于所有A[p,q-1]的元素,并且A[q]小于所有A[q+1,r]的元素。分别对两个子数组排序。子数组排序还是递归进行。难点在找到合适的分割点,从待排序数组中选择一个元素x(可任意选择),遍历所有数组中的元素,如果小于x,则安置在数组的前面[p,q-1]范围内,从下标p开始递增安置。最后将小于x的子数组的下一个位置,安排成元素x(将x从原位置调换过来)。
代码如下:
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
int heapSize = 0; // global variable
void sortIntegers2(vector<int> &A) {
// write your code here
quickSort(A,0,A.size()-1);
}
void quickSort(vector<int> &A,int p, int r) {
if (p<r) {
int q = partition(A,p,r);
quickSort(A,p,q-1);
quickSort(A,q+1,r);
}
}
int partition(vector<int> &A, int p, int r) {
int x = A[r];
int i = p-1;
int t;
for (int j = p; j <= r-1; j++) {
if (A[j]<x) {
i++;
t = A[j];
A[j] = A[i];
A[i] = t;
}
}
t = A[i+1];
A[i+1] = x;
A[r] = t;
return i+1;
}
};