七大排序算法详解

一、选择排序

算法描述

选择排序是内部排序法的其中一种,他是从一组乱序的数据中(n个数),(此处默认从大到小进行排序)每次找一个最小的数,然后将它和第一个元素交换。接下来在剩下的数中,再找到最小数,将它和第二个元素进行交换…直至仅剩一个数为止。所以选择排序所进行的排序次数是n-1次。

算法的思路分析

  1. 先进行一轮排序:(一轮排序一共有三步)先从list[0]~list[n-1]中找到最小的值;再将它与list[0]进行交换;遍历完整个数组之后就得到了本轮的最小数和最小数的下标。
  2. 再仿照第一轮,进行第二轮的循环;
  3. 再进行第三轮的循环;
  4. 找出这三轮循环的规律,再进行整个数组的排序。

算法的源代码描述

public class SelectSort {
    public static void main(String[] args) {
    //给定一个数组
        int[] list = {2, 56, 12, 23, 5};
        seclectSort(list);
        System.out.println(Arrays.toString(list));
    }
    public static void seclectSort(int[] list) {
        //从小到大排列
        for (int i = 0; i < list.length - 1; i++) {
            //假设每次循环时,第一个数是最小值
            int minIndex = i;
            int min = list[i];
            //从下标为i + 1往后开始遍历,找最小值
            for (int j = i + 1; j < list.length; j++) {
                //用假定的最小值依次和后面的值进行比较
                //比它还小则交换,记录下该值及该值的下标
                if(min > list[j]) {
                    min = list[j];
                    minIndex = j;
                }
            }
            if(minIndex != i) {
                //将最小值放在前面,也就是进行交换
                list[minIndex] = list[i];
                list[i] = min;
            }
        }
    }
}

二、插入排序

算法描述

插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

算法基本思想

插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。

算法的思路图

在这里插入图片描述

算法的代码描述

import java.util.Arrays;
public class InsertSort {
    public static void main(String[] args) {
        //给定一个数组,将其进行选择排序,默认升序
        int[] arr = {15, 25, 3, 10, 9};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void insertSort(int[] arr) {
        int insertVal = 0;//定义一个临时变量用于存放带插入的数
        int insertIndex = 0;
        for (int i = 1; i < arr.length; i++) {
            //定义带插入的数
            insertVal = arr[i];
            insertIndex = i - 1;//即arr[i]前面那个数的下标
            //现在要给insertVal找插入的位置
            //insertIndex >= 0是保证在插入过程中,数组的下标不会越界
            //insertVal < arr[insertIndex]则表示待插入的数,还没找到插入位置
            //找到插入位置后,要先将arr[insertIndex]后移,空出位置,才能进行下一步的插入
            while(insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex --;
            }
            //退出while循环表示已经找到了要插入的位置:insertIndex + 1
            //判断是否可以进行插入赋值
            if(insertIndex + 1 != i) {
                arr[insertIndex + 1] = insertVal;
            }
        }
    }
}

三、冒泡排序

算法描述

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

算法的思路分析

