- Sort Integers
中文English
Given an integer array, sort it in ascending order. Use selection sort, bubble sort, insertion sort or any O(n2) algorithm.
Example
Example 1:
Input: [3, 2, 1, 4, 5]
Output: [1, 2, 3, 4, 5]
Explanation:
return the sorted array.
Example 2:
Input: [1, 1, 2, 1, 1]
Output: [1, 1, 1, 1, 2]
Explanation:
return the sorted array.
解法1:
QuickSort经典模板。
注意:
- pivot是取值而不是取index,因为QuickSort过程中元素会交换,index会变。
- quickSort()刚进来要备份left=start, right=end,因为start和end会变。
- 所有比较left和right的地方都必须用<=,所有比较A[left]/A[right]和pivot的地方都必须用<。否则可能溢出,因为递归的时候start和end老是不变。
代码如下。 - quickSort()里面一开始if(start>=end) return这里可以用>=也可以用>。但MergeSort()这个地方只能用>=。所以为了统一,这里都用>=。
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
void sortIntegers(vector<int> &A) {
int n = A.size();
if (n == 0) return;
quickSort(A, 0, n - 1);
}
private:
void quickSort(vector<int> &A, int start, int end) {
if (start >= end) return; //这里也可以用>
int left = start, right = end;
int pivot = A[left + (right - left) / 2];
while(left <= right) {
while(left <= right && A[left] < pivot) { // 注意这里要写pivot,而不是A[left + (right - left) / 2]; 因为A[left + (right - left) / 2]值可能会变!!!
left++;
}
while(left <= right && A[right] > pivot) { // 注意这里要写pivot,而不是A[left + (right - left) / 2]; 因为A[left + (right - left) / 2]值可能会变!!!
right--;
}
if (left <= right) {
swap(A[left], A[right]);
left++;
right--;
}
}
quickSort(A, start, right);
quickSort(A, left, end);
}
};
另一个quickSort的解法,是把结尾元素当成pivot,每次quickSort执行后,start…i-1之间是小于pivot的,i等于pivot,i+1…end是大于pivot的。有点类似选择排序,不同点是选择排序每次是在i+1到end间选一个最小值跟a[i]交换,而这里是在i+1到end间找到第一个大于pivot的值就跟a[i]交换。
class Solution {
public:
/**
* @param a: an integer array
* @return: nothing
*/
void sortIntegers(vector<int> &a) {
int n = a.size();
quickSort(a, 0, n - 1);
}
private:
void quickSort(vector<int> &a, int start, int end) {
if (start >= end) return;
int i = start;
for (int j = start; j < end; j++) {
if (a[j] < a[end]) {
swap(a[i], a[j]);
i++;
}
}
swap(a[i], a[end]);
quickSort(a, start, i - 1);
quickSort(a, i + 1, end);
}
};
解法2:MergeSort经典模板。
注意:
- mergeSort()刚进去的地方if(start>=end) continue;这里只能用>=, 和quickSort()不同。
- mergeSort() 外面一定要开辟一个n长度的数组,这是mergeSort比quickSort费事的地方,因为开辟一个区间(之后还要回收)很费时间。
- mergeSort()进去了start和end都要备份。记得这里right=mid+1 (quickSort是right=end)。
- 所有的比较都用<=。
- merge()里面最后记得把buf[]的start…end元素拷贝到A[]里面(不能直接A=buf)。
- mid是取index(不是取值!)。
- 简而言之,mergeSort的代码结构是while-if-while-while-for,而quickSort是while-{while-while}-if。
代码如下:
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
void sortIntegers(vector<int> &A) {
int n = A.size();
if (n == 0) return;
vector<int> temp(n, 0);
mergeSort(A, 0, n - 1, temp);
}
private:
void mergeSort(vector<int> &A, int start, int end, vector<int> &buf) {
if (start >= end) return;//这里只能用>=
int mid = start + (end - start) / 2;
mergeSort(A, start, mid, buf);
mergeSort(A, mid + 1, end, buf);
merge(A, start, end, buf);
}
void merge(vector<int> &A, int start, int end, vector<int> &buf) {
int mid = start + (end - start) / 2;
int left = start;
int right = mid + 1;
int index = left;
while(left <= mid && right <= end) {
if (A[left] <= A[right]) {
buf[index++] = A[left++];
} else {
buf[index++] = A[right++];
}
}
while(left <= mid) {
buf[index++] = A[left++];
}
while(right <= end) {
buf[index++] = A[right++];
}
//remember to copy buf back to A
//cannot directly use A = buf!
for (int i = start; i <= end; ++i) {
A[i] = buf[i];
}
}
};
下面是其它一些排序算法的代码:
- 冒泡排序
void bubble_sort(int arr[], int len) {
int i, j, temp;
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
- 插入排序
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
void sortIntegers(vector<int> &A) {
int sizeA = A.size();
if (sizeA == 0) return;
insertionSort(A);
return;
}
private:
void insertionSort(vector<int> &A) {
int len = A.size();
for (int i = 1; i < len; i++) {
int j = i;
while (j > 0) {
if (A[j] < A[j - 1]) {
int tmp = A[j];
A[j] = A[j - 1];
A[j - 1] = tmp;
j--;
} else {
break;
}
}
}
}
};
注意:上面的版本是错的!!!这个版本实际上是冒泡排序。插入排序不需要swap。
新版本如下:
class Solution {
public:
/**
* @param a: an integer array
* @return: nothing
*/
void sortIntegers(vector<int> &a) {
//insertion sort
int len = a.size();
for (int i = 0; i < len; i++) {
int value = a[i];
int pos = 0;
for (int j = i - 1; j >= 0; j--) {
if (a[j] > value) {
a[j + 1] = a[j];
} else {
pos = j + 1;
break;
}
}
a[pos] = value;
}
}
};
注意,冒泡排序和插入排序时间复杂度都是O(n^2),为什么还是插入排序更受欢迎呢?因为冒泡排序需要swap,所以每次交换有至少3个赋值操作。而插入排序每次只有a[j+1]=a[j]这一个赋值操作。
- 选择排序
class Solution {
public:
/**
* @param A: an integer array
* @return: nothing
*/
void sortIntegers(vector<int> &A) {
int sizeA = A.size();
if (sizeA == 0) return;
selectionSort(A);
return;
}
private:
void selectionSort(vector<int> &A) {
int len = A.size();
for (int i = 0; i < len - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < len; j++) {
if (A[minIndex] > A[j]) minIndex = j;
}
int tmp = A[minIndex];
A[minIndex] = A[i];
A[i] = tmp;
}
return;
}
};
注意:直接选择排序算法是不稳定的,举个简单的例子,就知道它是否稳定…例如:(7) 2 5 9 3 4 [7] 1…当我们利用直接选择排序算法进行排序时候,(7)和1调换,(7)就跑到了[7]的后面了,原来的次序改变了,这样就不稳定了。
总结,有swap的排序都不稳定。
- Shell排序
- Heap排序
- Radix排序
- 桶排序