经典排序算法

1、算法概述

2、 选择排序

  选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。  

  2.1 算法描述

   n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。

  • 初始状态:无序区为R[1..n],有序区为空;
  • 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
  • n-1趟结束,数组有序化了

  2.2 动图演示

  

  2.3 代码实现 

//实现选择排序的函数
    /**
     * 选择要注意的特点:整体比较的次数为数组的长度-1 
     *    原因是最后的元素无需比较,就已经是最大的元素值了
     * @param arrays
     */
    public static void select_sort(int arrays[]){
        //整体的比较次数
        for (int i = 0; i < arrays.length-1; i++) {
            //找一个最小的元素下标
            int minIndex=i;
            for (int j = i+1; j < arrays.length; j++) {
                 //如果在数组的剩余元素中 还存在着比MinIndex对应更小的元素
                 //将minIndex的值重新赋值 
                 if(arrays[j]<arrays[minIndex]){
                    minIndex=j; 
                 }
            }
            if(minIndex!=i){//不满足此条件证明arrays[i]已经是最小的元素值了 不发生交换
                System.out.println("hello");
                //元素值交换的过程
                int temp=arrays[i];
                arrays[i]=arrays[minIndex];
                arrays[minIndex]=temp;
            }
        }
    }

3.冒泡排序

  冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 

  3.1 算法描述 

  • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个;
  • 重复步骤1~3,直到排序完成。

  3.2 动图演示

  

  3.3 代码实现

public static int[] bubble_sort(int []arr){
        if(arr==null||arr.length<2)
            return arr;
        int n=arr.length; //记录数组长度
        for(int i=0;i<n;i++){ //每次都从数组中找到一个最大|最小 总共获取的次数是总长度-1
            for(int j=0;j<n-i-1;j++){ //在数组中比较的次数越来越少    
                 if (arr[j + 1] < arr[j]) {
                     int t = arr[j];
                     arr[j] = arr[j+1];
                     arr[j+1] = t;
                 }
            }
        }
        return arr;
}

  3.4 算法分析

  当输入的数据已经是正序时最快;当输入的数据是倒序时最慢;

4、插入排序

  插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

  4.1 算法描述

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

  4.2 动图演示

  

  4.3 代码实现

/**
     * 外层循环第一次执行
     *    i=1 k=0(有序列表)
     *    temp=6 
     *    arrays[k]=9  {9,6,2,3,5} 后移过的结果 {9,9,2,3,5}
     *    k-- k=-1  k+1
     *    1 声明临时变量 保存无序列表中的第一个元素的值
     *    2 临时变量和有序列表中的元素进行对比(全部对比) 找到了要插入的位置
     *    3 真正的插入操作
     *    
     * @param arrays
     */
    private static void insert_sort(int[] arrays) {
        //条件需要改吗?
        for (int i = 1; i < arrays.length; i++) {
            //第一次要区分有序部分和无序部分 将数组下表为0的元素视为有效元素 
            //后续的元素:无序元素
            int k=i-1;//当前元素的前一个元素的下标 
            int temp=arrays[i];//将要比较的变量放置到临时变量中
            for (; k>=0&&arrays[k]>temp;k--) {
                //元素后移的操作
                arrays[k+1]=arrays[k];
            }
            arrays[k+1]=temp;//插入动作
        }
    }

5、希尔排序

  简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序

  希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;

  希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序

  5.1 算法描述

  先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

  • 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  • 按增量序列个数k,对序列进行k 趟排序;
  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

  5.2 图片解释

  

  5.3 代码实现

public static int[] shellSort(int arr[]) {
        if (arr == null || arr.length < 2) return arr;
        int n = arr.length;
        // 对每组间隔为 h的分组进行排序,刚开始 h = n / 2;
        for (int h = n / 2; h > 0; h /= 2) {
            //对各个局部分组进行插入排序
            for (int i = h; i < n; i++) {
                // 将arr[i] 插入到所在分组的正确位置上
                insertI(arr, h, i);
            }
        }
        return arr;
    }

    private static void insertI(int[] arr, int h, int i) {
        int temp = arr[i];
        int k;
        for (k = i - h; k >=0 && temp < arr[k]; k -= h) {
            arr[k + h] = arr[k];
        }
        arr[k + h] = temp;
    }

6、计数排序

  计数排序是一种适合最大值和最小值的差值不是很大的排序方式

  基本思想:就是把数组元素作为数组下标,然后用一个临时数组统计该元素出现的次数。最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。

  6.1 算法描述

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

  6.2 动图演示

   

  6.3 代码实现

private static void countSort(int[] arrays) {
        //获取数组的长度
        int len=arrays.length;
        //获取数组中的最大值
        int max=arrays[0];
        for(int i=1;i<len;i++){
            if(max<arrays[i]){
                max=arrays[i];
            }
        }
        //创建长度为max+1的临时数组
        int nums[]=new int[max+1];
        for(int i=0;i<len;i++)
            nums[arrays[i]]++;
        int k=0;
        //把统计好的数据输出到原数组
        for (int i=0; i<=max; i++) {
            for(int j=nums[i];j>0;j--){
                arrays[k++]=i;
            }
        }
    }

7、归并排序

  归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并

  7.1 算法描述

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。

  7.2 图片分析

  

  7.3 动图演示

 

  7.4 代码实现

