工具类准备好
public class ArrayUtils
{
/**
* 打印 数组
* @param arr
*/
public static void print(int arr[])
{
System.out.print ("arr:[");
for(int ele:arr)
{
System.out.print (ele+" ");
}
System.out.print ("]\n");
}
/**
* 交换两个元素
* @param arr 数组
* @param aIndex 下表索引
* @param bIndex
*/
public static void swap(int arr[],int aIndex,int bIndex)
{
int temp = arr[aIndex];
arr[aIndex] = arr[bIndex];
arr[bIndex] = temp;
}
}
一. 冒泡排序算法
顾名思义: 思想在于两两比较交换
import util.ArrayUtils;
/**
* 冒泡排序
*/
public class MaoPaoSort
{
public static void main(String[] args)
{
int arr[]={5,3,1,4,2,8,6,9,8,7};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
/**
* 思想:冒泡排序 在于从前往后 两两比较,如果前面的大于后面 则交换
* 最终两两交换 只需要 length-1 趟即可
*/
for(int i=0;i<arr.length-1;i++)
{
//注意 i 只管的是 几趟
// 最终目的是把 最小值 排在最前面 以此往后推
//注意这里的 j>i 否则j和j-1 会造成 数组下标越界
for(int j=arr.length-1;j>i;j--)
{
if(arr[j-1]>arr[j])
{
ArrayUtils.swap (arr,j,j-1);
}
}
}
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
}
二. 选择排序算法
思想: 从后续剩余的数组中选择最小的,插入到前面已经排序好的数组中
/**
* 简单选择排序 //不稳定的排序算法
*/
public class SelectSort
{
public static void main(String[] args)
{
int arr[]={5,3,1,4,2,8,6,9,8,7};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
/**
* 思想: 就是每趟从剩余元素中选择一个最小值 ,然后与 前面指定的
* arr【i】交换,一共只需要 (length-1) 躺
*/
for(int i=0;i<arr.length-1;i++)
{
//int minIndex=i; //这个为每次查找到的最小值的下表
for(int j=i+1;j<arr.length;j++)
{
if(arr[i]>arr[j])
{
ArrayUtils.swap (arr,i,j);
}
}
}
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
}
三. 直接插入排序算法
思想:从甚于的数种,依次选择一个,插入到前面已经排序好的数组当中
/**
* 直接插入排序
*/
public class InsertSort
{
public static void main(String[] args)
{
int arr[]={5,3,1,4,2,8,6,9,8,7};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
//从第二个元素开始 依次往前插入排序
//所以这里的 i=1 开始
for(int i=1;i<arr.length;i++)
{
//这里代表 从i开始依次往前两两比较,如果不符合顺序,则交换
for(int j=i;j>0;j--)
{
if(arr[j-1]>arr[j])
{
ArrayUtils.swap (arr,j-1,j);
}
}
}
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
}
四. 希尔排序
内部其实就是 直接插入排序,只不过 我们把整个数组分为了 几个数组进行直接插入排序,最终 步长为一时,就时直接插入排序
/**
* 希尔排序
*/
public class ShellSort
{
public static void main(String[] args)
{
int arr[]={5,3,1,4,2,8,6,9,8,7};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
//这里的gap(步长) 相当于 希尔数组 d[5,3,1]最后一个元素必定要为1
for(int gap=arr.length/2;gap>0;gap/=2)
{
//(重点记忆)希尔排序内部 就是一个 插入排序
for(int i=1;i<arr.length;i++)
{
for(int j=i;j-gap>0;j-=gap) //这里跳着循环比较
{
if(arr[j-gap]>arr[j])
{
ArrayUtils.swap (arr,j-gap,j);
}
}
}
}
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
}
五.快速排序
思想在于:以数组第一个元素为分界线,左边都是小于它的,右边都是大于它的元素分割成两个数组,然后再分别以 数组第一个元素进行以上操作,最终达到排序目的。
**
* 第一种快速 排序,即 单轴排序
*/
public class QuickSortMain
{
//用于存放 关键字的
int key;
@Test
public void QuickSortTest()
{
int arr[]={5,3,1,4,2,10,6,9,8,7};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
quickSort(arr,0,arr.length-1);
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
private void quickSort(int[] arr, int low, int high)
{
if(low<high)
{
int index = partition (arr,low,high); //对第一个 关键字 进行一趟快速排序,返回他的下标所在位置
quickSort (arr,low,index-1); //对左边部分进行 快速排序
quickSort (arr,index+1,high); //对右边部分进行 快速排序
}
}
//分割
private int partition(int arr[],int low,int high)
{ //空间复杂度为 1
key = arr[low];
if(low < high)
{ //这里必须是while //注意 这里是把 key 和数组元素比较
while(low < high && key<=arr[high])
{
high--;
}
arr[low] = arr[high];
while(low < high && key>arr[low])
{
low++;
}
arr[high] = arr[low];
}
arr[low] = key;
return low;
}
}
六.二路归并排序
在于 两两归并,最终形成一个有序数组
public class MergeSortMain
{
//辅助数组,归并排序的空间复杂度为 n
public int temp[] ;
@Test
public void MyTest()
{
int arr[]={5,3,1,4,2,8,6,9,8,7};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
initUtilArray(arr.length);
MergeSort(arr,0,arr.length-1);
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
//递归遍历 采用二路归并
public void MergeSort(int arr[],int low,int high)
{
if(low<high)
{
int mid = (low + high) / 2;
MergeSort (arr,0,mid);
MergeSort (arr,mid+1,high);
Merge (arr,low,mid ,high);
}
}
//合并操作,作用是将两个有序的 序列合并成一个
public void Merge(int arr[],int low,int mid,int high)
{
//条件不符合时候
if(low>=mid || low>=high || mid>= high)
{
return;
}
int i = low; //用于指向最一个数组开始
int j= mid+1; //用于指向 第二个数组开始
int k = 0;//k用于 辅助数组下表 每趟merge 都要这般操作
//用于比较 两个有序的 数组
while(i <= mid && j <= high) //这里的 i<=mid 保证了 数据的稳定性
{
if(arr[i] <= arr[j])
{
temp[k] = arr[i];
k++;
i++;
}
else
{
temp[k] = arr[j];
k++;
j++;
}
}
//代表 第一个有序序列没排序完
while(i<=mid)
{
temp[k] = arr[i];
k++;
i++;
}
//代表 第二个有序序列没排序完
while(j<=high)
{
temp[k] = arr[j];
k++;
j++;
}
//最后一步 非常重要 将 在temp中排序好的数组 再次放入 原数组中 位置从 low 到 high
for (k=0,i=low;i<=high;i++,k++)
{
arr[i] = temp[k];
}
//清空辅助数组 都为0,因为每次都要有一个新的空的数组
freeUtilArr();
}
//用于初始化 辅助数组
public void initUtilArray(int lenght)
{
this.temp = new int[lenght];
}
//用于将数组中的元素 都清空 为0
public void freeUtilArr()
{
Arrays.fill (this.temp,0);
}
}
七.堆排序
public class HeadSort
{
@Test
public void HeadSortTest()
{
int arr[]={0,5,3,1,4,2,8,6,9,10,7};//第一个位置空着
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
headSortMain (arr,arr.length-1);
System.out.println ("排序后的数组!");
ArrayUtils.print (arr);
}
/**
* 堆排序的 算法
* @param arr
* @param n
*/
private void headSortMain(int arr[],int n)
{
int i=0;
//建立初始堆 依次 从最后一个父节点 向上调整
for(i=n/2;i>=1;i--)
{
sift (arr,i,n);
}
//这个循环 主要实现的就是 每次 把最后一个元素和 第一个元素交换 然后调用向下调整
for(i = n;i>=2;i--)
{
//把 最后一个元素 和 第一个交换位置
ArrayUtils.swap (arr,i,1);
//然后向下调整
sift (arr,1,i-1);
}
}
/**
* 本函数主要完成 从arr[low] 到 arr[high] 上的 在对arr[low] 上的堆向下调整
* @param arr
* @param low
* @param high
*/
private void sift(int arr[],int low,int high)
{
int i = low;
int j = 2 * i ; //这个是 左孩子
//如果 不是叶子节点 也就是所谓的 左孩子 就向下调整
while(j<=high)
{
//用于 比较左孩子 和 右孩子的 大小
if(j <high && arr[j] < arr[j+1])
{
j++;
}
//如果 父亲节点小于 孩子节点 ,就交换位置;
if(arr[low] < arr[j])
{
ArrayUtils.swap (arr,low,j);
i = j; //下边这两句话,是要继续向下调整
j = 2 * i ;
}
else //如果任何问题 都没有,就直接结束循环
{
break;
}
}
}
}
八.基数排序
/**
* 基数排序,多关键字排序
* 思想: 先 在个位数排序,然后 十位数排序,然后百位数排序 依次下去,直到最高位
*/
public class RadixSort
{
@Test
public void RadixSortTest()
{
int arr[]={423,123,685,1489,587,158,698,247,358};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
int result[] = sort(arr);
System.out.println ("排序后的数组!");
ArrayUtils.print (result);
}
/**
* 基数排序核心算法
* @param arr 传入要排序的数组
* @return 一个排序好的新数组
*/
private int[] sort(int[] arr)
{
int result[] = new int[arr.length];
//count用于保存 0 - 9的位数
int count[] = new int[10];
//division 就是最高位数
int maxPos = findMaxPosition (arr);
System.out.println ("最高位: " +maxPos);
//System.out.println ((int)Math.pow (10,2));
for (int i=0;i<maxPos;i++)
{
/**
* division 就是倍数(10的几次方) 如果是 10 0 ->1 10 1->100 10 3 -> 1000
*/
int division = (int)Math.pow(10,i);
//存入 计数数组 count
for(int j=0;j<arr.length;j++)
{
//这句话就是 取个位数,十位数 或者百位数上的数字
//比如 421 取十位数 -> 421 / 10 %10 =2
int num = arr[j]/division%10;
count[num]++;
}
//这个 跟计数数组一样 小技巧 可以 得到每个 数字的位置
for(int m=1; m <count.length;m++)
{
count[m] = count[m] + count[m-1];
}
//接下来操作就是 存放到result 当中 从后往前,可以保证 数据的稳定性
for(int n = arr.length-1;n>=0;n--)
{
//根据 个位 或者十位上的数字 查找 在当前的位置
int num = arr[n]/division%10;
int position =--count[num];//得到 这个数字 要存放在的下标索引
result[position] = arr[n]; //存进去
}
//把一次循环完的(比如个位数排序好的数字 再次放入 arr 中 进行十位数的比较)
System.arraycopy (result,0,arr,0,arr.length);
//把计数 数组 count 清空,用于下次 十位数字比较
Arrays.fill (count,0);
}
return result;
}
//查找 一组关键字的 最高位数
//比如123 是三位 5689 4位等等...
private int findMaxPosition(int arr[])
{
int maxPosition = 0;
for(int i=0;i<arr.length;i++)
{
//这里每步 都要初始化 j;
int j=10;
int position = 1;
while(arr[i]/j != 0)
{
position++;
j *=10;
}
if(position > maxPosition)
{
maxPosition = position;
}
}
return maxPosition;
}
}
九.计数排序
用于统计 比如考研成绩人数统计
9.1 不稳定的计数排序
/**
* 计数排序
* 用处:比如高考成绩排序 0 到 750 人多 基数大 但是范围小
* 思想: 把每个成绩 的个数统计在一个数组中,然后 最终按个数 插入新数组中 返回
*
* 空间复杂度 n+k
*/
public class CountSort
{
@Test
public void CountSortTest()
{
int arr[]={2,4,2,3,7,1,1,0,0,5,6,9,8,5,7,4,0,9};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
int result[] = sort(arr);
System.out.println ("排序后的数组!");
ArrayUtils.print (result);
}
//计数排序 核心函数
private int[] sort(int[] arr)
{
int result[] = new int[arr.length];
int count[] = new int[10]; //统计的个数在 0 -9 之间
//统计每种元素的个数 放入count中
for(int i=0;i<arr.length;i++)
{
count[arr[i]]++;
}
//开始返回
for(int i=0,j=0;i<count.length;i++)
{
while(count[i]-- >0)
{
result[j] = i;
j++;
}
}
return result;
}
}
9.2稳定的计数排序
public class CountSort稳定的
{
@Test
public void CountSortTest01()
{
int arr[]={2,4,2,3,7,1,1,0,0,5,6,9,8,5,7,4,0,9};
System.out.println ("排序前的数组!");
ArrayUtils.print (arr);
int result[] = sort(arr);
System.out.println ("排序前的数组!");
ArrayUtils.print (result);
}
//计数排序 核心函数
private int[] sort(int[] arr)
{
int result[] = new int[arr.length];
int count[] = new int[10]; //统计的个数在 0 -9 之间
//统计每种元素的个数 放入count中
for(int i=0;i<arr.length;i++)
{
count[arr[i]]++;
}
System.out.println ("修改前的count数组");
ArrayUtils.print (count);
// 改进的计数排序
//比如之前的count数组为 arr:[3 2 2 1 2 2 1 2 1 2 ]
//现在变为[3 5 7 8 10 12 13 15 16 18 ]代表着每种元素 下标的最后一个位置
// 这样可以 从后循环遍历 arr数组,然后 一个一个添加,然后实现稳定性
for(int i=1;i<count.length;i++)
{
count[i] = count[i]+count[i-1];
}
System.out.println ("修改后的count数组");
ArrayUtils.print (count);
//实现稳定性插入 到result数组中
for (int i=arr.length-1;i>=0;i--)
{
//arr[i] 如果是 2 那么count[arr[i]] 就是在count中查找 2元素的最后一个位置
//然后 count[arr[i]]-1 因为2元素 要插入 的 下标最后一个位置
result[count[arr[i]]-1] = arr[i];
count[arr[i]]--;
}
return result;
}
}