八大排序算法汇总

一、冒泡排序
1.思路

从序列头开始,两两比较,将较大值交换到后面,直到将最大值冒到最后,重复此步骤,直到序列有序。

图示:
在这里插入图片描述
2.时间复杂度:

最坏情况:O(n^2)–完全无序
最好情况:O(n)–序列有序

3.空间复杂度: O(1)

4.稳定性:稳定

5.代码实现

public static void bubbleSort(int[] arr) {
        if(arr == null  || arr.length ==1){
            return;
        }
        boolean flag = false; // 表示是否发生交换
        for (int i = 0; i < arr.length-1; i++) { // 趟数
            flag = false;  //每一趟都要更新flag
            for (int j = 0; j < arr.length-1-i; j++) {  // 一趟的过程
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
        			arr[j] = arr[j+1]; // 将前面大数据与后面小数据交换
      			    arr[j+1] = temp;
                    flag = true;   // 判断是否进行了交换
                }
            }
            if(flag = false)
                return;
        }
    }

二、直接插入排序
1.思路

1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置,将新元素插入到该位置后

图示:
在这里插入图片描述
2.时间复杂度

最坏情况:O(n^2)–序列无序
最好情况:O(n)–序列有序

3.空间复杂度:O(n)

4.稳定性:稳定

5.代码实现

public static void insertionSort(int[] arr){
        if(arr == null || arr.length ==1){
            return;
        }
        for(int i = 1;i<arr.length;i++){ // i遍历待排序数据  第一个数据不需排序
            int temp= arr[i];
            int j =0;
            for(j = i-1;j>=0;j--){  //找要插入的位置,j从后往前遍历已排序的数据
                if(arr[j] > temp){
                    //数据移动,将大的数据往后移
                    arr[j+1] = arr[j];
                }else {
                    break;
                }
            }
            arr[j+1] = temp;
        }
    }

三、选择排序
1.思路

从待排序序列中选择最小值,和当前 i 交换

图示:
在这里插入图片描述
2.时间复杂度:O(n^2)

3.空间复杂度:O(1)

4.稳定性:不稳定

5.代码实现

public static void selectSort(int[] arr){
        if(arr == null || arr.length ==1){
            return;
        }
        int i=0,j=0;
        for(i = 0;i<arr.length;i++){ // 遍历
            int minIndex = i; //保存i下标
            for(j = i+1;j < arr.length;j++){  // 每次再遍历i后面的数据
                if(arr[j] < arr[minIndex]){  //  i与j的值比较
                    minIndex = j;  // 保存较小值下标
                }
            }
            if(i != minIndex) {  // 减少交换次数
                swap(arr, i, minIndex);
            }
        }
    }

四、快速排序
1.思路

通过选择一个关键值作为基准点,比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。依次递归,达到总体待排序序列都有序。

图示:
在这里插入图片描述

2.时间复杂度

最好情况:O(nlog2n)
最坏情况:O(n^2)

3.空间复杂度:O(log2n)

4.稳定性:不稳定

5.快排优化:(1)随机选取基准值。(2)三数取中:取三个关键字先进行排序,将中间数作为基准, 一般是取左端、右端和中间三个数。将三个数进行排序,将中间的值放到第一个 low 位置,作为基准进行比较。(3)数据非常少的时候用冒泡法。

6.代码实现

//一次划分过程  一次排序后:【比基准小的元素】【基准数】【比基准大的元素】
    public static int parttition(int[] arr,int low ,int high) {
        int temp = arr[low]; // 选取第一个数为基准

        while (low < high) {
            //先从右往左找到第一个比基准值小的元素
            while (low < high && arr[high] > temp) {
                high--;  //若比基准值大,继续比较下一个
            }
            if(low == high){
                break;
            }else {
                arr[low] = arr[high];  //把小于基准值的元素赋值到前面的“空位”
            }
            //再从左往右找到第一个比基准值大的元素
            while (low<high && arr[low] <= temp) {
                low++; // 若比基准值小,继续比较下一个
            }
            if(low == high){
                break;
            }else {
                arr[high] = arr[low];// 把大于基准值的元素赋值到后面的“空位”
            }
        }
        arr[low] = temp; //把基准值赋值到“空位”
        return low;
    }

    public static  void quick(int[] arr,int low,int high){
        int pos = parttition(arr,low,high); //得到基准值的下标
        //如果左边元素 > 1
        if(pos-low >1 ){
            quick(arr,low,pos-1);// 递归
        }
        //如果右边元素 > 1
        if(high-pos > 1){
            quick(arr,pos+1,high);
        }
    }
    public static void  quickSort(int[] arr){
        quick(arr,0,arr.length-1);
    }