public static void main(String[] args) {
        int arrays[] = { 8, 7, 5, 3, 1, 2, 4, 6 ,9};
        merge_sort(arrays,new int[arrays.length],0, arrays.length-1);
        for (int i = 0; i < arrays.length; i++) {
            System.out.print(arrays[i] + " ");
        }
    }
    /**
     * 
     * @param arr
     *            :要排序的数组
     * @param left
     *            :左边界
     * @param right
     *            :右边界 如果我们的左右边界的值相等 left==right的话 那么代表已经切分到 只剩下一个元素了,则停止递归操作
     */
    public static void merge_sort(int[] arr,int temp[],int left, int right) {
        if (left < right) {
            // 把大数组拆分为两个
            int middle = (left + right) / 2;
            // 对左边的数组进行排序
            merge_sort(arr, temp,left, middle);
            // 对右边的数组进行排序
            merge_sort(arr, temp,middle + 1, right);
            // 实现合并
            merge(arr,temp, left, middle, right);
        }
    }

    private static void merge(int[] arr, int[] temp,int left, int middle, int right) {
        int i=left,j=middle+1;//接收左右边界
        for (int k = left; k <=right; k++) {
            if(i>middle)
                temp[k]=arr[j++];
            else if(j>right)
                temp[k]=arr[i++];
            else if(arr[i]<=arr[j])
                temp[k]=arr[i++];
            else
                temp[k]=arr[j++];
        }
        for (int m = left; m <=right; m++) {
            arr[m]=temp[m];
        }
    }

 8、快速排序

  通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

  8.1 算法描述

  • 从数列中挑出一个元素,称为 “中轴元素”(pivot);
  • 重新排序数列,所有元素比中轴元素值小的摆放在中轴元素前面,所有元素比中轴元素值大的摆在中轴元素的后面(相同的数可以到任一边)。在这个分区退出之后,该中轴元素就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于中轴元素值元素的子数列和大于中轴元素值元素的子数列排序。

  8.2 动图演示

  

  8.3 代码实现

public static int[] quickSort(int[] arr, int left, int right) {
        if (left < right) {
            //获取中轴元素所处的位置
            int mid = partition(arr, left, right);
            //进行分割
            arr = quickSort(arr, left, mid - 1);
            arr = quickSort(arr, mid + 1, right);
        }
        return arr;
    }

    private static int partition(int[] arr, int left, int right) {
        //选取中轴元素
        int pivot = arr[left];
        int i = left + 1;
        int j = right;
        while (true) {
            // 向右找到第一个小于等于 pivot 的元素位置
            while (i <= j && arr[i] <= pivot) i++;
            // 向左找到第一个大于等于 pivot 的元素位置
            while(i <= j && arr[j] >= pivot ) j--;
            if(i >= j)
                break;
            //交换两个元素的位置,使得左边的元素不大于pivot,右边的不小于pivot
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        arr[left] = arr[j];
        // 使中轴元素处于有序的位置
        arr[j] = pivot;
        return j;
    }

9、桶排序

  桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

  9.1 算法描述

  • 创建数据结构了  二维集合 一个ArrayList 我们把ArrayList集合中的LinkedList作为每一个桶
  • 遍历输入数据,并且把数据一个一个放到对应的桶里去
  • 对每个不是空的桶进行排序
  • 将桶中数据拼接起来

  9.2 图解分析

  

  9.3 代码实现

private static void bucket_sort(double[] arrays) {
        //声明了最大值和最小值
        double max=arrays[0];
        double min=arrays[0];
        for (int i = 0; i < arrays.length; i++) {
            if(max<arrays[i])
                max=arrays[i];
            if(min>arrays[i])
                min=arrays[i];
        }
        //获取一下两者的差值
        double sub=max-min;
        //声明一下桶的数量 
        int bucketNum=arrays.length;
        //创建数据结构了  二维集合 一个ArrayList 我们把ArrayList集合中的LinkedList作为每一个桶
        //声明集合中元素的个数为bucketNum
        ArrayList<LinkedList<Double>> lists=new ArrayList<LinkedList<Double>>(bucketNum);
        
        //初始化一下
        for (int i = 0; i < bucketNum; i++) {
              lists.add(new LinkedList<Double>());
        }
        //向桶中放置元素 思路?
        for (int i = 0; i < arrays.length; i++) {
            //随机元素:arrays[i]
            //获取随机元素应该放置的下标位置 int 
            int index=(int)((arrays[i]-min)/(sub/(bucketNum-1)));
            //插入操作
            lists.get(index).add(arrays[i]);
        }
        
        //对桶进行局部排序 LinkedList Collections.sort()
        for (int i = 0; i < lists.size(); i++) {
            Collections.sort(lists.get(i));//局部排序
        }
        int k=0;
        //输出回原数组
        for (LinkedList<Double> linkedList : lists) {
            for (Double item : linkedList) {//一个桶中可能保存了多个元素值
                //使用有序数列覆盖无序数列
                arrays[k++]=item;
            }
        }
    }

 

 10、基数排序

   基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前

  10.1 算法描述 

  • 取得数组中的最大数,并取得位数;
  • arr为原始数组,从最低位开始取每个位组成radix数组;
  • 对radix进行计数排序(利用计数排序适用于小范围数的特点);

  10.2 动图演示

   

   10.3 代码实现

  

  

原文地址:https://www.cnblogs.com/xiao-ran/p/11975633.html