排序----十大排序算法介绍

最近发现了一个挺厉害的人工智能学习网站,内容通俗易懂,风趣幽默,感兴趣的可以点击此链接进行查看:床长人工智能教程

 废话不多说,请看正文!

排序的基本概念

 

排序的稳定性

假设Ki = Kj(1<= i <= n, 1<=j<=n, i != j ),且在排序前的序列中Ri 领先于 Rj (即 i < j)

若在排序后的序列中 Ri 仍然领先于 Rj ,则称所用的排序方法是稳定

若可能使排序后的序列中 Rj 领先于 Ri, 则称所用排序方法使不稳定的。

内部排序:指的是待排序记录全部存放在计算机内存中进行排序的过程。

外部排序:指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进访问的排序过程。 

内部排序分类

  • 插入类:将无序子序列中的一个或几个记录“插入”到有序序列中,从而增加记录的有序子序列的长度。主要包括直接插入排序折半插入排序希尔排序
  • 交换类:通过“交换”无序序列中的记录从而得到其中关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。主要包括冒泡排序快速排序
  • 选择类:从记录的无序子序列中“选择”关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。主要包括简单选择排序树型选择排序堆排序
  • 归并类:通过“归并”两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度。2-路归并排序是最为常见的归并排序方法。
  • 分配类:是唯一一类不需要进行关键字之间比较的排序方法,排序时主要利用分配和收集两种基本操作来完成。基数排序是主要的分配类排序方法

 十大排序算法

1、 冒泡排序

比较相邻的元素。如果第一个比第二个大,就交换他们两个。

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

针对所有的元素重复以上的步骤,除了最后一个。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

  • 什么时候最快?

        当输入的数据已经是正序时。

  • 什么时候最慢?

        当输入的数据是反序时。

 平均复杂度 O(n²)
最好情况 O(n)
最坏情况 O(n²)
空间复杂度O(1);
稳定性 稳定


import java.util.Arrays;

public class BubbleSort {

    public static void main(String[] args) {
        int arr[] = {101, 34, 119, 1, -1, 90, 123};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));

    }

    /**
     * 将前面额冒泡排序算法,封装成一个方法
     * @param arr
     */
    public static void bubbleSort(int[] arr) {

        /**
         * temp 临时变量
         * 冒泡排序 的时间复杂度 O(n^2)
         * flag 标识变量,表示是否进行过交换
         */
        boolean flag = false;
        for (int i = 0; i < arr.length - 1; i++) {

            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 如果前面的数比后面的数大,则交换
                if (arr[j] > arr[j + 1]) {
                    flag = true;
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }

            /**
             * 在一趟排序中,一次交换都没有发生过
             */
            if (!flag) {
                break;
            } else {
                /**
                 * 重置flag!!!, 进行下次判断
                 */
                flag = false;
            }
        }
    }
}

2、选择排序

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

重复第二步,直到所有元素均排序完毕。

平均复杂度 O(n²)
最好情况 O(n²)
最坏情况 O(n²)
空间复杂度O(1);
稳定性 不稳定

import java.util.Arrays;

/**
 * 选择排序
 */
public class SelectSort {

	public static void main(String[] args) {
		int [] arr = {101, 34, 119, 1, -1, 90, 123};
		selectSort(arr);
		System.out.println(Arrays.toString(arr));


	}


	/**
	 * 选择排序
	 * @param arr
	 */
	public static void selectSort(int[] arr) {

		/**
		 * 选择排序时间复杂度是 O(n^2)
		 */
		for (int i = 0; i < arr.length - 1; i++) {
			int minIndex = i;
			int min = arr[i];
			for (int j = i + 1; j < arr.length; j++) {
				/**
				 * 说明假定的最小值,并不是最小
				 * 重置min
				 * 重置minIndex
				 */
				if (min > arr[j]) {
					min = arr[j];
					minIndex = j;
				}
			}

			/**
			 * 将最小值,放在arr[0], 即交换
			 */
			if (minIndex != i) {
				arr[minIndex] = arr[i];
				arr[i] = min;
			}

		}
	}

}

 3、插入排序

将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素
当成是未排序序列。

