注:数组排序以从升序排序为例
1.冒泡排序
1.1 算法原理:从左向右比较相邻的元素,如果前面的数比后面的数大,则交换它们的位置,这样每次比较完,靠后的位置的数字都是比前面的数值更大,循环比较到倒数第二位。详细解释见冒泡排序
1.2 算法分析:
时间复杂度:若数组开始为正序,则比较次数Cmin= n-1,移动次数Mmin=0,最好的时间复杂度为O(n);若数组初始为反序的,则需要进行n-1趟排序,最坏的时间复杂度为O(n2),综合一下冒泡排序的时间复杂度为O(n2)。
算法的稳定性:冒泡排序的算法是比较两个相邻的元素,将较大值往后移,较小值往前移,移动只是发生在相邻元素之间,即使是两个相等的元素,也不会改变前后顺序,冒泡排序是一种稳定的排序算法。
1.3 算法实现:
/**
* 冒泡排序
* 实现方式:使用for循环遍历数组,将前后两个数组元素依次遍历比较,将较大值往后靠
* @param arr 传入一个int类型的数组
* */
private static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("冒泡排序后的数组为:");
printArr(arr);
}
2.快速排序
快速排序是对冒泡排序的一种改进
2.1 算法原理:假设数组下标第一个为i,最后一个为j选取数组最左边的元素为基准值,先从j开始向左遍历,逐个查找比基准值小的数值然后再与基准值交换位置,再从i开始向右遍历,找到第一个大于基准值的元素,然后与基准值交换位置,如此反复查找,当j<=i的时候,该趟查找结束,剩下的也这样递归查找,直到数组为升序。详细解释见快速排序
2.2 算法分析:
时间复杂度:最坏情况的时间复杂度和插入排序是一样的为O(n2),最好的情况的时间复杂度是O( nlogn),综上快速排序算法的平均时间复杂度为O( nlogn)。
算法稳定性:是种不稳定的排序方式。
2.3 算法实现:
/**
* 快速排序
* 1.取中值:先定义数组两边的下标分别标为i,j,取最左边的值为基准值,然后从j开始向右遍历,找到第一个小于基准值的数值
* 再与基准值交换位置,然后再从i开始向右遍历,寻找到第一个大于基准值的数值,交换位置,直到i >= j,第一次遍历结束,
* 之后重复步骤
* @param low 传入int类型的值
* @param high 传入int类型的值
* @param arr 传入一个int类型的数组
* @return low 返回一个int类型的值
* */
private static int getMid(int low, int high, int[] arr) {
int temp = arr[low];
while (low < high) {
if (arr[high] >= temp) {
high--;
}
int temp1 = arr[low];
arr[low] = arr[high];
arr[high] = temp1;
if (low < high && arr[low] <= temp) {
low++;
}
int temp2 = arr[high];
arr[high] = arr[low];
arr[low] = temp2;
}
return low;
}
/**
* 2.快速排序
* 实现方式:将每次遍历得到的中值前后两个数组进行快速排序
* @param low 传入一个int类型的值
* @param high 传入一个int类型的值
* @param arr 传入一个int类型的值
* */
private static void quickSort(int low, int high, int[] arr) {
int mid = getMid(low, high, arr);
if (low < high) {
quickSort(low, mid - 1, arr);
if (mid != arr.length - 1) {
quickSort(mid + 1, high, arr);
}
}
}
注:在取基准值进行排序时,对基准值右边即给后半部分进行排序的时候,需要加入基准值是否为数组最后一个元素,如果不加判断条件,在基准值进行+1操作之后,会出现越界错误,这是一个测试用例,可以用这例子对其它快速排序算法进行测试一下:
int[] arr = {12,45,1,45,89,78,19};
3.插入排序
包括直接插入排序、,二分插入排序(又称折半插入排序),链表插入排序,希尔排序(又称缩小增量排序),这里主要使用的是直接插入排序。
3.1 算法原理:将一个数值插入到已经排好顺序的序列中,使插入后的数组仍然是个有序数组。详细解释见插入排序
3.2 算法分析:
时间复杂度:O(n2)
算法稳定性:是稳定的排序方法
3.3 算法实现
/**
* 插入排序
* 实现方式:使用for循环遍历,将数组依次划分成小区间,将小区间里面的元素进行排序
* @param arr 传入一个int类型的数组
* */
private static void insertSort(int[] arr) {
int currentElem = 0, j = 0;
for (int i = 1; i < arr.length; i++) {
currentElem = arr[i];
for (j = i - 1; j >= 0 && arr[j] > currentElem; j--) {
arr[j + 1] = arr[j];
}
arr[ j + 1] = currentElem;
}
System.out.println("插入排序后的数组为:");
printArr(arr);
}
4.选择排序
4.1 算法原理:每一次从数组中选取最小值,存放在数组的起始位置。详细解释见选择排序
4.2 算法分析:
时间复杂度:最好的时间复杂度是O(n2),最坏的时间复杂度是O(n2),平均时间复杂度是O(n2)
算法稳定性:是种不稳定的排序方式。
4.3 算法实现:
/**
* 选择排序
* 实现方式:使用for循环遍历,将数组元素依次比较,将较小值的小标标为min,每次遍历排序找出最小的值
* @param arr 传入一个int类型的数组
* */
private static void selectSort(int[] arr) {
int min = 0;
for (int i = 0; i < arr.length; i++) {
min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
if (min != i) {
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
System.out.println("选择排序后的数组为:");
printArr(arr);
}
5、计数排序算法
5.1 算法原理:计数排序是一种非常快捷的稳定性强的排序方法,非基于比较的排序算法,时间复杂度O(n+k),其中n为要排序的数的个数,k为要排序的数的组大值。 计数排序对一定量的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序。 计数排序是消耗空间发杂度来获取快捷的排序方法,其空间发展度为O(K)同理K为要排序的最大值
5.2 算法分析:
时间复杂度:O(n + k) k 为数组中最大值-最小值
算法稳定性:稳定
5.3 算法实现:
/**
* 计数排序:
* 实现方式:
* 1、对输入的数字进行频率统计
* 2、将频率转换为开始索引
* 准备三个数组:
* 1、要排序的数列大小
* 2、要记录比某个数小的元素的个数,大小为 K, 构造一个 k + 1 大小的数组
* */
private static void countSort(int[] arr) {
int max = arr[0], min = arr[0];
for (int i: arr) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
// 最大最小元素之间范围【min, max】的长度
int r = max - min + 1;
// 1、计算频率
int[] count = new int[r + 1]; // 数组每一个下标位置的值,代表了对应整数出现的次数
for (int i: arr) {
// 使用加 1 后的索引,有重复值就自增
count[i - min + 1]++;
}
// 2、频率 -> 元素的开始索引,后面的元素等于前面的元素之和
for (int i = 0; i < count.length; i++) {
count[i + 1] += count[i];
}
// 3、元素按照开始索引分类,用到一个和待排序数组一样大临时数组存放数据
int[] aux = new int[arr.length];
for (int i: arr) {
// 填充一个数据后,自增,以便相同的数据可以填到下一个空位
aux[count[i - min]++] = i;
}
System.out.println("计数排序后的数组为:");
printArr(aux);
}
局限:
1、数列最大值和最小值差距过大
2、数列元素不是整数
6、桶排序
6.1、算法原理:
1、创建桶 , 桶个数:等于原始数列的元素个数,除了最后一个桶只包含最大值,其他桶的区间按比例确定 区间跨度:(最大值 - 最小值)/(桶的数量 - 1)
2、遍历原始数组,将元素对号入座放入各个桶中
3、每个桶内部的元素分别排序
4、遍历所有的桶,输出所有元素
6.2 算法分析:
时间复杂度:假设有 n 个元素,分成 m 个桶(分桶 m = n)平均每个桶的元素个数为 n / m
O(n + m + n(logn-logm))
空间复杂度:O(m + n)
6.3、代码实现
/**
* 桶排序
* @param arr 数组
* */
public static double[] bucketSort(double[] arr) {
// 1、取最大值和最小值,并输出区间 d
double max = arr[0];
double min = arr[0];
for (double i: arr) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
double d = max - min;
// 2、初始化桶,每个桶里面有一个 LinkedList 链表
int bucketNum = arr.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketList.add(new LinkedList<Double>());
}
// 3、遍历原始数组,将每个元素放入桶中
for (int i = 0; i < arr.length; i++) {
int num = (int)((arr[i] - min) * (bucketNum - 1) / d);
bucketList.get(num).add(arr[i]);
}
// 4、对每个桶内进行排序
for (int i = 0; i < bucketList.size(); i++) {
Collections.sort(bucketList.get(i));
}
// 5、输出全部元素
double[] sortedArr = new double[arr.length];
int index = 0;
for (LinkedList<Double> list: bucketList) {
for (double element: list) {
sortedArr[index] = element;
index++;
}
}
return sortedArr;
}
版权声明:欢迎转载, 转载请保留原文链接。https://mp.csdn.net/postedit/79351742