一、插入排序
1.直接插入排序
public class InsertionSort {
/**
* 直接插入排序:把待排序的序列,按其数据的大小逐个插入到前面已经排好序的有序序列中,直到所有的数据插入完为止,得到
* 一个新的有序序列
* @param arr
*/
//以升序为例
public static void insertSort(int[] arr){
if(arr==null){
return;
}
int temp=0;
int i=0;
int j=0;
for ( i = 1; i < arr.length ; i++) {//与前面有序序列比较,找位置插入,从第二个数开始往前插入,因为第一个数肯定有序.
for ( j = 0; j < i; j++) {//遍历比较这个数前面的有序序列.
if(arr[i]<arr[j]){
break;
}
}
//判断要插入,即提前结束循环了.
if(j<i){
//此时 j 下标为被插入位置,所以从 j 开始往后移一位.
temp=arr[i];//记录要插入的值,因为后移时会被覆盖.
for (int k = i; k > j ; k--) {//后移:从 i 位置开始,逐个被前一个值覆盖.
arr[k]=arr[k-1];
}
arr[j]=temp;
}
}
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{2,1,-3,32,6,4,33,10,-2,-1,5};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
}
2.希尔排序
public class ShellSort {
/**
* 希尔排序(缩小增量排序 )其基本思想是:把记录按步长 gap(差距,间隙)分组,对每组记录采用直接插入排序方法进行排序。
* 随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,则完成排序.
* 对直接插入排序的优化:前面是预排序,让数据先逐渐趋于有序,步长为 1 时,就是全部数据排序.
* (少量的数据趋于有序-->大量的数据趋于有序-->全部数据有序)
* @param arr
*/
public static void shellSort(int[] arr){
if(arr==null){
return;
}
//一般初始化步长为数组长度的一半
int gap = arr.length / 2;
//步长逐渐减小
while(gap>=1){
//步长确定后,可知道步长为多少,就会分成多少组,
for(int n=0 ;n<gap;n++){//每组分开进行排序,从第一组开始.
//把插入排序的移动一位,改为移动 gap 位.
int temp=0;
int i=0;
int j=0;
for ( i = n+gap; i < arr.length ; i+=gap) {//与前面有序序列比较,找位置插入,从第二个数开始往前插入,因为第一个数肯定有序.
for ( j = n; j < i; j+=gap) {//遍历比较这个数前面的有序序列.
if(arr[i]<arr[j]){
break;
}
}
//判断要插入,即提前结束循环了.
if(j<i){
//此时 j 下标为被插入位置,所以从 j 开始往后移一个 gap 位.
temp=arr[i];//记录要插入的值,因为后移时会被覆盖.
for (int k = i; k > j ; k-=gap) {//后移:从 i 位置开始,逐个被前一个值覆盖.
arr[k]=arr[k-gap];
}
arr[j]=temp;
}
}
}
gap/=2;//调整步长/2.
}
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{4,1,-3,342,6,4,33,10,-2,-1,5};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
}
二、选择排序
1.直接选择排序
public static void selectionSort(int[] arr) {
if(arr==null){
return;
}
int temp=0;
int maxIndex=0;//最大值下标,每次先假设为0,则遍历从 1 开始.
int times=0;//遍历次数
for(times=0;times<arr.length-1;times++){
for (int i = 1; i < arr.length-times; i++) { //arr.length-times-1 是最后一位
if(arr[i]>arr[maxIndex]){
maxIndex=i;
}
}
if(maxIndex!=arr.length-times-1){//(如果不是最后一位)与最后一位交换
temp=arr[maxIndex];
arr[maxIndex]=arr[arr.length-times-1];
arr[arr.length-times-1]=temp;
}
maxIndex=0;//最大值下标重新假设为0
}
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{4,1,-3,32,6,4,33,10,-2,-1,5};
selectionSort(arr);
System.out.println(Arrays.toString(arr));
}
}
2.堆排序
public class Heapsort {
/**
* 堆排序:升序 -->建大根堆,降序-->建小根堆.
* 先建堆,然后进行堆删除.其中都涉及向下调整.
* @param arr
*/
public static void heapSort(int[] arr){
//先建堆,以升序为例,建大根堆.
//建堆:找倒数第一个非叶子节点(即是倒数第二层的最后一个非叶子节点),从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整.
for (int i = (arr.length-1-1)>>2; i>=0; i--) {
shiftDown(arr,i,arr.length);
}
//然后进行堆删除
//堆删除:把根节点与最后一个节点交换,将新的根节点向下调整.(向下调整时要忽略已经换到后面的数)
//每删除(交换)一次就会排序好一个数,所以排序 n 个数 要遍历 n-1 次.
int len=arr.length;
int temp=0;
for (int i = 0; i < arr.length-1; i++) {//次数
//交换首尾
temp=arr[0];
arr[0]=arr[len-1];
arr[len-1]=temp;
//向下调整
len--;// 忽略已经换到后面的数
shiftDown(arr,0,len);
}
}
/**
* 向下调整,调整为大根堆.
*/
public static void shiftDown(int[] arr,int parent,int len){
if(arr==null){
return;
}
int temp=0;
int child=2*parent+1;//首先得有左孩子
while(child<len){
//判断有无右孩子及比较左右孩子大小
if(child+1<len && arr[child+1]>arr[child]){
//如果有右孩子且比左孩子大
child++;//则更新成右孩子.
}
//然后比较父节点与孩子节点
if(arr[child]>arr[parent]){
//交换
temp=arr[child];
arr[child]=arr[parent];
arr[parent]=temp;
//把交换了的孩子节点作为新的父节点向下调整.
parent=child;
child=2*parent+1;
}
else{
//不用交换则已经是堆结构
break;
}
}
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{4,1,-3,32,6,4,33,10,-2,-1,5};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}
三、交换排序
1.冒泡排序
public class BubbleSort {
/**
* 冒泡排序:依次往后比较两两相邻的数字,反序则交换,用双重循环来实现。
* 以升序为例,每一次比较都会让所有较小的数字往前冒一位,
* 所以最坏的情况就是最小的数字在最后一位,要把它冒到第一位,则最多要两两相邻比较 arr.length-1次.
*而且每比较一次都会让一个较大的数字沉底,则后面数据会有序无需参与比较.
* 另外,如果在一次两两相邻比较过程中没有发生交换,则表示数据已经处于正序,停止循环。
*/
public static void bubbleSort(int[] arr){
if(arr==null){
return;
}
int temp=0;
boolean flag=true;//做标记,没有发生交换,则停止循环.
for (int i = 0; i < arr.length-1 && flag; i++) {//往后两两相邻比较次数
flag=false;
for(int j=0;j<arr.length-i-1;j++){//比较
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=true;
}
}
}
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{4,1,-3,32,6,4,33,10,-2,-1,5};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
}
2.快速排序
public class QuickSort {
/**
* 快速排序
* 基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,
* 左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值.(以升序为例)
* 然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止
* @param arr
* @param left
* @param right
*/
//递归,利用左右双指针
public static void quickSort(int[] arr, int left, int right){
if(arr==null){
return;
}
//递归终止条件
if(left>=right){
return;
}
int base=arr[left]; //选定最左边为基准值
int l=left;
int r=right;
//比较和移动数据,一次循环是右左交替移动
while(l<r){
//右
while( l<r && arr[r]>=base){
r--;
}
if(l==r){//判断上面循环是否是因为l==r而停止,是则终止整个大的循环,不是则遇到了要移动的数据.
break;
}
arr[l]=arr[r];
//左
while( l<r && arr[l]<base){
l++;
}
if(l==r){//判断上面循环是否是因为l==r而停止,是则终止整个大的循环,不是则遇到了要移动的数据.
break;
}
arr[r]=arr[l];
}
arr[l]=base;//此时l==r 就是基准值的位置
//左边递归
quickSort(arr,left,l-1);
//右边递归
quickSort(arr,l+1,right);
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{4,0,-3,342,6,4,23,10,-2,-1};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
四、归并排序
public class MergeSort {
/**
*
* 以升序为例
*/
//合并两两相邻的被分段数列
public static void mergeSort(int[] arr,int f,int mid,int s){
//f--mid 为第一段,mid+1--s 为第二段.
int i=f;
int j=mid+1;
int k=0;
//用一个新数组放置合并的新数据
int[] arr2= new int[s-f+1];
//比较合并两段数据
while(i<=mid && j<=s){
if(arr[i]<=arr[j]){
arr2[k]=arr[i];
k++;
i++;
}
else {
arr2[k]=arr[j];
k++;
j++;
}
}
//如果其中一段没放完
while(i<=mid){
arr2[k]=arr[i];
k++;
i++;
}
while(j<=s){
arr2[k]=arr[j];
k++;
j++;
}
//将数据覆盖原来数组下标位置.
k=0;
for (int l = f; l <=s; l++) {
arr[l]=arr2[k];
k++;
}
}
//分段
public static void mergeDiv(int[] arr,int gap){
//从 0 下标开始,到gap-1为第一段,gap 到 2gap-1 为第二段,依次往后...
int i;
for (i = 0; i+2*gap-1<arr.length; i+=(2*gap)) {//每次都要分个数相等的两段过去合并
mergeSort(arr,i,i+gap-1,i+2*gap-1);
}
//上面循环终止,后面剩下不足以分成两段相等的数据
if(arr.length-1>(i+gap-1) && i+2*gap-1>arr.length-1 ){//判断后面数据:剩下一段+不足一段
mergeSort(arr,i,i+gap-1,arr.length-1);
}
}
/**
* 自下而上的归并(可以看作已经分解好了,向上合并即可):(从每段为 1 个数开始)不断地每两段合并成一段往上合并
* (我这里按个数相等的两段去合并)
* (要处理一个问题即后面剩下不足以分成两段相等的数据:
* 只剩下不足一段(上一步长时合并成的,有序),或刚好一段,它是有序的,不用操作.
* 剩下一段+不足一段也要合并)
* @param arr
*/
public static void sort(int[] arr){
if(arr==null){
return;
}
//让数组分段
for(int gap=1;gap<arr.length;gap*=2){//让步长从 1 开始,逐步乘以 2,而且步长不能大于数组长度.
mergeDiv(arr,gap);
}
}
/**
* 递归的归并:
* 将数组不断往下对半分,分到只剩下两个开始往上合并
* @param arr
*/
public static void sort2(int[] arr,int left,int right){
if(arr==null){
return;
}
//递归结束条件
if(left==right){
return;
}
//对半拆分
int l=left;
int m=(left+right)/2;
int r=right;
//递归,拆分成左右两边,左右两边再继续分别拆分
sort2(arr,l,m);//左边
sort2(arr,m+1,r);//右边
//合并
mergeSort(arr,l,m,r);
}
public static void main(String[] args) {
//待排序数组
int[] arr=new int[]{4,1,-3,32,6,4,5312,10,-2,-1,5};
int[] arr2=new int[]{4,6,2,52,3,3,5,6,234,0,7};
//sort(arr);//自下而上的归并
sort2(arr,0,arr2.length-1);
System.out.println(Arrays.toString(arr));
}
}