  1. 如果是n个数的话,一共要进行n-1次排序;
  2. 每进行完一趟排序后,他的下一趟的排序次数会减少一次;
  3. 排序时,若两个元素为逆序(默认从小到大的升序顺序),则进行交换;
  4. 设置标志位flag,如果发生了交换flag设置为1;如果没有交换就设置为-1。这样当一轮比较结束后如果flag仍为-1,即:这一轮没有发生交换,说明数据的顺序已经排好,没有必要继续进行下去。

算法的源代码描述

import java.util.Arrays;
public class BubbleSort {
    public static void main(String[] args) {
        //给定一个数组,将其从小到大进行排序
        int[] arr = {12, 23, 5, 11, 2};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void bubbleSort(int[] arr) {
        int temp = 0;//定义一个临时变量,用于进行两数的交换操作
        int flag = -1;//设置一个标志量,用于检测是否进行了一轮排序
        for (int i = 0; i < arr.length - 1; i++) {
            //外层循环表示所要进行的趟数,有n个数的话,要进行n-1趟排序
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //内存循环则进行的是排序过程,每进行一趟排序,他的下一趟排序的次数都会少一次
                //若是逆序,就进行两数的交换
                if(arr[j] > arr[j + 1]) {
                    flag = 1;//若进行交换,就给标志量置1
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            //一趟排序进行完后,标志量的值如果为1,表示实际上是进行了一趟排序
            //如果不为1,表示没进行排序
            //这样的话就不用进行后面的排序了,数组已经是有序数组
            if(flag != 1) {
                return;
            } else {
                flag = -1;//每进行一趟排序之后,将标志量重置
            }
        }
    }
}

四、快速排序

算法简单介绍

快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

算法的描述

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

算法的思路图

在这里插入图片描述

算法的代码描述

import java.util.Arrays;
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = { -9, 78, 0, 23, -567, 70};
        quickSort( arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }
    public static void quickSort(int[] arr, int left, int right) {
        int l = left;//左下标
        int r = right;//右下标
        int mid = arr[(left + right) / 2];
        int temp = 0;
        //当遇到比mid小的值的话,放在mid左边;当遇到比mid大的值得话,放在mid右边
        while(l < r) {
            //如果在mid的左边找到>=mid的数,就会退出循环
            while(arr[l] < mid) {
                l ++;
            }
            //如果在mid的右边找到<=mid的数,就会退出循环
            while(arr[r] > mid) {
                r --;
            }
            //如果left>=right则说明mid左右两边的值,已经排好了(即mid左边的数都小于mid,mid右边的数都大于mid)
            if(l >= r) {
                break;
            }
            //交换两个数的操作
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //如果交换完后,发现arr[left]==mid,这时候要将right前移
            if(arr[l] == mid) {
                r --;
            }
            //如果交换完后,发现arr[right]==mid,这时候要将left后移
            if(arr[r] == mid) {
                l ++;
            }
        }
        //如果left==right,必须left++,right--,否则的话会出现栈的溢出
        if(l == r) {
            l ++;
            r --;
        }
        //往左边递归
        if(left < r) {
            quickSort(arr ,left ,r);
        }
        //往右边递归
        if(right > l) {
            quickSort(arr ,l ,right);
        }
    }
}

五、希尔排序

算法简单介绍

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

算法的描述

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt< d(t-1)…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

算法的思路图

在这里插入图片描述

算法的代码描述

import java.util.Arrays;
public class ShellSort {
    public static void main(String[] args) {
        int[] arr ={ 8, 5, 12, 9, 1, 7, 3};
        shellSort1(arr);
        System.out.println(Arrays.toString(arr));
        System.out.println("==========================");
        shellSort2(arr);
        System.out.println(Arrays.toString(arr));
    }
    //第一种排序方法:交换排序
    public static void shellSort1(int[] arr) {
        int temp = 0;//定义一个临时变量,用于两数交换的操作
        for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
            //第一层循环用于控制步长
            for (int i = gap; i < arr.length; i++) {
                //第二层循环用于遍历各组中所有的元素(共有gap组,每组两个元素)
                for (int j = i - gap; j >= 0 ; j -= gap) {
                    //如果当前元素大于加上步长后的那个元素,说明交换
                    if(arr[j] > arr[j + gap]) {
                        temp = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = temp;
                    }
                }
            }
        }
    }
    
    
    //第二种排序方法:移位法(类似于插入排序的思想,先一个一个找位置,将大于它的数往后移)
    public static void shellSort2(int[] arr) {
        for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
            //定义一个增量gap表示步长,并逐步的缩小范围
            for (int i = gap; i < arr.length; i++) {
                //从第gap个元素开始,逐个对其所在的组进行直接插入排序
                int j = i;
                int temp = arr[j];
                if(arr[j] < arr[j - gap]) {
                    while(j - gap >=0 && temp < arr[j - gap]) {
                        //移动
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    //当退出while循环后,就表示已经给temp找到了合适的位置
                    arr[j] = temp;
                }
            }
        }
    }
}

六、归并排序

算法简单说明

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

算法的思路描述

1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4.重复步骤3直到某一指针超出序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾

在这里插入图片描述

算法的代码描述

import java.util.Arrays;
public class MergeSort {
    public static void main(String[] args) {
        //给定一个乱序的数组,将其用归并排序法进行排序
        int[] arr = { 8, 2, 5, 3, 1, 4, 7, 6};
        //归并排序需要一个额外的数组空间
        int[] temp = new int[arr.length];
        mergeSort( arr, 0, arr.length - 1, temp);
        System.out.println(Arrays.toString(arr));
    }
    //分 + 合的方法
    public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if(left < right) {
            //中间的一个索引
            int mid = (left + right) / 2;
            //向左递归进行分解
            mergeSort( arr, left, mid, temp);
            //向右递归进行分解
            mergeSort( arr,mid + 1, right, temp);
            //将所有的解进行合并
            merge( arr, left, mid, right, temp);
        }
    }
    //合并的方法
    /**
     *
     * @param arr 排序的原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边索引
     * @param temp 做中转操作的数组
     */
    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int i = left;//初始化i,左边有序序列的初始索引
        int j = mid + 1;//初始化j,右边有序序列的初始索引
        int t = 0;//指向temp数组的当前索引
        //1.先把左右两边的有序的数据按照规则填充到temp数组
        //直到左右两边的有序序列,有一边处理完为止
        while(i <= mid && j <= right) {
            //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
            //即将左边的当前元素,填充到temp数组
            //t 和 i都向后移一位
            if(arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t ++;
                i ++;
            } else {
                //反之则将右边有序序列的当前元素,填充到temp数组
                temp[t] = arr[j];
                t ++;
                j ++;
            }
        }
        //2.把有剩余数据的一边剩下的数,挨个依次填充到temp
        while(i <= mid) {
            //若左边有序序列还有剩余的元素,就全部填充到temp
            temp[t] = arr[i];
            t ++;
            i ++;
        }
        while(j <= right) {
            //若右边有序序列还有剩余的元素,就全部填充到temp
            temp[t] = arr[j];
            t ++;
            j ++;
        }
        //3.将temp数组的元素拷贝到arr数组中
        //【注意】并不是每次都拷贝所有的数
        t = 0;
        int tempLeft = left;//定义一个临时变量,用于储存左边索引的下标
        //第一次合并tempLeft = 0,right = 1;第二次tempLeft = 2 ,right = 3 ;第三次tempLeft = 0, right = 3;
        //合并完左边的数组后,合并右边的数组
        //最后一次tempLeft = 0 , right= 7
        while(tempLeft <= right) {
            arr[tempLeft] = temp[t];
            t ++;
            tempLeft ++;
        }
    }
}

