需要先了解各排序算法的原理
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)
冒泡排序
/**
* TODO
* 冒泡排序
* 相邻两个比较,重复多轮
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[]{4,5,1,7,2,9,11};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr){
//控制共比较多少轮
for(int i=0;i<arr.length-1;i++){
//控制比较的次数
for(int j=0;j<arr.length-1-i;j++){
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
快速排序
/**
* TODO
* 快速排序
* 选取一个中间数,数据一分为二,小的在左,大的在右(无序)
* 过程:右->左,跟第一个比较,大就不动下标 j 前移一位
* 小就跟第一个交换位置,同时第一个下标 i 后移一位。
* 最后 i,j 重复的位置放第一个数
* 下一轮:左右各自以第一个数为比较单位,递归操作,直到剩下一个(i,j 重合)
* 参数初始传入:arr,0,arr.length-1
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[]{8,4,5,1,7,2,9,11};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr,int start,int end){
//直到剩下一个元素 start = end
if(start < end){
//把数组中的第 0 个数字作为标准数
int stard = arr[start];
//记录需要排序的下标
int low = start;
int high = end;
//循环找比标准数大的数和比标准数小的数
while (low < high){
//左右的比较是轮流的
//右边的数字比标准数大
while (low<high && stard<=arr[high]){
//下标往前移动
high--;
}
//使用右边的数字替换左边的数
//即将当前这轮的标准数替换掉
arr[low] = arr[high];
//如果左边的数字比标准数小
while (low<high && arr[low]<=stard){
//下标往后移动
low++;
}
//使用左边的数字替换右边的数字
//即将前面替换标准数的小值所在位置替换成新的大值
arr[high] = arr[low];
}
//low 跟 high 已重合
//把标准数赋值给低位所在位置的元素
//这一步已经把数组分成两部分:大于标准数 和 小于标准数
//arr[low] = arr[high]
arr[low] = stard;
//递归处理所有小的数字
quickSort(arr,start,low);
//递归处理所有大的数字
quickSort(arr,low+1,end);
}
}
}
插入排序
/**
* TODO
* 插入排序
* 下标移动到哪排序到哪,即下标所到之处前面都是排序好的
* 每次用下标所指元素跟前面的一个一个比较,大的后移直到遇到比他小/无 就停止并把进行判断的元素赋值给该位置的后一个位置
* 下标从第二个开始就好了,因为默认下标前面都是有序的,第二个开始排序比较
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = new int[]{8,12,4,5,1,7,2,9,11};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertSort(int[] arr){
//遍历所有数字
for(int i=1;i< arr.length;i++){
//如果当前数字比前一个数字小
if(arr[i]<arr[i-1]){
//把当前遍历的数字存起来
int temp = arr[i];
int j;
//遍历当前数字前面所有的数字
for(j=i-1;j>=0&&temp<arr[j];j--){
//把前一个数字赋给后一个数字
//即元素后移
arr[j+1] = arr[j];
}
//把临时变量赋给不满足条件的后一个元素
arr[j+1] = temp;
}
}
}
}
希尔排序
/**
* TODO
* 希尔排序
* 使用**步长**:相隔 步长(total/2) 进行比较,每轮 步长/2
* 通过步长将数据分成几组分别比较,最后组合起来
* 本质:插入排序
* 1.确定步长
* 2.根据步长分组
* 3.插入排序
* 4.递归前面步骤 直至步长为0
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class ShellSort {
public static void main(String[] args) {
int[] arr = new int[]{8,12,3,5,1,7,2,9,11};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 遍历所有元素时,i = d 第一次肯定是中间那个,每次 i++ 实际上遍历新的一组元素(内部循环遍历是 步长来 递减 比较)
* 遍历本组第一次 i-d 的 值 是第一个元素,即表示从第一个元素(本组)开始遍历本组
* 本组元素一个循环内比较的是 **左->右**(插入排序)即将最新的一个跟前面排序好的比较
* @param array
*/
public static void shellSort(int[] array){
//遍历所有的步长
for(int d=array.length/2;d>0;d/=2){
//遍历所有组
for(int i=d;i<array.length;i++){
//遍历本组中所有的元素
for(int j=i-d;j>=0;j-=d){
//如果当前元素大于加上步长后的那个元素
if(array[j]>array[j+d]){
int temp = array[j];
array[j] = array[j+d];
array[j+d] = temp;
}
}
}
}
}
}
选择排序
/**
* TODO
* 选择排序
* 每次只把未排序的元素中的最小/最大值 挑选出来放到最前面下标前移一位
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[]{8,12,3,5,1,8,8,2,9,11};
selectSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void selectSort(int[] arr){
//遍历所有的元素
for(int i=0;i<arr.length;i++){
int minIndex = i;
//把当前遍历的数和后面所有的数一次进行比较,并记录最小的数的下标
for(int j=i+1;j<arr.length;j++){
//如果后面比较的数比记录的最小数的下标
if(arr[minIndex] > arr[j]){
//记录下最小的那个数的下标
minIndex = j;
}
}
//如果最小的数和当前遍历的数的下标不一致
//说明下标为minIndex的数比当前遍历的数更小
//将下标为minIndex的数换到 ”最前”
if(i!=minIndex){
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
}
归并排序
/**
* TODO
* 归并排序
* 适合于数据本身元素 **部分有序**
* 递归调用。需要用到 额外数组 装载比较结果
* 拆成多组,从各自最小的开始比较,小的放进数组
* 自动处理 middle参数 && 递归处理直到 一组中只有一个元素
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = new int[]{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));
System.out.println(Arrays.toString(arr));
mergeSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
//递归处理
public static void mergeSort(int[] arr,int low,int high){
int middle=(high+low)/2;
//直到某组中只有一个元素,停止递归
if(low<high){
//处理左边
mergeSort(arr,low,middle);
//处理右边
mergeSort(arr,middle+1,high);
//归并
merge(arr,low,middle,high);
}
}
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];
}
}
}
基数排序
/**
* TODO
* 基数排序
* 先通过个位数大小按顺序排序,形成新数据
* 再通过十位数大小按顺序排列,形成新数据,以此类推
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[]{73,22,93,43,55,14,28,65,39,81};
radixSort2(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];
//用于记录在temp中相应数组中存放的数字的数量
int[] counts = new int[10];
//根据最大长度的数决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10){
//把每一个数字分别计算余数
for(int j=0;j<arr.length;j++){
//计算余数:个->十->百->千...
int ys = arr[j]/n%10;
//把当前遍历的数据放入指定的数组中
temp[ys][counts[ys]] = arr[j];
//记录数量,每添加一个(余数相同)记录数+1
//用于判断该放入二维数组哪个位置
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;
}
}
}
}
//使用优先队列优化
public static void radixSort2(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();
//用于临时存储数据的队列的数组
MyQueue[] temp = new MyQueue[10];
//为队列数组赋值
for(int i=0;i<temp.length;i++){
temp[i] = new MyQueue();
}
//根据最大长度决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10){
//把每一个数字分别计算余数
for(int j=0;j<arr.length;j++){
//计算余数
int ys = arr[j]/n%10;
//把当前遍历的数据放入指定的队列中
temp[ys].add(arr[j]);
}
//记录取的元素需要放的位置
int index = 0;
//把所有队列中的数字取出来
for(int k=0;k<temp.length;k++){
//循环取出元素
while (!temp[k].isEmpty()){
//取出元素
arr[index] = temp[k].poll();
//记录下一个位置
index++;
}
}
}
}
}
需要的队列类
/**
* TODO
* 队列的实现
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class MyQueue {
int[] elements;
public MyQueue(){
elements = new int[0];
}
//入队
public void add(int element){
int[] newArr = new int[elements.length+1];
//把原数组的元素复制到新数组
for(int i=0;i<elements.length;i++){
newArr[i] = elements[i];
}
//把添加的元素放入新数组中
newArr[elements.length] = element;
//新数组替换旧数组
elements = newArr;
}
//出队-第一个元素
public int poll(){
int element = elements[0];
int[] newArr = new int[elements.length-1];
for(int i=0;i<newArr.length;i++){
//除去原数组第一个元素
newArr[i] = elements[i+1];
}
//替换数组
elements = newArr;
return element;
}
public boolean isEmpty(){
return elements.length==0;
}
}
堆排序
/**
* TODO
* 堆排序的实现
* 1.构建最大堆/最小堆
* 2.进行堆排序
* 升序排序使用大顶堆
* 降序排序使用小顶堆
* @author Redamancy
* @version 1.0
* @since jdk 1.8
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[]{9,6,8,7,0,1,10,4,2};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 开始堆排序
* @param 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;
//堆顶最大值交换到最后一位之后就不去动他
//故数组不变,i每次向前移动一位,固定从堆顶开始重新构建大顶堆
maxHeap(arr,i,0);
}
}
/**
* 构建大顶堆
* @param arr 使用的数组
* @param size 需要构建的长度
* @param index 从哪一个节点开始
*/
public static void maxHeap(int[] arr,int size,int 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);
}
}
}