/**
     * 快排优化
     * 1.随机选取基准值
     */
    public static int randomNumber(int[] arr){
        Random random = new Random();  //随机选取
        int index = random.nextInt(arr.length); // [0,arr.length)
        return index;
    }

/**
     * 快排优化
     * 2.三数取中
     */
    public static  int getNumber(int[] arr,int low,int high){
        int numLeft = arr[low];
        int numRight = arr[high];
        int numMid = arr[(high-low)>>>1+low];
        int[] brr = {numLeft,numMid,numRight};
        bubbleSort(brr);
        return brr[arr.length/2];
    }

/**
     * 快排优化
     * 3.数据非常少的时候用冒泡法
     */
     /*if(high - low < 100) {
     	bubbleSort(arr);
       }*/

五、希尔排序
1.思路

即简单插入排序的优化。希尔排序是按照一定的增量进行分组排序,待排序序列有n个元素,取一个小于n的整数m1为增量,把待排序序列以间隔m1分成若干子序列,对每个子序列进行简单插入排序;再取第二个小于m1的增量m2,重复上述分组排序操作;直到取到增量为1后,排序结束。

图示:
在这里插入图片描述

2.时间复杂度:

最好情况:O(n)
最坏情况:O(n^2)

3.空间复杂度:O(1)

4.稳定性:不稳定

5.代码实现

 public static void shell(int[] arr,int gap){  //gap为增量
        for(int i = gap;i<arr.length;i++){  //从第gap个元素开始
            int temp = arr[i];
            int j = 0;
            for(j = i-gap;j >= 0;j -= gap){  //和前第gap个元素比较
                if(temp < arr[j]){
                    arr[j+gap] = arr[j];  //若前大则将前值赋给后值
                }else {
                    break;
                }
            }
            arr[j+gap] = temp;
        }
    }
    public static void shellSort(int[] arr){
        int[] group = {5,3,1};  //增量序列
        for(int i = 0;i<group.length;i++){
            shell(arr,group[i]);
        }
    }

六、归并排序
1.思路

先让每一小段有序,再让小段之间变得有序。将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。

图示:
在这里插入图片描述

2.时间复杂度:O(nlog2n)

3.空间复杂度:O(n)

4.稳定性:稳定

5.代码实现

public static void merge(int[] arr,int gap){
        int left1 = 0; // 第一个归并段的起始下标
        int right1 = left1+gap-1;// 第一个归并段的结束下标
        int left2 = right1+1;// 第二个归并段的起始下标
        int right2 = left2+gap-1 > arr.length-1 ? arr.length-1 : left2+gap-1; //判断是否越界

        int[] brr = new int[arr.length];
        int j = 0;
        //有两个归并段
        while(right2>=left2) {
            while (left1 <= right1 && left2 <= right2) {  //结束条件:左边界<=右边界
                if (arr[left1] <= arr[left2]) {
                    brr[j++] = arr[left1++];
                } else {
                    brr[j++] = arr[left2++];
                }
            }
            //其中一个归并段已没有数据,另一个归并段直接拷贝下来
            while (left1 <= right1) { //第一个归并段还有数据,直接拷贝下来
                brr[j++] = arr[left1++];
            }
            while (left2 <= right2) {
                brr[j++] = arr[left2++];
            }

            //更新归并段,循环跑起来
            left1 = right2 + 1;
            right1 = left1 + gap - 1;
            left2 = right1 + 1;  //不存在下标越界错误ArrayIndexOutOfBoundsException
            right2 = left2 + gap - 1 > arr.length - 1 ? arr.length - 1 : left2 + gap - 1;
        }

        //没有两个归并段,单的直接拷贝下来
        if(right1 >= arr.length-1){
            for(int i = left1;i < arr.length;i++){
                brr[j++] = arr[i];
            }
        }
        //将brr拷贝到arr
        for(int i = 0;i < brr.length;i++){
            arr[i] = brr[i];
        }
    }

    public static void mergeSort(int[] arr){
        for(int i =1;i<arr.length;i*=2){  // 2个2个有序,4个4个有序
            merge(arr,i);
        }
    }

