虽然我们用的语言中有排序的函数,我们用的时候直接调用就可以,不过这些经典排序算法中的许多思想以及实现它们过程中的一些编程技巧在我们实际编程中还是被频繁被用到的,作为一个程序员,这是基本功,还是很有必要掌握的。
下面算法全部最终按从小到大排序。
1.冒泡排序
思想:相邻两个数进行比较,如果前一个更大,就和后一个交换,这样每次将未排序部分的最大的数添加到已排好序的部分的第一个,进行n-1次即可。
代码如下:
public int[] mp(int[] a) {
if (a == null || a.length == 0) {
return null;
}
int temp;
for (int i = 0; i < a.length - 1; i++) {
for (int j = 0; j < a.length - i - 1; j++) {
if (a[j] > a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
return a;
}
平均时间复杂度O(N^2),最坏O(N^2),最好O(N),辅助存储O(1),是稳定的排序算法。
2.选择排序
思想:每次将未排好序的部分的最小的数添加到已排好序的部分的末尾,排序过程中一直使前部分保持有序。
(当然你也可以每次将未排好序的部分的最大的数添加到已排好序的部分的开头,排序过程中一直使后部分保持有序)
代码如下:
public int[] selectionSort(int[] a) {
if (a == null || a.length == 0) {
return null;
}
int k,temp;
for(int i = 0;i < a.length - 1;i++) {
k = i;
for(int j = i+1;j < a.length;j++) {
if(a[j] < a[k]) {
k = j;
}
}
if(k != i) {
temp = a[i];
a[i] = a[k];
a[k] = temp;
}
}
return a;
}
平均时间复杂度O(N^2),最坏O(N^2),最好O(N^2),辅助存储O(1),不稳定(看下 2,2,1就知道了,开始在前面的2,排好序后到后面去了)。
3.直接插入排序
思想:直接插入排序的思想是每次将未排序部分的第一个数插入到已排好序的部分,并使已排好序的部分依旧有序。
代码如下:
public int[] insertionSort(int[] a) {
if(a == null || a.length == 0) {
return null;
}
int j,temp;
for(int i = 1;i < a.length;i++) {
temp = a[i];//a[i]会被a[j+1] = a[j]覆盖掉,所以先用一个变量存起来
j = i-1;
while(j >= 0 && a[j] > temp) {
a[j+1] = a[j];
j--;
}
a[j+1] = temp;
}
return a;
}
平均时间复杂度O(N^2),最坏O(N^2),最好O(N),辅助存储O(1),是稳定的排序算法。
4.快速排序
思想:把比某个数(一般取序列的第一个元素)小的数移到它的左边,大的数移到右边,分治递归这个过程,到序列只剩一个数自然就排好了。。
代码如下:
void quickSort(int[] a,int low,int high) {
if (low >= high) {//不要用low == high,下面i-1会小于low的
return;
}
int i = low,j = high,temp = a[low];
while (i < j) {
while (i < j && a[j] >= temp) {
j--;
}
if (i < j) {
a[i++] = a[j];
}
while (i < j && a[i] <= temp) {
i++;
}
if (i < j) {
a[j--] = a[i];
}
}
a[i] = temp;
quickSort(a, low, i - 1);
quickSort(a, i + 1, high);
}
可以稍微简化下:
void quickSort(int[] a,int low,int high) {
if (low >= high) {
return;
}
int i = low,j = high;
int temp = a[low];
while (i < j) {
while (i < j && a[j] >= temp) {
j--;
}
if (i < j) {
a[i++] = a[j];
}
while (i < j && a[i] <= temp) {
i++;
}
a[j--] = a[i];
}
a[i] = temp;
quickSort(a, low, i - 1);
quickSort(a, i + 1,high);
}
Python版,把partition抽离出来,并在partition时随机选择该部分中的某个元素作为主元。
import random
def partition(a, s, e):
randIx = random.randint(s, e)
t = a[s]
a[s] = a[randIx]
a[randIx] = t
t = a[s]
i, j = s, e
while i < j:
while i < j and a[j] >= t:
j -= 1
if i < j:
a[i] = a[j]
while i < j and a[i] <= t:
i += 1
if i < j:
a[j] = a[i]
a[i] = t
return i
def quickSort(a, s, e):
if s < e:
ix = partition(a, s, e)
quickSort(a, s, ix-1)
quickSort(a, ix+1, e)
# a = [5, 3, 1, 8, 6, 0]
# a = [5, 3, 3, 8, 8, 0]
# a = [3, 3, 2, 8, 6, 0]
a = [2, 2, 3, 2, 1, 1]
quickSort(a, 0, len(a)-1)
print (a)
# [1, 1, 2, 2, 2, 3]
时间复杂度O(N*lg(N)),最坏O(N^2),最好O(N*lg(N)),辅助存储O(lg(N)),不稳定。
上面代码的实现每次和第一个数比较,这样当数组有序的时候,会直接退化为O(N^2),可以每次随机选择一个数,与第一个数进行交换,然后再按上面的方法来,避免这样的问题。
5.归并排序
思想:先一层层的往下【递推】到每个区间只有一个数,然后在【回归】的时候不断合并【区间内已经有序的区间】,并保证合并后的区间有序。
#include<stdio.h>
void merge(int a[],int s,int mid,int e,int temp[]) {
int i = s,j = mid + 1,k = s;
while(i <= mid && j <= e) {
if(a[i] <= a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
while(i <= mid) {
temp[k++] = a[i++];
}
while(j <= e) {
temp[k++] = a[j++];
}
for(int i = s;i <= e;i++) {
a[i] = temp[i];
}
}
void mergeSort(int a[],int s,int e,int temp[]) {
if(s < e) {
mergeSort(a,s,(s + e) / 2,temp);
mergeSort(a,(s + e) / 2 + 1,e,temp);
merge(a,s,(s + e) / 2,e,temp);
}
}
int main() {
int a[] = {4,2,7,9,1,3,10,5};
int temp[8];
mergeSort(a,0,7,temp);
for(int i = 0;i < 8;i++) {
printf("%d ",a[i]);
}
}
速度仅次于快速排序,一般用于总体无序,但是各子项相对有序的数列。
平均时间复杂度O(N*lg(N)),最坏O(N*lg(N)),最好O(N*lg(N)),稳定,辅助存储O(N)。
6.堆排序
思想:简单来说就是先建堆(比如你要实现从小到大排序,就建个大顶堆),然后交换第一个元素和当前堆的最后一个元素,然后heapSize减一,然后调整堆,这样每次都能把未排序部分的最大值放到当前堆的最后一个位置上去,具体代码如下(完全是根据算法导论上的伪代码来写的):
#include<stdio.h>
#define PARENT(i) (i - 1) / 2
#define LEFT(i) 2 * i + 1
#define RIGHT(i) 2 * i + 2
int A_heapSize,A_length;
//维护堆的性质
void MAX_HEAPIFY(int A[],int i) {
int l = LEFT(i);
int r = RIGHT(i);
int largest;
if(l < A_heapSize && A[l] > A[i]) {
largest = l;
} else {
largest = i;
}
if(r < A_heapSize && A[r] > A[largest]) {
largest = r;
}
if(largest != i) {
int temp = A[largest];
A[largest] = A[i];
A[i] = temp;
MAX_HEAPIFY(A,largest);
}
}
//建堆
void BUILD_MAX_HEAP(int A[]) {
A_heapSize = A_length;
for(int i = A_length / 2;i >= 0;i--) {
MAX_HEAPIFY(A,i);
}
}
//堆排序
void HEAPSORT(int A[]) {
BUILD_MAX_HEAP(A);
for(int i = A_length - 1;i >= 1;i--) {
int temp = A[0];
A[0] = A[i];
A[i] = temp;
A_heapSize--;
MAX_HEAPIFY(A,0);
}
}
int main() {
int A[] = {4,2,7,9,1,3,10,5};
A_length = 8;
HEAPSORT(A);
for(int i = 0;i < A_length;i++) {
printf("%d ",A[i]);
}
printf("\n");
}
平均时间复杂度O(N*lg(N)),最坏O(N*lg(N)),最好O(N*lg(N)),不稳定,辅助存储O(1)。
下次接着写。。。