时间复杂度
(图源自微信公众:五分钟学算法)
关于稳定性
-
稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。
-
不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。
一、冒泡排序(Bubble Sort)
冒泡排序原理是从数组开头依次两两比较,遇到大的数在上边,就两两互换, 当“天平”移动到末端时,最小的数就“冒”到最上边,然后天平归位重新开始。
代码如下:
//Buble Sort by Java
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] < arr[j + 1]) { //将小的数往右排
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
貳、选择排序(Selection Sort)
遍历数组一直到倒数第二位,每次遍历时,从该位以后一直到末尾找符合条件的数, 并记录该数的下标,遍历结束后即可进行换位。
代码如下:
//Selection Sort by Java
for (int i = 0; i < arr.length - 1; i++) {
int index = i;
for (int j = i + 1; j < arr.length; j++) {
if(arr[j] > arr[index]) { //此处index不能用i替代
index = j;
}
}
if(index != i) {
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
叁、插入排序(Insert Sort)
将一个记录插入到已排好序的序列中,从而得到一个新的有序序列 (将序列的第一个数据看成是一个有序的子序列, 然后从第二个记录逐个向该有序的子序列进行有序的插入,直至整个序列有序)
代码如下:
//Insert Sort by Java
for (int i = 1; i < arr.length; i++) {
// 待排元素小于有序序列的最后一个元素时,向前插入
if (arr[i] < arr[i - 1]) {
int temp = arr[i];
for (int j = i; j >= 0; j--) {
if (j > 0 && arr[j - 1] > temp) {
arr[j] = arr[j - 1];
} else { //只剩下第一个位置或者找到要插入的位置
arr[j] = temp;
break;
}
}
}
}
肆、希尔排序
希尔排序(Shell's Sort)在插入排序算法的基础上进行了改进,算法的时间复杂度与前面几种算法相比有较大的改进。其算法的基本思想是:先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。
代码如下:
//Shell's Sort by Java
int increasement = arr.length;
do {
increasement = increasement / 3 + 1; //这里+1,下面的for就不需要单独-1
for (int i = 0; i < increasement; i++) {
for (int j = i + increasement; j < arr.length; j += increasement) {
if (arr[j] < arr[j - increasement]) { //左边的比右边的大,交换位置
int temp = arr[j], k;
for (k = j - increasement; k >= 0 && temp < arr[k]; k -= increasement) { //同插入排序
arr[k + increasement] = arr[k]; //符合条件,往后挪,让前面位置空出来
}
arr[k + increasement] = temp; //不符合条件,则在当前位置插入讨论的那个数
}
}
}
} while (increasement > 1);
伍、快速排序
实际上就是分治思想的冒泡,利用递归解决
代码如下:
// Quick Sort by Java
public static void QuickSort(int array[], int left, int right) {
int i = left, j = right;
int temp;
int pivot; // 基准点,也就是数组的中点;
pivot = array[(left + right) / 2];
while (i <= j) {
// 从左到右找到大于等于基准点的元素;
while (array[i] < pivot) {
i++;
}
// 从右到左找到小于等于基准点的元素;
while (array[j] > pivot) {
j--;
}
// 如果i <= j , 则互换;
if (i <= j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
}
if (left < j) {
QuickSort(array, left, j);
}
if (right > i) {
QuickSort(array, i, right);
}
}
陆、归并排序
“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序表。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(表示不小于x的最小整数)个长度为2(或者是1)的有序子序列,再两两归并。如此重复,直到得到一个长度为n的有序序列为止。这种排序方法称为2-路归并排序。
代码如下:
public static void MergeSort(int[] arr, int start, int end, int[] temp)
{
if (start >= end)
return;
int mid = (start + end) / 2;
MergeSort(arr, start, mid, temp);
MergeSort(arr, mid + 1, end, temp);
// 合并两个有序序列
int length = 0; // 表示辅助空间有多少个元素
int i_start = start;
int i_end = mid;
int j_start = mid + 1;
int j_end = end;
while (i_start <= i_end && j_start <= j_end)
{
if (arr[i_start] < arr[j_start])
{
temp[length++] = arr[i_start++];
}
else
{
temp[length++] = arr[j_start++];
}
}
while (i_start <= i_end)
{
temp[length++] = arr[i_start++];
}
while (j_start <= j_end)
{
temp[length++] = arr[j_start++];
}
// 把辅助空间的数据放到原空间
for (int i = 0; i < length; i++)
{
arr[start + i] = temp[i];
}
}