七、堆排序

!!!详细分析请移步大佬的博客!!!

代码实现

import java.util.Arrays;

public class HeapSort {

    public static void main(String[] args) {
        int[] arr = { 4, 6, 8, 5, 9};
        heapSort(arr);
    }

    //写一个堆排序的方法
    public static void heapSort(int[] arr) {
        int temp = 0;
        System.out.println("进行堆排序!!!");

        //将无序序列构建成一个堆,根据升序降序需求来选择大顶堆还是小顶堆
        for (int i = arr.length / 2 - 1; i >= 0 ; i--) {
            adjustHeap( arr, i, arr.length);
        }

        //将堆顶元素与末尾元素交换,将最大元素“沉”到数组的最末端
        //重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
        for (int j = arr.length - 1; j > 0 ; j--) {
            //交换
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            adjustHeap( arr, 0, j);//每次需要调整的数组的大小在减小
        }
        System.out.println("排序后的数组为:" + Arrays.toString(arr));
    }


    //将一个数组(二叉树),调整成一个大顶堆(局部的调整)===> 以结点i为根的子树的调整

    /**
     * 功能:完成将以i对应的非叶子节点的树调整成大顶堆
     * 举例:int[] arr = { 4, 6, 8, 5, 9}===> i=1 ===> adjustHeap ===> 得到{ 4, 9, 8, 5, 6}
     *         如果再次调用adjustHeap,adjustHeap传入的是 i=0 ===> 原来的{ 4, 9, 8, 5, 6}会变为{ 9, 9, 8, 5, 4}
     * @param arr 待调整的数组
     * @param i 表示非叶子节点在数组中的索引
     * @param length 表示对多少个元素继续调整,length是在逐渐的减少
     */
    public static void adjustHeap( int[] arr, int i, int length) {

        int temp = arr[i];//先取出当前元素的值,保存在临时变量里
        //k=2*i+1是i节点的左子结点
        for (int j = 2 * i + 1; j < length; j = 2 * j + 1) {
            if(j + 1 < length && arr[j] < arr[j + 1]) {
                //说明左子结点的值小于右子节点的值
                j ++;//j指向右子节点
            }
            if(arr[j] > temp) {
                //如果子节点大于父节点
                arr[i] = arr[j];//把较大的值赋给当前结点
                i = j;//!!!i指向k,继续循环比较
            } else {
                break;
            }
        }
        //当循环结束后,我们已经将以i为父节点的树的最大值,放在了该子树的最顶端(局部的调整)
        arr[i] = temp;//将temp值放到调整后的位置
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值