从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。
(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等
元素的后面。)

 平均复杂度 O(n²)
最好情况 O(n)
最坏情况 O(n²)
空间复杂度O(1);
稳定性 稳定

import java.util.Arrays;

public class InsertSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1, -1, 90, 123};
        /**
         * 调用插入排序算法
         */
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 插入排序
     * @param arr
     */
    public static void insertSort(int[] arr) {
        for (int i = 1; i < arr.length; i++){
            for(int j = i; j > 0; j--){
                if(arr[j-1] > arr[j]){
                    int temp = arr[j-1];
                    arr[j-1] = arr[j];
                    arr[j] = temp;
                }else{
                    break;
                }
            }
        }

    }

}

4、希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希
尔排序是非稳定排序算法。
希尔排序按照下标增量分组,对每组使用插入排序算法,随着增量的减少,每组
包含的元素越来越多,当增量减到1时,整个元素被分成一组。

  平均复杂度 O(nlogn)
最好情况 O(n log²n)
最坏情况 O(n log²n)
空间复杂度O(1);
稳定性 不稳定

import java.util.Arrays;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1, -1, 90, 123};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    /**
     * 对数组a中的元素进行排序
     * @param arr
     */
    public static void sort(int[] arr){

        /**
         * 1.根据数组a的长度,确定增长量h的初始值;
         */
        int h = 1;
        while(h < arr.length / 2){
            h = 2*h+1;
        }

        /**
         * 2.希尔排序
         */
        while(h >= 1){
            /**
             * 找到待插入的元素
             */
            for (int i = h; i<arr.length; i++){
                /**
                 * 把待插入的元素插入到有序数列中
                 */
                for (int j = i;j >= h;j -= h){
                    /**
                     * 待插入的元素是a[j],比较a[j]和a[j-h]
                     */
                    if(arr[j-h] > arr[j]){
                        int temp = arr[j-h];
                        arr[j-h] = arr[j];
                        arr[j] = temp;
                    }else {
                        /**
                         * 待插入元素已经找到了合适的位置,结束循环;
                         */
                        break;
                    }
                }

            }
            /**
             * 减小h的值
             */
            h= h/2;
        }

    }

}

5、归并排序

1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

2、设定两个指针,最初位置分别为两个已经排序序列的起始位置;

3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

4、重复步骤 3 直到某一指针达到序列尾;

5、将另一序列剩下的所有元素直接复制到合并序列尾。

 平均复杂度 O(nlogn)
最好情况 O(nlogn)
最坏情况 O(nlogn)
空间复杂度O(n);
稳定性 稳定

import java.util.Arrays;

//排序代码
public class MergeSort {
    /**
     * 归并所需要的辅助数组
     */
    private static int[] assist;

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1, -1, 90, 123};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 对数组a中的元素进行排序
     * @param arr
     */
    public static void sort(int[] arr) {
        /**
         * 初始化辅助数组
         */
        assist = new int[arr.length];
        int left = 0;
        int right = arr.length - 1;
        sort(arr, left, right);
    }

    /**
     * 对数组a中从left到hi的元素进行排序
     * @param arr
     * @param left
     * @param right
     */
    private static void sort(int[] arr, int left, int right) {
        if (left >= right){
            return;
        }
        int mid = left + (right - left) / 2;
        /**
         * 对left到mid之间的元素进行排序;
         */
        sort(arr, left, mid);
        /**
         * 对mid+1到hi之间的元素进行排序;
         */
        sort(arr, mid+1, right);
        /**
         * 对left到mid这组数据和mid到hi这组数据进行归并
         */
        merge(arr, left, mid, right);
    }

    /**
     *对数组中,从left到mid为一组,从mid+1到hi为一组,对这两组数据进行归并
     * @param arr
     * @param left
     * @param mid
     * @param right
     */
    private static void merge(int[] arr, int left, int mid, int right) {
        /**
         * left到mid这组数据和mid+1到hi这组数据归并到辅助数组assist对应的索引处
         * i 定义一个指针,指向assist数组中开始填充数据的索引
         * p1 定义一个指针,指向第一组数据的第一个元素
         * p2 定义一个指针,指向第二组数据的第一个元素
         */
        int i = left;
        int p1 = left;
        int p2 = mid + 1;
        /**
         * 比较左边小组和右边小组中的元素大小,哪个小,就把哪个数据填充到assist数组中
         */
        while (p1 <= mid && p2 <= right) {
            if(arr[p1] < arr[p2]){
                assist[i++] = arr[p1++];
            }else {
                assist[i++] = arr[p2++];
            }
        }
        /**
         * 上面的循环结束后,如果退出循环的条件是p1<=mid,则证明左边小组中的数据已经归并完毕,如果退出循环的条件是p2<=hi,则证明右边小组的数据已经填充完毕;
         * 所以需要把未填充完毕的数据继续填充到assist中,//下面两个循环,只会执行其中的一个
         */
        while(p1<=mid){
            assist[i++]=arr[p1++];
        }
        while(p2<=right){
            assist[i++]=arr[p2++];
        }
        /**
         * 到现在为止,assist数组中,从left到hi的元素是有序的,再把数据拷贝到a数组中对应的索引处
         */
        for (int index=left;index<=right;index++){
            arr[index]=assist[index];
        }
    }

}

 6、快速排序

