冒泡排序 选择排序 插入排序 是简单排序
快速排序 希尔排序是高级排序
一、冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序的算法实现如下:【排序后,数组从小到大排列】
时间复杂度:比较和交换都是N^2成正比 时间复杂度为O(N^2)
/**
* 冒泡排序
* 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
* 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
* 针对所有的元素重复以上的步骤,除了最后一个。
* 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
* @param numbers 需要排序的整型数组
*/
public static void bubbleSort(int[] numbers) {
int temp = 0;
int size = numbers.length;
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - 1 - i; j++) {
if (numbers[j] > numbers[j + 1]) //交换两数位置
{
temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
}
}
}
}
二、快速排序
快速排序的基本思想:
通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。
方法: 把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。
快速排序之所以比较快,是因为相比冒泡排序,每次的交换都是跳跃式的,每次设置一个基准值,将小于基准值的都交换到左边,大于基准值的都交换到右边,这样不会像冒泡一样每次都只交换相邻的两个数,因此比较和交换的此数都变少了,速度自然更高。当然,也有可能出现最坏的情况,就是仍可能相邻的两个数进行交换。
优化:在有序或者逆序数组排序时候效率是O(N^2),为了避免采用取头,尾,中间,三个数的中间值做为其实基数,并对这三个进行排序,提高了内部循环的执行效率
快速排序基于分治思想,它的时间平均复杂度很容易计算得到为O(N*logN)
代码实现:
/**
* 快排核心算法,递归实现
*
* @param array
* @param left
* @param right
*/
public void quickSort(int[] array, int left, int right) {
if (left > right) {
return;
}
// base中存放基准数
int base = array[left];
int i = left, j = right;
while (i != j) {
// 顺序很重要,先从右边开始往左找,直到找到比base值小的数
while (array[j] >= base && i < j) {
j--;
}
// 再从左往右边找,直到找到比base值大的数
while (array[i] <= base && i < j) {
i++;
}
// 上面的循环结束表示找到了位置或者(i>=j)了,交换两个数在数组中的位置
if (i < j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
// 将基准数放到中间的位置(基准数归位)
array[left] = array[i];
array[i] = base;
// 递归,继续向基准的左右两边执行和上面同样的操作
// i的索引处为上面已确定好的基准值的位置,无需再处理
sort(array, left, i - 1);
sort(array, i + 1, right);
}
三、选择排序
基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
和冒泡排序比较了相同次数N*(N-1)/2,交换次数为N,时间复杂度为O(N^2) 比冒泡效率高,
/**
* 选择排序算法
* 在未排序序列中找到最小元素,存放到排序序列的起始位置
* 再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。
* 以此类推,直到所有元素均排序完毕。
* @param numbers
*/
//选择排序
public void selectSort(int arr[]) {
int min;
for (int i = 0; i < arr.length-1 ; i++) {//控制趟数
min=i;
for (int j = i+1 ; j < arr.length; j++) {//查找最小值
if (arr[min]>arr[j]){
min=j;
}
}
if (i!=min){
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
for (int num : arr) {
Log.e("selectSort", num + " ");
}
}
四、插入排序
基本思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。
比冒泡块一倍 比选择略快 时间负责度O(N^2) 对于顺序数据 执行比冒泡块 逆序则慢
数据量小的情况下 插入排序比冒泡排序和选择排序好 数据量大的情况下 用快速排序
/**
* 插入排序
* 从第一个元素开始,该元素可以认为已经被排序
* 取出下一个元素,在已经排序的元素序列中从后向前扫描
* 如果该元素(已排序)大于新元素,将该元素移到下一位置
* 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
* 将新元素插入到该位置中
* 重复步骤2
* @param numbers 待排序数组
*/
public static void insertSort(int[] numbers)
{
int size = numbers.length;
int temp = 0 ;
int j = 0;
for(int i = 1 ; i < size ; i++)
{
temp = numbers[i];
//假如temp比前面的值小,则将前面的值后移
for(j = i ; j > 0 && temp < numbers[j-1] ; j --)
{
numbers[j] = numbers[j-1];
}
numbers[j] = temp;
}
}
五 归并排序
/**
* 文件描述: 归并排序
* 分而治之(divide - conquer);每个递归过程涉及三个步骤
* 第一, 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素.
* 第二, 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
* 第三, 合并: 合并两个排好序的子序列,生成排序结果.
*/
public static int[] sort(int[] a, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
sort(a, low, mid);
sort(a, mid + 1, high);
//左右归并
merge(a, low, mid, high);
}
return a;
}
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low;
int j = mid + 1;
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = a[j++];
}
// 把新数组中的数覆盖nums数组
for (int x = 0; x < temp.length; x++) {
a[x + low] = temp[x];
}
}
时间复杂度 O(N*logN) 在存储器中保持2个数组,占用空间大 在空间足够情况下 效率高
时间复杂度比较
算法的效率主要由以下两个复杂度来评估:
- 时间复杂度:评估执行程序所需的时间。可以估算出程序对处理器的使用程度。 函数的执行次数
- 空间复杂度:评估执行程序所需的存储空间。可以估算出程序对计算机内存的使用程度。