插入排序
原理:从数组第二个元素开始,依次与第一个元素到当前元素的前一个元素比较,直到找到合适位置,移动、插入。
public static void insertSort(int[] arr){
//用于交换的临时变量
int temp;
//从第二个元素起,依次从最左侧比较
for(int i=1;i<arr.length;i++){
for (int j=0;j<i;j++){
//找到插入位置
if(arr[i]<arr[j]){
temp=arr[i];
//从插入位置到该元素位置的前一个元素依次向后移动
while(i>j){
arr[i]=arr[i-1];
i-=1;
}
//插入
arr[i]=temp;
break;
}
}
}
}
希尔排序
也叫减小增量排序, 是对插入算法的优化,解决插入一个元素时,移动元素过多导致速度慢的问题。解决方法是(原理):根据增量把数组分为多个组,对每个分组进行插入排序。折半减小增量,直至为一,在数组基本有序的基础上最后再进行一次插入排序。
移位式:
//插入式希尔排序
public static void shellSort2(int[] arr){
//插入元素时使用的临时变量
int temp;
//循环折半减小增量,直至增量为1
for(int cap=arr.length/2;cap>0;cap/=2){
//对所有子数组进行插入排序
for(int i=0;i<cap;i++){
//对每个子数组进行插入排序,增量为cap
for(int j=i+cap;j<arr.length;j+=cap) {
//j用于循环,不能修改其值,因此用临时变量k
int k=j;
//从每个子数组的第二个元素开始比较插入
temp=arr[k];
while(k-cap>=0 && temp<arr[k-cap]){
//需要移动
arr[k]=arr[k-cap];
//继续向前比较
k-=cap;
}
//已经找到插入位置
arr[k]=temp;
}
//不需移动则直接排序下一个子数组
}
System.out.println("增量为"+cap+"排序后的子数组:"+Arrays.toString(arr));
}
}
交换式(效率比移位式低):
//交换式希尔排序
public static void shellSort1(int[] arr){
//用于交换的临时变量
int temp;
//循环折半减小增量,直到为1,并对子数组排序
for(int cap=arr.length/2;cap>0;cap/=2){
//对每个子数组进行交换排序
for(int i=0;i+cap<arr.length;i+=1){
for(int j=i;j>=0;j-=cap) {
if (arr[j] > arr[j + cap]) {
//交换元素值
temp = arr[j];
arr[j] = arr[j + cap];
arr[j + cap] = temp;
}
}
}
System.out.println("增量为"+cap+"排序后的子数组:"+Arrays.toString(arr));
}
}
选择排序
原理:n个数比较(n-1)轮,每轮求出一个最小或最大值,通常从前往后排序。
public static void selectSort(int[] arr){
//元素值交换时的临时变量
int temp;
//n个数,比较n-1次
for(int i=0;i<arr.length-1;i++){
//从arr[i]后一个元素比较到最后一个元素
for(int j=i+1;j<arr.length;j++){
//找到新的最小元素,值交换
if(arr[i]>arr[j]){
temp=arr[j];
arr[j]=arr[i];
arr[i]=temp;
}
}
}
}
冒泡排序
原理:两两元素相比较,每轮求出一个最小或最大值,通常从后往前排序。
public static void bubbleSort(int[] arr){
//值交换时的临时变量
int temp;
//n个数比较n-1次
for(int i=0;i<arr.length-1;i++){
//两两元素相比较
for(int j=0;j+1<arr.length-i;j++){
//升序值大的往后“沉”
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
快速排序
原理:每轮比较选取一个中间值,使其左侧元素都不大于它,右侧元素都不小于它,再分别向左右递归快速排序,直到左右元素个数都为1。
/*
快速排序
arr:待排序数组
start:排序起始下标
end:排序结束下标
*/
public static void quickSort(int[] arr,int start,int end){
//左下标
int left=start;
//右下标
int right=end;
//值交换时的中间变量
int temp;
//选取中间元素
int midValue=arr[(start+end)/2];
//使中间元素左侧的值都小于等于它,右侧的值都大于等于它
while(left<right) {
//在中间元素左侧找小于它的
while (arr[left]<midValue){
left+=1;
}
//在中间元素右侧找大于它的
while (arr[right]>midValue){
right--;
}
//判断左右下标的关系
if(left>=right){
//说明满足条件
break;
}
//否则左右元素进行值交换
temp=arr[left];
arr[left]=arr[right];
arr[right]=temp;
//若交换后左侧元素与中间元素相等,右下标向前取,找不大于中间元素的元素进行值交换
if(arr[left]==midValue){
right-=1;
}
//若交换后右侧元素与中间元素相等,左下标向后取,找不小于中间元素的元素进行值交换
if(arr[right]==midValue){
left+=1;
}
}
//若左右下标相等,防止左右递归时包含中间元素
if(left==right){
left+=1;
right-=1;
}
//若中间元素左侧元素个数大于1个,需要排序
if(start<right) {
//向左递归快速排序
quickSort(arr,start,right);
}
//若中间元素右侧元素个数大于1个,需要排序
if(end>left){
//向右递归快速排序
quickSort(arr,left,end);
}
}
归并排序
原理:将数组递归分,直到元素个数为1,再合并为有序数组。
//递归归并排序
public static void mergeSortRecursion(int[] arr){
if(arr.length<1){
System.out.println("数组为空!");
return;
}else if(arr.length==1){
System.out.println("分:"+Arrays.toString(arr));
return;
}else{
//向左递归分
System.out.println("分:"+Arrays.toString(arr));
int[] leftArr= Arrays.copyOfRange(arr,0,arr.length/2);
mergeSortRecursion(leftArr);
//向右递归分
int[] rightArr=Arrays.copyOfRange(arr,arr.length/2,arr.length);
mergeSortRecursion(rightArr);
//用于合的临时数组
//int[] tempArr=new int[leftArr.length+rightArr.length];
//将两个有序的数组合并成一个有序的数组
for(int i=0,j=0,k=0;k<arr.length;k++){
if(i<leftArr.length && j<rightArr.length) {
//两个数组都没有比到最后一个元素时
if (leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
}else {
//某个数组已经比较完毕
if(i<leftArr.length){
arr[k]=leftArr[i];
i++;
}else {
arr[k]=rightArr[j];
j++;
}
}
}
System.out.println("合:"+Arrays.toString(arr));
}
}
基数排序
原理:稳定排序(相同元素,排序后的顺序与排序前的顺序一致)。从个位到最大元素的最高位依次排序,把元素放入对应的一维数组中,再用一个一维数组记录每轮排序中,每个一维数组中的有效数字,直至最高位排序结束后,依次读取每个一维数组中的有效数字。
//基数排序
public static void radixSort(int[] arr){
//用于存储待排序数组中元素的二维数组
int[][] bucket=new int[10][arr.length];
//用于记录每轮排序后,bucket中每个一维数组中的有效元素个数
int[] validElementsCounts=new int[10];
//从个位开始提取数值进行比较,加入bucket中对应索引的一维数组中,直到最高位
for (int i = 10; getMax(arr)/(i/10) != 0 ; i*=10) {
//每轮排序前初始化validElementsCounts
for (int j = 0; j < 10; j++) {
validElementsCounts[j]=0;
}
//遍历待排序数组,并加入对应的一维数组中
for (int j = 0; j < arr.length; j++) {
//元素应该加入的一维数组在bucket中的下标(一个元素数值的个位、十位、百位......)
int index=arr[j] % i / (i/10);
//把当前元素加入到一维数组中
bucket[index][validElementsCounts[index]]=arr[j];
//该一维数组的有效数字加一
validElementsCounts[index]=validElementsCounts[index]+1;
}
//把每一轮的排序结果赋值给原数组
for (int j = 0,l = 0; j < 10; j++) {
for (int k = 0; k < validElementsCounts[j]; k++) {
arr[l]=bucket[j][k];
l++;
}
}
//输出此轮排序后的数组
System.out.println(Arrays.toString(arr));
}
}
//返回数组中最大值
public static int getMax(int[] arr){
int max=arr[0];
for (int i = 1; i < arr.length; i++) {
if(max<arr[i]){
max=arr[i];
}
}
return max;
}
堆排序
原理:构建大顶堆或小顶堆,交换根节点与数组最后一个元素。重复构建、交换,直至数组有序。
//堆排序
public static void heapSort(int[] arr){
int temp;//交换时的临时节点
/*
首次构建大顶堆:
首次构建大顶堆是从最后一个非叶子节点逐级向上构建,直至根节点。
构建完成后每轮交换只改变大顶堆根节点的值。
根节点以下还是排好序的大顶堆,因此不需要从最后一个非叶子节点重新逐级构造。
只需要从根节点开始构造,提高排序速度。
*/
for (int j = arr.length/2-1 ; j >= 0; j-- ) {
adjustHeap(arr,j,arr.length);
}
//每次构建完成的大顶堆的根节点与数组最后一个元素交换
for (int i = arr.length-1; i > 0; i--) {
temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
adjustHeap(arr,0,i);//不是首次构建大顶堆只需要从根节点开始构建,因为根节点以下已经是排好序的大顶堆了
}
}
//将以下标为i的元素做为根节点的子树(局部数组)构建成大顶堆
/*
arr:待调整数组
i:非叶子节点在数组中的索引
length:数组中需要构建大顶堆的元素个数
*/
public static void adjustHeap(int[] arr,int i,int length){
//取出当前元素的值,保存在临时变量中
int temp=arr[i];
//k表示下标为i的节点的左子节点的下标
for (int k = 2*i+1; k < length; k=2*i+1) {
if(k+1<length && arr[k]<arr[k+1]){//存在右子节点,且左子节点小于右子节点
k++;//k变为右子节点的下标
}
if(arr[k]>temp){//如果子节点大于父节点
//交换父节点与子节点的值,保证父节点为最大值
arr[i]=arr[k];
arr[k]=temp;
i=k;//i指向k,继续循环比较,因为虽然下面的子树是排好序的,但是此处的改变可能会导致其子树也需更改
}else {
/*
堆排序构建堆的方式是:从左往右、从下往上构建堆。
如果以当前节点(arr[i])作为根节点的子树不许要改变,
则直接退出循环,因为下面的子树都是已经排好序的
*/
break;
}
}
}