八种排序算法的代码
**
参考原文连接:https://www.cnblogs.com/l199616j/p/10742603.html
1.冒泡排序
package PaiXuSuanFa;
import java.util.Arrays;
import java.util.Scanner;
public class MaoPao {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String [] num = s.split(",");
int [] n = new int[num.length];
for (int i = 0; i <n.length ; i++) {
n[i] = Integer.parseInt(num[i]);
}
int temp;
for (int i = 0; i <n.length-1 ; i++) {
for (int j = 0; j <n.length-1 ; j++) {
if(n[j]>n[j+1]){
temp = n[j];
n[j] = n[j+1];
n[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(n));
}
}
2.选择排序
1.第一个数跟后面的所有数相比,如果小于第一个数的时候,暂存较小数的下标,第一趟结束后,将第一个数与暂存的那个最小数进行交换,第一个数就是最小的数。
2.下标移动到第二位,第二个数跟后面的所有数相比,一趟下来,确定第二小的数;
重复以上步骤
知道指针移动到倒数第二位,确定倒数第二小的数,那么最后一位也就确定了,排序完成。
package PaiXuSuanFa;
import java.util.Arrays;
import java.util.Scanner;
public class Choose_Sort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String [] num = s.split(",");
int [] n = new int[num.length];
for (int i = 0; i <n.length ; i++) {
n[i] = Integer.parseInt(num[i]);
}
int temp,index;
for (int i = 0; i < n.length-1 ; i++) {
index = i; //定义一个临时变量用来存放每一循环比较的最小值
//如果大于,暂存较小的数的下标
for (int j = i+1; j <n.length ; j++) {
if(n[index] >n[j]){
index = j;//这里将遍历i后续的数,首先将i的值赋给index,如果后续有小于i的值,将j的值赋予index,更新index再进行比较
}
}
//将一趟下来求出的最小数,与最开始比较的数也就是i进行交换
if(index>0){
temp = n[i];
n[i]= n[index];
n[index] = temp;
}
System.out.println(Arrays.toString(n));//此处打印的是每次循环结束后n数组的值
}
System.out.println(Arrays.toString(n));
}
}
3.直接插入排序
例如从小到大排序:
1.从第二位开始遍历;
2.当前数(第一趟是第二位数)与前面的数依次比较,如果前面的数大于当前数,则将这个数放在当前数的位置上,当前数的下标-1;
3.重复以上步骤,直到当前数不大于前面的某一个数为止,这时,将当前数,放到这个位置上,
4.重复以上3步,直到遍历到最后一位数,并将最后一位数插入到合适的位置,插入排序结束。
1.时间复杂度:插入算法,就是保证前面的序列都是有序的,只需要把当前数插入前面的某一个位置即可。
所以如果数组本来就是有序的,则数组的最好情况下时间复杂度为O(n)
如果数组恰好是倒序,想要排成从小到大,则每一趟前面的数都要往后移,一共要执行n(n- 1)/2 次,去掉低次幂以及系数,所以最坏情况下时间复杂度为O(n^2)
package PaiXuSuanFa;
import java.util.Arrays;
import java.util.Scanner;
public class Insert_Sort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String[] num = s.split(",");
int[] n = new int[num.length];
for (int i = 0; i < n.length; i++) {
n[i] = Integer.parseInt(num[i]);
}
int temp = 0,j;
for (int i = 1; i < n.length; i++) {
temp = n[i];
for (j = i; j >0; j--) {
//如果当前数前面的数大于当前数,则把前面的数向后移一个位置
if(n[j-1]>temp){
n[j] = n[j-1];
//第一个数已经移到第二个数,将当前数放到第一个位置,这一趟结束
if(j==1){
n[j-1] = temp;
break;
}
}else{//如果不大于,将当前数放到j的位置,这一趟结束
n[j] = temp;
break;
}
}
System.out.println(Arrays.toString(n));
}
System.out.println(Arrays.toString(n));
}
}
4.快速排序
思路分析:
快速排序的思想就是,选一个数作为基数(一般选择第一个数),大于这个基数的放在右边,小于这个基数的放在左边,等于这个基数的数放在左边或者右边。
一趟结束后,将基数放在中间分割的位置,第二趟将数组从基数的位置分为两半,分割后的两个数组继续重复以上步骤,选基数,将小数放在基数左边,将大数放到基数的右边,再分割数组,,,直到数组不能再分为止,排序结束。
时间复杂度:O(nlog2(n))
package PaiXuSuanFa;
import com.sun.scenario.effect.Brightpass;
import java.util.Arrays;
import java.util.Scanner;
public class Fast_Sort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String [] num = s.split(",");
int [] arr = new int[num.length];
for (int i = 0; i <arr.length ; i++) {
arr[i] = Integer.parseInt(num[i]);
}
f(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void f(int[]arr,int start,int end){
//直到start>=end时结束递归
if(start<end){
int left = start;
int right = end;
int temp = arr[start];
while(left<right){
//右边的数字大于标准数时,右边的数的位置不变,指针向左移一个位置
while(left<right && arr[right]>=temp){
right--;
}
//右边的数字及下标小于或者等于基本数,将右边的数放到左边
if(left<right){
arr[left] = arr[right];
left++;
}
//左边的数字小于或者等于标准数时,左边的数的位置不变,指针向右移动一个位置
while(left<right && arr[left]<=temp){
left++;
}
//左边的数字大于基数,将左边的数放到右边
if(left<right){
arr[right] = arr[left];
right--;
}
}
//一趟循环结束,此时left= right,将基数放到这个重合的位置,
arr[right] = temp;
//将数组从left位置分为两半,继续递归下去进行排序
f(arr,start,left);
f(arr,left+1,end);
}
}
}
5.归并排序
思路分析
归并排序就是递归的将原始数组递归对半分割,直到不能再分(只剩下一个元素)后,开始从最小的数组向上归并排序。
1.向上归并排序的时候,需要一个暂存数组用来排序;
2.将待合并的两个数组,从第一位开始比较,小的放到暂存数组,指针向后移;
3.直到一个数组空,这时,不用判断哪个数组空了,直接将两个数组剩下的元素追加到暂存数组里;
4.再将暂存数组排序后的元素放到原数组里,两个数组合成一个,这一趟结束。
时间复杂度:
无论原始数组是否有序的,都要递归分割并向上归并排序,所以时间复杂度使用是O(nlog2n)。
空间复杂度:
每次两个数组进行归并排序的时候,都会利用一个长度为n的数组作为辅助数组用于保存合并序列,所以空间复杂度为O(n)。
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{3,6,4,7,5,2};
merge(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
//归并
public static void merge(int[] arr,int low,int high){
int center = (high+low)/2;
if(low<high){
//递归,直到low==high,也就是数组已不能再分了,
merge(arr,low,center);
merge(arr,center+1,high);
//当数组不能再分,开始归并排序
mergeSort(arr,low,center,high);
System.out.println(Arrays.toString(arr));
}
}
//排序
public static void mergeSort(int[] arr,int low,int center,int high){
//用于暂存排序后的数组的临时数组
int[] tempArr = new int[arr.length];
int i = low,j = center+1;
//临时数组的下标
int index = 0;
//循环遍历两个数组的数字,将小的插入到临时数组里
while(i<=center && j<= high){
//左边数组的数小,插入到新数组
if(arr[i]<arr[j]){
tempArr[index] = arr[i];
i++;
}else{//右边数组的数小,插入到新数组
tempArr[index] = arr[j];
j++;
}
index++;
}
//处理左半边数组多余的数据,将左半边多余的数据直接追加的临时数组的后面
while(i<=center){
tempArr[index] = arr[i];
i++;
index++;
}
//处理右半边数组多余的数据,将右半边多余的数据直接追加的临时数组的后面
while(j<= high){
tempArr[index] = arr[j];
j++;
index++;
}
//将临时数组中的数据重新放进原数组
for (int k = 0; k < index; k++) {
arr[k+low] = tempArr[k];
}
}
}
6.基数排序
基数排序第i趟将待排数组里的每个数的i位数放到tempj(j=1-10)队列中,然后从这十个队列中取出数据,重新放到原数组里,直到i大于待排数的最大位数。
1.数组里的数最大位数是n位,就需要排n趟。
2.若数组里共有m个数,则需要十个长度为m的数组tempj(j=0-9)用来暂存i位上数位j的数,
3.分配结束后,再一次从tempj数组中取出数组,遵循先进先出原则。
时间复杂度:
O(d*n) n是数组长度,d是最大位数
空间复杂度:
基数排序的空间复杂度为O(n+k),其中k为桶的个数,需要分配n个数
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{10,6,3,8,33,27,66,9,7,88};
radixSort(arr);
}
private static void radixSort(int[] arr) {
//求出待排数的最大数
int maxLength=0;
for (int i = 0; i < arr.length; i++) {
if(maxLength<arr[i])
maxLength = arr[i];
}
//根据最大数求最大长度
maxLength = (maxLength+"").length();
//用于暂存数据的数组
int[][] temp = new int[10][arr.length];
//用于记录temp数组中每个桶内存的数据的数量
int[] counts = new int[10];
//用于记录每个数的i位数
int num = 0;
//用于取的元素需要放的位置
int index = 0;
//根据最大长度决定排序的次数
for (int i = 0,n=1; i < maxLength; i++,n*=10) {
for (int j = 0; j < arr.length; j++) {
num = arr[j]/n%10;
temp[num][counts[num]] = arr[j];
counts[num]++;
}
//从temp中取元素重新放到arr数组中
for (int j = 0; j < counts.length; j++) {
for (int j2 = 0; j2 < counts[j]; j2++) {
arr[index] = temp[j][j2];
index++;
}
counts[j]=0;
}
index=0;
}
System.out.println(Arrays.toString(arr));
}
}
7.希尔排序
希尔排序在数组中采用跳跃式分组的策略,通过某个增量将数组元素划分为若干组,然后分组进行插入排序,然后逐步缩小增量,继续按组进行插入排序操作,直到增量为1。希尔排序通过这种策略使得整个数组在初始阶段达到从宏观看基本有序,小的基本在前,大的基本在后。然后缩小增量,到增量为1时,其实多数情况下只需微调即可,不会设计过多的数据移动。
1.时间复杂度:
最坏的情况下,每两个数都要比较并交换一次,则最坏情况下的时间复杂度为O(n^2),最好的情况下,数组是有序的,不需要交换,只需要比较,则最好情况下的时间复杂度为O(n)
2.空间复杂度:
希尔排序只需要一个变量用于两数交换,与n的大小无关,所以空间复杂度为O(1)。
import java.util.Arrays;
public class shell {
public static void main(String[] args) {
int[] arr = new int[]{10,6,3,8,33,27,66,9,7,88};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void shellSort(int[] arr) {
int temp;
//控制增量序列,增量序列为1的时候为最后一趟
for (int i = arr.length/2; i >0; i/=2) {
//根据增量序列,找到每组比较序列的最后一个数的位置
for (int j = i; j < arr.length; j++) {
//根据该比较序列的最后一个数的位置,依次向前执行插入排序
for (int k = j-i; k >=0; k-=i) {
if(arr[k]>arr[k+i]){
temp = arr[k];
arr[k] = arr[k+i];
arr[k+i] = temp;
}
}
}
}
}
}
8.堆排序
堆的相关概念:堆是具有以下性质的完全二叉树:每个节点的值都大于或者等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或者等于其左右孩子节点的值,称之为小顶堆。
堆排序的基本思想以及步骤
将待排序序列构成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列。
总结下堆排序的基本思路:
(1)将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
(2)将堆顶元素与末尾元素交换,将最大元素“沉”到数组末端;
(3)重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
时间复杂度:
堆排序是一种选择排序,整体主要是有构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,近似为nlogn。所以堆排序时间复杂度最好和最坏情况下都是O(nlogn)级。
空间复杂度:
堆排序不要任何辅助数组,只需要一个辅助变量,所占空间是常数与n无关,所以空间复杂度为O(1).
import java.util.Arrays;
public class duipaixu {
public static void main(String[] args) {
int[] arr = new int[]{4,6,8,5,9};
int length = arr.length;
//从最后一个非叶节点开始构建大顶堆
for (int i = arr.length/2-1; i >=0; i--) {
maximumHeap(i,arr,length);
}
//从最小的叶子节点开始与根节点进行交换并重新构建大顶堆
for (int i = arr.length-1; i >=0; i--) {
// System.out.println(Arrays.toString(arr));
swap(arr,0,i);
length--;
maximumHeap(0,arr,length);
}
System.out.println(Arrays.toString(arr));
}
//构建大顶堆
public static void maximumHeap(int i,int[] arr,int length){
int temp = arr[i];
for (int j = i*2+1; j < length; j=j*2+1) {
//如果右孩子大于做孩子,则指向右孩子
if(j+1<length && arr[j+1]>arr[j]){
j++;
}
//如果最大的孩子大于当前节点,则将大孩子赋给当前节点,修改当前节点为其大孩子节点,再向下走。
if(arr[j]>temp){
arr[i] = arr[j];
i = j;
}else{
break;
}
}
//将temp放到最终位置
arr[i] = temp;
}
//交换
public static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
if(j+1<length && arr[j+1]>arr[j]){
j++;
}
//如果最大的孩子大于当前节点,则将大孩子赋给当前节点,修改当前节点为其大孩子节点,再向下走。
if(arr[j]>temp){
arr[i] = arr[j];
i = j;
}else{
break;
}
}
//将temp放到最终位置
arr[i] = temp;
}
//交换
public static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}