七、堆排序
1.思路

堆是一棵顺序存储的完全二叉树。完全二叉树中所有非终端节点的值均不大于(或不小于)其左、右分支节点的值。其中每个节点的值小于等于其左、右分支的值,这样的堆称为小根堆;其中每个节点的值大于等于其左、右孩子的值,这样的堆称为大根堆。若想升序则建立大根堆,若想降序,则建立小根堆。
堆排序的原理:假设从小到大排序,先构造出来大根堆,然后取出堆顶元素(也就是最大的元素),放到数组的最后面,然后再将剩余的元素构造大根堆,再取出堆顶元素放到数组倒数第二个位置,依次类推,直到所有的元素都放到数组中,排序就完成了。

图示:
在这里插入图片描述
2.时间复杂度:nlog2n

3.空间复杂度:O(1)

4.稳定性:不稳定

5.代码实现

public static void adjust(int[] arr,int begin,int end){
        int temp = arr[begin];
        //挑选左右分支较大值,根节点=较大值
        for(int i = 2*begin+1;i<=end;i = 2*i+1){ // i从根节点跑到其左分支
            if(i+1 <= end && arr[i] < arr[i+1]){
                i = i+1; // i保存左右分支较大值小标
            }
            if(arr[i] > temp){
                arr[begin] = arr[i];
                begin = i;
            }else {
                break;
            }
        }
        arr[begin] = temp;
    }
    public static void heapSort(int[] arr){
        //建大根堆
        for(int i = (arr.length-1-1)/2;i>=0;i--){ // i代表要调整根节点的下标
            adjust(arr,i,arr.length-1); // 大根堆建立好了
        }

        for(int i = 0;i<arr.length;i++){
            int temp = arr[0];
            arr[0] = arr[arr.length-1-i];
            arr[arr.length-1-i] = temp;

            adjust(arr,0,arr.length-1-i-1);
        }
    }

八、基数排序(桶排序)
1.思路

基数排序,从低位(个位)开始,根据个位数排序一次,然后根据十位数排序,再根据百位数进行排序……最终完成整个数组的排序。
对于十进制数字而言,每一位只会是 0~9 这十个数字,我们通常使用桶排序(计数排序)来完成每一位数的排序。
桶排序根据要排序的数据的个位索引下标依次入桶操作,求得最大数的最大位数,接下来依次出桶,然后继续以十位数的数字进行再次入桶操作,然后在进行出桶操作,…出桶操作,我们将其看成一个队列,进行先进先出操作。

图示:
在这里插入图片描述
2.时间复杂度:O(n)

3.空间复杂度:O(n)

4.稳定性:稳定

5.代码实现

public static int getMaxFigur(int[] arr){
	int max=arr[0];
	for(int i=1;i<arr.length;i++){
		if(arr[i]>max){
			max=arr[i];
		}
	}
	int num = GetFigur(max);
	return num;
}

public static int GetFigur(int n){
	int count=0;
	if(n==0){
		return 1;
	}
	while(n!=0){
		count++;
		n/=10;
	}
	return count;
}

public static void redixSort(int[] arr){
	int maxfigur=getMaxFigur(arr);
	for(int i=0;i<maxfigur;i++){
		redix(arr,i);//i 表示当前取第几个关键字
	}
}

//获取 num 数字的右数第 count 个十进制数
private static int getDec(int num,int count){
	for(int i=0;i<count;i++){
		num/=10;
	}
	return num%10;
}

@SuppressWarnings("unchecked")
public static void redix(int[] arr,int figur){//figur 表示取第几个关键字
//定义是个链表头
	List[] list=new LinkedList[10];//定义是个桶
	for(int i=0;i<list.length;i++){
		list[i]=new LinkedList();
	}
	for(int i=0;i<arr.length;i++){
		//入桶 insert
		int key=getDec(arr[i],figur);//key 表示要进第几个桶
		//插入到第几号桶中
		list[key].add(arr[i]);
		//将桶中的数据插入到数组当中
	}
	int z=0;
	for(int j=0;j<10;j++){//将桶中的数据先进先出的方式出到数组中
		for(int i=0;i<list[j].size();i++){//桶中数据个数
			arr[z++] = (int)list[j].get(i);//桶中数据依次放到数组中
		}
	}
}

排序算法的对比

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值