从数列中挑出一个元素,称为 "基准"(pivot);

重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

平均复杂度 O(nlogn)
最好情况 O(nlogn)
最坏情况 O(n²)
空间复杂度O(logn);
稳定性 不稳定 

package com.yxj.mianshi.algorithm;

import java.util.Arrays;

public class QuickSort3 {
    public static void main(String[] args) {
        int[] nums = {101, 34,3,5,3,5,2,78,1100};
        quickSort(nums,0,nums.length - 1);
        System.out.println(Arrays.toString(nums));
    }
    public static void quickSort(int[] nums, int left, int right){
        if (nums == null || nums.length == 0){
            return;
        }
        if (left < right){
            int i = left;
            int j = right;
            int pivot = nums[left];
            while (i < j){
                while (i < j && nums[j] >= pivot)
                {
                    j--;
                }
                swap(nums,i,j);
                while (i < j && nums[i] <= pivot){
                    i++;
                }
                swap(nums,i,j);
            }
            quickSort(nums,left,i - 1);
            quickSort(nums, i + 1,right);

        }
    }

    public static void  swap(int[] nums,int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

7、计数排序 

(1)找出待排序的数组中最大和最小的元素
(2)统计数组中每个值为i的元素出现的次数,存入数组的第i项
(3)对所有的计数累加(从数组中的第一个元素开始,每一项和前一项相加)
(4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

 平均复杂度 O(n + k)
最好情况 O(n + k)
最坏情况 O(n + k)
空间复杂度O(n);
稳定性 稳定

import java.util.Arrays;

public class CountSort {
    public static void main(String[] args) {
        int arr[] = {101, 34, 119, 1, 90, 123, 130};
        countSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void countSort(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
        }

        int min = arr[0];
        for (int i = 1; i < arr.length; i++){
            if (arr[i] < min){
                min = arr[i];
            }
        }

        /**
         * 创建计数数组
         */
        int[] count = new int[max - min + 1];
        for(int i = 0; i < arr.length; i++){
            count[arr[i]-min]++;
        }
        int k = 0;
        for(int i = 0; i < count.length; i++){
            while (count[i] > 0){
                arr[k++] = i + min;
                count[i]--;
            }
        }
    }
}

8、桶排序

把数据放在桶里,桶里排好序,遍历桶即可 

 平均复杂度 O(n + k)
最好情况 O(n + k)
最坏情况 O(n²)
空间复杂度O(n + k);
稳定性 稳定

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class BucketSort {
    public static void main(String[] args) {
        int arr[] = {101, 34, 119, 1, 90, 123, 130};
        bucketSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void bucketSort(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
        }

        int min = arr[0];
        for (int i = 1; i < arr.length; i++){
            if (arr[i] < min){
                min = arr[i];
            }
        }

        /**
         * 确定桶的数量
         */
        ArrayList<ArrayList<Integer>> buckets = new ArrayList<>();
        int count = (max - min) / arr.length + 1;
        for(int i = 0; i < count; i++){
            buckets.add(new ArrayList<Integer>());
        }

        for(int i = 0; i < arr.length; i++){
            buckets.get((arr[i] - min) / arr.length).add(arr[i]);
        }

        for(int i = 0; i< buckets.size(); i++){
            Collections.sort(buckets.get(i));
        }

        /***
         * 遍历桶里的每个数据,输出到arr
         */
        int k = 0;
        for(int i = 0; i < buckets.size(); i++){
            ArrayList<Integer> list = buckets.get(i);
            for (int j = 0; j < list.size(); j++){
                arr[k++] = list.get(j);
            }
        }
    }
}

 9、基数排序

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的
数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日
期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

基数排序:根据键值的每位数字来分配桶;

平均复杂度 O(n * k)
最好情况 O(n * k)
最坏情况 O(n * k)
空间复杂度O(n + k);
排序方式 Out-place 
稳定性 稳定

import java.util.Arrays;

public class RadixSort {

    public static void main(String[] args) {
        int arr[] = {101, 34, 119, 1, 90, 123,0,0,1};
        radixSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 基数排序方法
     * @param arr
     */
    public static void radixSort(int[] arr) {


        /**
         * 得到数组中最大的数的位数
         * 假设第一数就是最大数
         */
        int max = arr[0];
        for(int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        //得到最大数是几位数
        int maxLength = (max + "").length();

        /**
         * 定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
         * 二维数组包含10个一维数组
         * 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
         */
        int[][] bucket = new int[10][arr.length];
        /**
         * 为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
         * m[0] , 记录的就是  bucket[0] 桶的放入数据个数
         */
        int[] m = new int[10];


        for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
            for(int j = 0; j < arr.length; j++) {
                /**
                 * 取出每个元素的对应位的值
                 */
                int temp = arr[j] / n % 10;
                /**
                 * 放入到对应的桶中
                 */
                bucket[temp][m[temp]] = arr[j];
                m[temp]++;
            }

            /**
             * 按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
             */
            int index = 0;
            /**
             * 遍历每一桶,并将桶中是数据,放入到原数组
             */
            for(int k = 0; k < m.length; k++) {
                /**
                 * 如果桶中,有数据,我们才放入到原数组
                 */
                if(m[k] != 0) {
                    /**
                     * 循环该桶即第k个桶(即第k个一维数组), 放入
                     */
                    for(int l = 0; l < m[k]; l++) {
                        /**
                         * 取出元素放入到arr
                         */
                        arr[index++] = bucket[k][l];
                    }
                }
                /**
                 * 第i+1轮处理后,需要将每个 m[k] = 0 !!!!
                 */
                m[k] = 0;
            }

        }

    }
}

10、堆排序

创建一个堆 H[0……n-1];
把堆首(最大值)和堆尾互换; 

平均复杂度 O(nlongn)
最好情况 O(nlogn)
最坏情况 O(nlogn)
空间复杂度O(1);
稳定性 不稳定

import java.util.Arrays;

public class HeapSort {

	public static void main(String[] args) {
		int arr[] = {101, 34, 119, 1, -1, 90, 123};
		
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	public static void heapSort(int[] arr) {
		// 初始化建堆
		for (int i = arr.length / 2 - 1; i >= 0; i--){
			heapify(arr,i,arr.length - 1);
		}

		for (int i = arr.length - 1; i > 0; i--){
			// 交换当前节点和堆顶节点
			int temp = arr[0];
			arr[0] = arr[i];
			arr[i] = temp;
			heapify(arr,0,i - 1);
		}
	}

	public static void heapify(int[] arr, int i, int last_index) {
		int max = i;
		if (2 * i + 1 <= last_index && arr[2 * i + 1] > arr[max]){
			max = 2 * i + 1;
		}
		if (2 * i + 2 <= last_index && arr[2 * i + 2] > arr[max]){
			max = 2 * i + 2;
		}

		if (max != i){
			int temp = arr[i];
			arr[i] = arr[max];
			arr[max] = temp;
			heapify(arr,max,last_index);
		}
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zpeien

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值