在算法系列(二)查找算法 一文中,主要介绍了二分查找。这篇文章来介绍一下排序算法。
排序算法概述
排序算法比较多,分两个篇幅来讲。由易道难。
先总体看一下都有哪些排序算法
冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序,计数排序,基数排序,希尔排序,桶排序。
排序算法分类
根据排序过程中待排序文件存放的位置不同,可以把排序分为内部和外部排序两大类。在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序;在排序过程中,只有部分数被调入内存,并借助内存调整数在外存中的存放顺序排序方法称为外排序。内部排序适用于记录个数不很多的较小待排序文件的排序;外部排序则适用于记录个数太多不能一次全部放入内存的较大待排序文件的排序。
内部排序分类:
交换排序:常用的交换排序方法有冒泡排序和快速排序。
选择排序:常用的选择排序方法有直接选择排序、树型选择排序和堆排序。
插入排序:主要的插入排序方法有直接插入排序、希尔排序、二分法插入排序、二路插入排序和共享栈插入排序等。
归并排序
基数排序
交换排序:常用的交换排序方法有冒泡排序和快速排序。
选择排序:常用的选择排序方法有直接选择排序、树型选择排序和堆排序。
插入排序:主要的插入排序方法有直接插入排序、希尔排序、二分法插入排序、二路插入排序和共享栈插入排序等。
归并排序
基数排序
PS:我们只分析内部排序,暂时不对外部排序进行分析
待排序的记录序列中可能存在两个或两个以上关键字相等的记录。排序前的序列中Ri领先于Rj(即i<j).若在排序后的序列中Ri仍然领先于Rj,则称所用的方法是稳定的
如:插入排序 ,基数排序 ,归并排序 ,冒泡排序 ,计数排序
不稳定的排序算法有:快速排序,希尔排序,简单选择排序,堆排序。
冒泡排序
原理
原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,
这样一趟过去后,最大或最小的数字被交换到了最后一位,
然后再从头开始进行两两比较交换,直到倒数第二位时结束
这样一趟过去后,最大或最小的数字被交换到了最后一位,
然后再从头开始进行两两比较交换,直到倒数第二位时结束
代码实现
package com.algorithm.sort;
/**
* 冒泡排序算法
*
* @author chao
*
*/
public class Bubblesort {
/**
* 简单冒泡排序
*/
public static void sort(int[] num) {
int temp;
int len = num.length;
for (int i = 1; i < len; i++)
for (int j = 0; j < len - i; j++) {
if (num[j] > num[j + 1]) {
temp = num[j];
num[j] = num[j + 1];
num[j + 1] = temp;
}
}
}
public static void main(String[] args) {
int[] num = { 1, 5, 3, 2 };
sort(num);
for (int i = 0; i < num.length; i++)
System.out.print(num[i] + " ");
}
}
选择排序
原理
它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
代码实现
package com.algorithm.sort;
/**
* 选择排序算法
*
* @author chao
*
*/
public class SelectionSort {
/**
* 选择排序算法简单实现
*
* @param num
*/
public static void sort(int num[]) {
int min, index, tmp;
int len = num.length;
for (int i = 0; i < len; i++) {
min = num[i];
index = i;
for (int j = i + 1; j < len; j++) {
if (num[j] < min) {
min = num[j];
index = j;
}
}
if (index != i) {
tmp = num[i];
num[i] = num[index];
num[index] = tmp;
}
}
}
public static void main(String[] args) {
int[] num = { 1, 5, 3, 2 };
sort(num);
for (int i = 0; i < num.length; i++)
System.out.print(num[i] + " ");
}
}
插入排序
原理
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据
代码实现
package com.algorithm.sort;
/**
* 插入排序
*
* @author chao
*
*/
public class InsertionSort {
/**
* 简单插入排序
*
* @param num
*/
public static void sort(int num[]) {
int temp;
int len = num.length;
for (int i = 1; i < len; i++) {
if (num[i - 1] > num[i]) {
temp = num[i];
int j = i;
while (j > 0 && num[j - 1] > temp) {
num[j] = num[j - 1];
j--;
}
num[j] = temp;
}
}
}
public static void main(String[] args) {
int[] num = { 1, 5, 3, 2 };
sort(num);
for (int i = 0; i < num.length; i++)
System.out.print(num[i] + " ");
}
}
算法分析
以上三种算法的时间复杂度都为O(n^2)
插入排序
最好的情况下:正序有序(从小到大),这样只需要比较n次,不需要移动。因此时间复杂度为O(n)最坏的情况下:逆序有序,这样每一个元素就需要比较n次,共有n个元素,因此实际复杂度为O(n^2)
选择排序
最好情况下:交换0次,但是每次都要找到最小的元素,因此大约必须遍历N*N次,因此为O(N^2)。
减少了交换次数!
最坏情况下,平均情况下:O(N^2)
稍作改进,有序情况因此时间复杂度为O(n)
最坏情况下,平均情况下:O(N^2)
冒泡排序
普通算法都是O(N^2),稍作改进,有序情况因此时间复杂度为O(n)
选择排序的优点是移动次数少,仅移动n次就可以了,插入排序在有序的情况下运行时间会好些,插入排序移动次数多达n^2次。如果移动一个元素相当耗时,最好运行选择排序。插入排序更适合链表结构的数据排序。
算法实现代码github地址为https://github.com/robertjc/simplealgorithm
后续会不断补充,有些地方写的可能有问题,请多指教。
欢迎扫描二维码,关注公众账号
