排序算法:
1、 插入类:直接插入排序
折半插入排序
Shell排序
2、交换类:冒泡排序
快速排序
3、选择类:简单选择排序
堆排序
4、 归并类:归并排序
5、 分配类:基数排序
交换类
1、冒泡排序
特点:
- 稳定排序
- 可用于链表和顺序表
- 不适合用于初始记录无序、n较大的情况。
- 交换次数较多,平均效率低于直接插入排序
时间复杂度为:Ο(n2)
【当需要排序的元素小于32时使用!!高效!!!!】
【当数据量,以及该函数被调用较少(即不需要考虑效率时),冒泡】
需求:
请按照从小到大对列表,进行排序------>:[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619]
思路:相邻两个值进行比较,将较大的值放在右侧,依次比较!
原理分析:
列表中有5个元素两两进行比较,如果左边的值比右边的值大,就用中间值进行循环替换!
既然这样,我们还可以用一个循环把上面的循环进行再次循环,用表达式构造出内部循环!
package algorithm;
/**
*
* 冒泡排序
* @author Mona
*
*/
public class BubblingTest {
public void bubbleTest(int[] number) {
for(int i=1;i<=number.length;i++) {
for(int j=number.length-1;j>=i;j--) { //将j>=1改为j>=i则优化了
if(number[j]>number[j-1]) {
int temp = number[j];
number[j] = number[j-1];
number[j-1] = temp;
}
}
}
// for(int i=0;i<arr.length-1;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;
// }
//
// }
// }
}
public static void main(String[] args) {
BubblingTest bt = new BubblingTest();
int[] number = {4,6,2,1,7,3};
bt.bubbleTest(number);
for(int value : number) {
System.out.println(value);
}
}
}
2、快速排序
快速排序是从冒泡排序演变而来的一种排序算法,但是比冒泡排序高效的多,因此叫做快速排序。
【使用了分治法】快速排序也属于交换排序。
算法特点:
1.不稳定排序
2.只能用于顺序表(其他的实现方法可以用于链表)
3.适合用于初始记录无序、n较大的情况。
4.n较大时,平均情况下快速排序是所有内部排序算法最快的算法之一
时间复杂度是:O(nlogn) ~Ο(n2)
思路分析:
1. 在数组中选一个基准数(通常为数组第一个);
2. 将数组中小于基准数的数据移到基准数左边,大于基准数的移到右边;
3. 对于基准数左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序。
/**
* 快速排序
*
* @author Mona
*
*/
public class QuickTest{
// 排序
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int middle = getMiddle(arr, low, high);
quickSort(arr, low, middle - 1); // 递归调用左半数组
quickSort(arr, middle + 1, high); // 递归调用右半数组
}
}
// 划分函数
public static int getMiddle(int[] arr, int low, int high) {
int privot = arr[low]; // 设置第一个元素为基准,用privot保存基准,数组中空出一个空位来
while (low < high) {
while (low < high && arr[high] >= privot) { // 先从后向前,找到比privot小的值
high--;
}
arr[low] = arr[high];
while (low < high && arr[low] <= privot) { // 先从前向后,找到比privot大的值
low++;
}
arr[high] = arr[low];
} // 当low比high大时,说明找到了该位置
arr[low] = privot; // low所指向的privot就是最终位置
return low;
}
// 测试
public static void main(String[] args) {
int[] arr = { 12, 42, 1, 5, 90, 3, 8, 2, 10 };
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}
选择类
3、选择排序
需求:
请按照从小到大对列表,进行排序----->:
[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619]
思路:第一次,从列表最左边开始元素为array[0],往右循环,从右边元素中找到小于array[0]的元素进行交换,直到右边循环完之后。
第二次,左边第一个元素现在是最小的了,就从array[1],和剩下的array[1:-1]内进行对比,依次进行对比!
对比:它和冒泡排序的区别就是,冒泡排序是相邻的两两做对比,但是选择排序是左侧的“对比元素”和右侧的列表内值做对比!
/**
* 选择排序
* @author Mona
*
*/
public class SelectionSortTest11 {
//选择排序算法
public static void selectionSort(int[] arr) {
int minIndex;
for(int i=0;i<arr.length;i++) {
minIndex = i;
for(int j=i;j<arr.length;j++) {
if(arr[j] < arr[minIndex]) {
minIndex = j; //每次循环记住最小数字的下标,这一轮比较结束后
} //将找出的这个最小数和最初选定的第一个数交换,避免无意义交换
}
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
toString(arr); //调用toString()遍历输出
}
//循环遍历输出函数
public static void toString(int[] arr) {
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i] + " ");
}
}
public static void main(String[] args) {
int[] arr = {3,5,-1,10,8,7,4,2};
selectionSort(arr);
}
}
4、堆排序
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆(升序使用大顶堆);
或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆(降序使用小顶堆)。
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
import java.util.Arrays;
/**
* 堆排序
* 所选的第一个index为最后一个非叶子结点
*
* @author Mona
*
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = {9,6,8,7,0,1,10,4,2};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
//排序
public static void heapSort(int[] arr) {
//开始位置是最后一个非叶子结点,即最后一个结点的父结点
int start = (arr.length-1)/2;
//调整为大顶堆
for(int i=start;i>=0;i--) {
maxHeap(arr, arr.length, i);
}
//先把数组中的第0个和堆中的最后一个数交换位置,再把前面的处理为大顶堆
for(int i=arr.length-1;i>0;i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
maxHeap(arr, i, 0);
}
}
//大顶堆
public static void maxHeap(int[] arr,int size,int index) { //index为当前结点
//左子结点
int leftNode = 2*index+1;
//右子结点
int rightNode = 2*index+2;
int max = index;
//和两个子结点分别比较,找出最大的结点
if(leftNode<size && arr[leftNode]>arr[max]) {
max = leftNode;
}
if(rightNode<size && arr[rightNode]>arr[max]) {
max = rightNode;
}
//交换位置
if(max != index) {
int temp = arr[index];
arr[index] = arr[max];
arr[max] = temp;
//交换位置之后,可能会破坏已经排好的堆,所以之前的堆需要重新排列--->递归
maxHeap(arr, size, max);
}
}
}
运行结果:
插入类
5、直接插入排序:
算法特点:
- 不稳定排序(可以改写成稳定形式)
- 可用于顺序表和链表
需求:请按照从小到大对列表,进行排序------>:
[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619]
思路:一个列表默认分为左侧为排序好的,我们拿第一个元素举例,他左边的全是排序好的,他右侧是没有排序好的,如果右侧的元素小于左侧排序好的列表的元素就把他插入到合适的位置
/**
* 插入排序
* @author Mona
*/
public class InsertionSort {
//优化
//插入排序算法--节省空间方法
public static void insertionSort(int[] ins) {
for(int i=1;i<ins.length;i++) {
int j;
int temp = ins[i]; //保存每次需要插入的那个数
for(j=i;j>0 && ins[j-1]>temp;j--) { //比下面的优化了些
ins[j] = ins[j-1]; //把大于需要插入的数向后移,最后把不大于temp的数就空出来
}
ins[j] = temp; //将需要插入的数放入这个位置
}
for(int v:ins) {
System.out.println(v);
}
}
// //插入排序算法
// public static void insertionSort(int[] ins) {//因为每次替换都需要一个temp空间,比较浪费空间
// for(int i=0;i<ins.length-1;i++) {
// for(int j=i;j>=0;j--) {
// if(ins[j+1]<ins[j]) {
// int temp = ins[j+1];
// ins[j+1] = ins[j];
// ins[j] = temp;
// }
// }
// }
// for(int in : ins) {
// System.out.println(in);
// }
// }
public static void main(String[] args) {
int[] ins = {2,5,1,7,4,8,9,3};
insertionSort(ins);
}
}
6、Shell排序
是直接插入排序算法的一种更高效的改进版本
定义:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
import java.util.Arrays;
/**
* 希尔排序
* 是直接插入排序算法的一种更高效的改进版本
*
* 如果当前元素与加上步长后的那个元素相比较
*
* @author Mona
*
*/
public class ManyTests {
public static void shellSort(int[] arr) {
//遍历所有的步长
for(int d=arr.length/2;d>0;d/=2) {
//接下来即为做直接插入排序操作
for(int i=d;i<arr.length;i++) {
int j;
int max = arr[i]; //将当前值拿出来存储(看成是最大值 与之比较)
max = arr[i];
for(j=i;j>=d && arr[j-d] > max;j-=d) { //遍历本组中所有的元素
arr[j] = arr[j-d];
}
arr[j] = max;
}
}
}
public static void main(String[] args) {
int[] arr = {9,6,8,7,0,1,10,4,2};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
}
归并类
7、归并排序
归并排序,即将两个有序的数组归并成一个更大的有序数组,这是一种简单的递归排序算法。要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。
性质:它能保证将任意长度为N的数组排序所需时间和NlogN成正比;
缺点:它所需的额外空间和N成正比。
import java.util.Arrays;
/**
* 归并排序 1 3 5 7 2 4 6 8 10
*
* @author Mona
*
*/
public class MergeSort {
// 用于存储归并后的临时数据
public static void merge(int[] arr, int low, int middle, int high) {
int[] temp = new int[high - low + 1]; // 临时数组大小
// 记录第一个数组中需要遍历的下标
int i = low;
// 第二个数组中下标
int j = middle + 1;
// 用于记录在临时数组中存放的下标
int index = 0;
// 遍历两个数组,取出小的放入临时数组
while (i <= middle && j <= high) { // 条件为:因为两边元素个数可能不相同,一组循环完毕,另外一边没有,则跳出循环
// 第一个数组的数据更小
if ((arr[i] <= arr[j])) {
temp[index] = arr[i];
// 让下标向后移一位
i++;
} else {
temp[index] = arr[j];
j++;
}
index ++;
}
// 处理多余的数据
while (j <= high) {
temp[index] = arr[j];
j++;
index ++;
}
while (i <= middle) {
temp[index] = arr[i];
i++;
index ++;
}
// 把临时数组中的数据重新存入原数组
for (int k = 0; k < temp.length; k++) {
arr[k + low] = temp[k];
}
}
//由测试可以看出,直接写一个递归即可
public static void mergeSort(int[] arr,int low,int high) {
int middle=(low + high)/2;
if(low < high) { //注意写递归结束条件,不然无限循环会报错
//处理左边
mergeSort(arr, low, middle);
//处理右边
mergeSort(arr, middle + 1, high);
//归并
merge(arr, low, middle, high);
}
}
//测试
public static void main(String[] args) {
int[] arr = { 1, 3, 5, 2, 4, 6, 8, 10 };
// System.out.println(Arrays.toString(arr)); //测试
// merge(arr, 0, 2, arr.length - 1);
// System.out.println(Arrays.toString(arr));
//写了递归方法即可调用递归方法
mergeSort(arr, 0,arr.length-1);
System.out.println(Arrays.toString(arr)); //toString()方法返回字符串形式的
}
}
分配类
8、基数排序
1、按个位放入
按顺序取出(每个桶中先进先出)
2、按十位排
再取出
3、按百位排
取出
代码实现:
import java.util.Arrays;
/**
* 基数排序
*
* @author Mona
*
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[] {45,2,67,897,508,556,42,76,34};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr) {
//存储数组中的最大数字
int max = Integer.MIN_VALUE;
for(int i=0;i<arr.length;i++) {
if(arr[i] > max) {
max = arr[i];
}
}
//计算最大数字是几位数
int maxLength = (max + "").length();//给数字加一个空串,即变为字符串形式,轻易求出字符串长度(数字位数)
//用于临时存放数的数组
int[][] temp = new int[10][arr.length]; //-----10代表桶的个数--------
//用于记录再temp中相应的数组中存放的数字的数量
int[] counts = new int[10];
//根据最大长度的数决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10) {//设置n,是为了取模运算
//把每一个数字分别计算
for(int j = 0;j<arr.length;j++) {
//计算余数
int ys = arr[j]/n%10;
//把当前遍历的数据放入指定的数组中
temp[ys][counts[ys]] = arr[j];
//记录数量
counts[ys]++;
}
int index = 0;
for(int k=0;k<counts.length;k++) {
//记录数量的数组中当前余数记录的数量不位0
if(counts[k] != 0) {
//循环取出元素
for(int l = 0;l<counts[k];l++) {
//取出元素
arr[index] = temp[k][l];
index++;
}
//把数量置为0
counts[k] = 0;
}
}
if(i==0) { //遍历第一次的
for(int[] nums : temp) { //二维数组的遍历
System.out.println(Arrays.toString(nums));
}
}
}
}
}