1、冒泡排序
1.从头到尾遍历所有元素,如果当前元素大于下一个元素,则进行交换。以此类推,则最大的元素将会被放到数组的末尾。
2.数组总长度减少1,继续重复上一步操作,直到最外层循环的元素个数变成1则排序完成。
public static void bubbleSort(int[] arr) {
//1、所有元素的数量
for (int i = arr.length - 1; i > 0; i--) {
//2、交换元素的过程
for (int j = 0; j < i; j++) {
//3、设置交换元素的条件
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
冒泡排序算法只需要常数数量的额外的空间,所以空间复杂度为O(1);
当数组是顺序序列时,时间复杂度为O(N);
当数组是倒序序列时,时间复杂度为O(N^2);//通常认为的平均复杂度也是如此
冒泡排序是一种稳定的排序算法;
2、选择排序
1、假设第一个元素是最小的元素,不断和后面的元素进行比较,直到找出最小的元素的下标,赋值给minIndex;
2、如果minIndex和第一个元素的下标(i)不相同,那么就进行数据交换;
3、重复上述步骤
public static void selectionSort(int[] arr) {
//1、遍历所有元素
for (int i = 0; i < arr.length; i++) {
//2、假设第一个元素就是数值最小的元素
int minIndex = i;
//3、使用当前的元素和其它所有元素做对比
for (int j = i; j < arr.length; j++) {
//4、元素进行比较
if (arr[j] < arr[minIndex]) {
minIndex = j;//如果发现arr[j]比arr[minIndex]要小,则下标赋值给最小索引,即把j赋值给minIndex
}
}
//4、最后进行真正的数据交换,而前面的步骤只是下标赋值
if (minIndex != i) {//注意,一开始的minIndex是和i相等的,所以需要判断minIndex和i不相等的情况。
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
时间复杂度和空间复杂度和冒泡排序是相同的,但是数据交换的操作更少,性能会高于冒泡排序;
需要注意的是,选择排序在标准语法上,相同的元素也是需要交换的,因此是一种不稳定的排序算法;
如何解决它的不稳定性呢?个人认为是开辟一个新的数组,将元素放入新的数组而不是进行交换,这种做法显然会牺牲空间。
3、插入排序
1、插入第一个元素,并指定该元素为已排序部分。
2、然后插入第二个元素,在和第一个元素构成的区域进行排序。
3、重复上述步骤
public static void insertionSort(int[] arr) {
//1、循环插入元素
for (int i = 0; i < arr.length; i++) {//如果这一步是从左往右的,那么下一步数据比较就应该从右往左为佳
//如果同向比较,可能会造成位置错乱的问题
//2、遍历已经存在于数组的元素,并进行排序(类似冒泡)
for (int j = i - 1; j >= 0; j--) {//应该从右往左进行数据比较
//3、对元素数值进行交换操作
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
时间复杂度和空间复杂度与上述两种排序算法都相同。
在有序部分元素和待插入元素相等的时候,待插入的元素将被放在前面,因而插入排序是稳定的。
4、希尔排序
1、将所有数据分成(array.length/2)组
2、循环遍历每个小组,并对其进行排序
3、把步长变为原先的二倍,重复上述操作
public static void shellSort(int[] arr) {
//1、记录一共有多少个组
int gap = arr.length / 2;
//声明一个变量,记录当前值
int current;
//2、循环遍历小组
while (gap > 0) {
//对小组中的数据进行插入排序
for (int j = gap; j < arr.length; j++) {
current = arr[j];//得到需要比较的数据
int preIndex = j - gap;//得到小组内上一个数据的下标
//进行插入排序
while (preIndex >= 0 && current < arr[preIndex]) {
//数据交换
arr[preIndex + gap] = arr[preIndex];
preIndex -= gap;
}
arr[preIndex + gap] = current;
}
gap = gap / 2;
}
}
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。
——摘自百度百科
空间复杂度认为是O(1)或者O(N)
时间复杂度通常认为是O(N^1.3~N^2);
5、快速排序
1、记录一个基准数字,选取未排序数组的第一个元素作为基准数字;
2、数组第一个元素的下标记作i,数组最后一个元素的下标记作j;
3、从左边开始找到比基准数字小的元素,从右边开始找到比基准数字大的元素,然后将这两个元素交换;
4、将基准数字进行调整,把基准数字和下标为i的元素进行交换,并且基准数字接下来不再参与排序;
5、递归执行上述操作
public static void quickSort(int[] arr, int left, int right) {
//设置递归推出条件
if (left > right) {
return;
}
//记录左边编号和右边编号
int i = left;
int j = right;
//记录基准数字
int base = arr[left];
//主要操作
while (i != j) {
//右侧编号查找数字
while (j > i && arr[j] >= base) {
j--;
}
//左侧编号查找数字
while (i < j && arr[i] <= base) {
i++;
}
//数字交换
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//基准数字调整,即基准数字和i下标位置数字交换
arr[left] = arr[i];
arr[i] = base;
//i位置是基准数字,不再参与排序过程
//左侧数字进行二次分类
quickSort(arr, left, i - 1);
//右侧数字进行二次分类
quickSort(arr, i + 1, right);
}
快速排序是上面五种排序算法中效率较高的一种,空间复杂度为O(log N)
时间复杂度为O(N * log N)