十大排序算法
【前言】最近在重新研究算法,此篇博文供自己复习使用也为方便广大程序员同学!此文代码均为自己实现,通过对比经典解法校验,若有错请读者及时提出
//说明,为了方便统一方法,所以引进了一个基类,让子类去实现该方法.
public abstract class BaseSort {
protected void swap(int[] A, int i, int j) {
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
protected boolean isNullOrEmpty(int[] A){
return A==null||A.length==0;
}
public abstract void sort(int[] array);
}
1.冒泡排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//冒泡排序
//策略:每次遍历往上固定一个最小值
public class BubbleSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
boolean flag;//在每一趟的比较中的标志位,我们可以用来优化冒泡排序
//需要比较的趟数
for (int i = 0; i < array.length - 1; i++) {
flag = false;
//需要比较的次数,默认按照升序进行排序
for (int j = i; j < array.length; j++) {
if (array[i] > array[j]) {
flag = true;
swap(array, i, j);
}
}
if (!flag) {
break;
}
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
2.选择排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//选择排序
//每次挑选出一个最值(最小值即为升序,最大值即为降序)
public class SelectSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
int minTemp;
for (int i = 0; i < array.length-1; i++) {
minTemp=i;
//找到下标i开始后面的最小值
for (int j = i+1; j <array.length ; j++) {
if (array[minTemp]>array[j]){
minTemp=j;
}
}
//确保稳定排序,数值相等就不用交换
if(i!=minTemp){
swap(array,i,minTemp);
}
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
3.插入排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//插入排序
public class InsertSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
//用模拟插入扑克牌的思想
//插入的扑克牌
int i, j, temp;
//默认前面已经插入一张
for (i = 1; i < array.length; i++) {
//当前需要插入的值,然后进行插牌(将大于当前值的后移一位)
temp=array[i];
for ( j =i; j >0&&temp<array[j-1] ; j--) {
array[j]=array[j-1];
}
//将牌插入
array[j]=temp;
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
4.希尔排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//希尔排序是将待排序的数组按照摸一个增量d(n/2,n为需要排序的元素个数)分为若干组,每组中记录小标相差d
//对每组中全部元素进行插入排序,然后再用一个较小的增量(d/2)对它进行分组,每组中在进行直接插入排序.当增量
//减到1时,进行直接插入排序后,排序完成
//希尔排序算法(缩小增量法)属于插入排序类算法,将无序列分割成若干小的子序列分别进行插入排序.
public class ShellSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
int i, j, temp;
//设定增量d,增量d/2逐渐减小
for (int d = array.length/2; d >=1; d=d/2) {
//从下标d开始,对d组进行插入排序
for(i=d;i<array.length;i++){
temp=array[i];
for ( j = i; j>=d&&array[j-d]>temp ; j-=d) {
array[j]=array[j-d];
}
array[j]=temp;
}
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
5.归并排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//归并排序
//策略:将两个有序有序数组合并为一个有序数组
//将一个数组分解成一个个元素,那么可以将这些元素看做长度为1的有序数组,
//然后将这些子数组合并成一个有序数组
public class MergeSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
int[] tempArr = new int[array.length];
sort(array, tempArr, 0, array.length - 1);
}
private void sort(int[] array, int[] tempArr, int startIndex, int endIndex) {
if (startIndex == endIndex) return;
//中部坐标
int middleIndex = (startIndex + endIndex) / 2;
//分解
sort(array, tempArr, startIndex, middleIndex);
sort(array, tempArr, middleIndex + 1, endIndex);
//归并
merge(array, tempArr, startIndex, middleIndex, endIndex);
}
private void merge(int[] array, int[] tempArr, int startIndex, int middleIndex, int endIndex) {
//复制要合并的数据
for (int s = startIndex; s <= endIndex; s++) {
tempArr[s] = array[s];
}
int left = startIndex;//左边首位下标
int right = middleIndex + 1;//右边首位下标
// for (int k = startIndex; k <= endIndex; k++) {
// if(left > middleIndex){
// //如果左边的首位下标大于中部下标,证明左边的数据已经排完了。
// array[k] = tempArr[right++];
// } else if (right > endIndex){
// //如果右边的首位下标大于了数组长度,证明右边的数据已经排完了。
// array[k] = tempArr[left++];
// } else if (tempArr[right] < tempArr[left]){
// array[k] = tempArr[right++];//将右边的首位排入,然后右边的下标指针+1。
// } else {
// array[k] = tempArr[left++];//将左边的首位排入,然后左边的下标指针+1。
// }
// }
int k=startIndex;
while (left <= middleIndex && right <= endIndex){
if (tempArr[left]<tempArr[right]){
array[k++]=tempArr[left++];
}else{
array[k++]=tempArr[right++];
}
}
while (left<=middleIndex&&k<=endIndex){
array[k++]=tempArr[left++];
}
while (right<=endIndex&&k<=endIndex){
array[k++]=tempArr[right++];
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
6.快排
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//快速排序是对冒泡排序的一种改建,使用分治法策略见一个序列分为两个子序列
//步骤:从数列中跳出一个元素,称为枢轴
//重新排序数列,所有元素比枢轴小的摆放在基准前面,所有元素比枢轴大的摆放在枢轴后面(相同的数可以放到任意一边)
//在这个分区结束之后,该枢轴就处于中间位置,这个成为分区操作
//递归地把小于枢轴值元素的子数列和大于枢轴值元素的子数列排序
public class QuickSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
sort(array, 0, array.length - 1);
}
private void sort(int[] array, int startIndex, int endIndex) {
if (endIndex <= startIndex) return;
int pivotIndex = partition(array, startIndex, endIndex);
sort(array, startIndex, pivotIndex - 1);
sort(array, pivotIndex + 1, endIndex);
}
private int partition(int[] array, int startIndex, int endIndex) {
int pivot = array[startIndex];//取基准值
int mark = startIndex;//Mark初始化为起始下标
for (int i = startIndex + 1; i <= endIndex; i++) {
if (array[i] < pivot) {
//小于基准值 则mark+1,并交换位置。
mark++;
swap(array, mark, i);
}
}
//基准值与mark对应元素调换位置
array[startIndex] = array[mark];
array[mark] = pivot;
return mark;
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
7.堆排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
public class HeapSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
buildHeap(array, array.length);
for (int i = array.length - 1; i > 0; i--) {
//将堆顶元素与末位元素调换
swap(array, 0, i);
heapAdjust(array, 0, i);
}
}
//构建堆
//节点:index 左孩子:2*index+1 右孩子:2*index+2
private void buildHeap(int[] array, int length) {
for (int i = length / 2; i >= 0; i--) {
heapAdjust(array, i, length);
}
}
//调整堆(递归写法)
private void heapAdjust(int[] array, int index, int length) {
int leftChild = 2 * index + 1;//左子节点下标
int rightChild = 2 * index + 2;//右子节点下标
int present = index;//要调整的节点下标
//指向左孩子
if (leftChild < length && array[leftChild] > array[present]) {
present = leftChild;
}
//指向右孩子
if (rightChild < length && array[rightChild] > array[present]) {
present = rightChild;
}
//调整该节点及其子树
if (index!=present){
swap(array,index,present);
heapAdjust(array,present,length);
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
8.计数排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.Arrays;
//计数排序
//计数排序是基于一种肺比较的排序算法,我们之间的各种排序算法都是基于元素之间的比较来进行排序的,
//计数排序的时间复杂度是O(n+m)m指的是数据量,简单来说就是计数排序的时间复杂度为O(n),快于任何比较型的排序算法
//对于计数排序呢也存在相当多的bug
// 1)如果数组中存在负数,可以用偏移量来解决
public class CountSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
//找出数组中的最大值
int max=array[0];
for (int i = 1; i < array.length; i++) {
if (array[i]>max){
max=array[i];
}
}
//初始化计数数组
int[] countArray = new int[max + 1];
//计数
for (int i = 0; i < array.length; i++) {
countArray[array[i]]++;
}
int index=0;
for (int i = 0; i < countArray.length; i++) {
while (countArray[i]-->0){
array[index++] = i;
}
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4,11};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
9.桶排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
//桶排序可以看成是计数排序的升级版,他可以将排的数据分到多个有序的桶里,每个桶里的数据在单独排序,再把每个桶的数据依次取出,
// 再把每个每个桶的数据依次取出即可完成排序
public class BucketSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
//最大最小值
int max = array[0], min = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
//最大值和最小值的差
int diff = max - min;
//桶列表
ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
bucketList.add(new ArrayList<>());
}
//每个桶的存数区间
int section = diff / (array.length - 1);
//数据入桶
for (int i = 0; i < array.length; i++) {
//当前数除以区间得出存放桶的位置 减1后得出桶的下标
int num = (int) (array[i] / section) - 1;
if (num < 0) {
num = 0;
}
bucketList.get(num).add(array[i]);
}
//桶内排序
for (int i = 0; i < bucketList.size(); i++) {
//jdk的排序速度当然信得过
Collections.sort(bucketList.get(i));
}
//写入原数组
int index = 0;
for (ArrayList<Integer> arrayList : bucketList) {
for (int value : arrayList) {
array[index] = value;
index++;
}
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}
10.基数排序
package com.itheima.sort.impl;
import com.itheima.sort.BaseSort;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
//基数排序:是一种非比较的整数排序算法,其原理是将数据按位数切割成不同的数字,然后按每个位数分别比较
//假设说要对100万个数据进行排序,name应该选择什么排序算法呢,排的比较快的有归并与快排,计数排序和桶排序虽然更快一些,但是需要多少桶呢
//内存条表示不服,这个时候基数排序就是最好的选择
public class RadixSort extends BaseSort {
@Override
public void sort(int[] array) {
if (isNullOrEmpty(array)) return;
int max = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
//当前排序位置
int location = 1;
//桶列表
ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
//长度为10 装入余数0-9的数据
for (int i = 0; i < 10; i++) {
bucketList.add(new ArrayList());
}
while (true) {
//判断是否排完
int dd = (int) Math.pow(10, (location - 1));
if (max < dd) {
break;
}
//数据入桶
for (int i = 0; i < array.length; i++) {
//计算余数 放入相应的桶
int number = ((array[i] / dd) % 10);
bucketList.get(number).add(array[i]);
}
//写回数组
int index = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < bucketList.get(i).size(); j++) {
array[index++] = bucketList.get(i).get(j);
}
bucketList.get(i).clear();
}
location++;
}
}
@Test
public void testList() {
int[] ints = new int[]{11, 334, 64, 32, 667, 346, 478, 3, 1, 789, 453, 4};
sort(ints);
System.out.println("sort = " + Arrays.toString(ints));
}
}