Java常用排序算法实现

来源:互联网 发布:图片收费网站源码 编辑:程序博客网 时间:2024/05/02 00:21

目录

  • 目录
    • 概述
    • 插入排序
    • 希尔排序
    • 简单排序
    • 堆排序
    • 冒泡排序
    • 快速排序
    • 源码

概述

这里总的给出了几种常用的排序算法,也是当作复习和巩固.

  • 插入排序
  • 希尔排序
  • 简单排序
  • 堆排序
  • 冒泡排序
  • 快速排序

以上几个都是根据排序的原理和原则自定义实现的.对于同一个排序实际的实现过程可能有一些不同,但思想和处理方式是相同的.


插入排序

  • 基本思想:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
//插入排序public static boolean insertSort(int[] src, boolean isAsc) {    if (src != null) {        return insertSort(src, 0, src.length - 1, isAsc);    } else {        return false;    }}//指定数组的某一段进行排序//startIndex/endIndex分别为数组中开始排序的位置private static boolean insertSort(int[] src, int startIndex, int endIndex, boolean isAsc) {    if (src == null || startIndex > endIndex) {        return false;    } else {        int temp = 0;        for (int i = startIndex; i <= endIndex; i++) {            //取下一个位置的数据            for (int k = 0; k < i; k++) {                swap(src, k, i, isAsc);            }        }        return true;    }}

希尔排序

  • 基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。
//希尔排序是基于插入排序的,先将数组分为N份进行插入排序,完成后减小增量再进行排序public static boolean shellSort(int[] src, boolean isAsc) {    if (src == null) {        return false;    } else {        //计算一个增量        int increment = src.length / 4;        if (increment <= 0) {            //增量必须大于等于1            increment = 1;        }        //增量递减进行排序        for (int i = increment; i >= 1; i--) {            int j = 0;            //增量分组进行插入排序            for (; j + i < src.length; j += i) {                insertSort(src, j, j + i, isAsc);            }            //如果数组不能完全按增量切分,最后部分也可以正常排序            insertSort(src, j, src.length - 1, isAsc);        }        return true;    }}

简单排序

  • 基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
//依次从数组中每次取出最值,按序排放直到排序结束public static boolean simpleSort(int src[], boolean isAsc) {    if (src == null) {        return false;    } else {        for (int i = 0; i < src.length; i++) {            //记录第N个要排序的位置            int j = i;            //记录当前最大/小值的位置            int k = j;            for (; j + 1 < src.length; j++) {                if (isAsc) {                    if (src[k] > src[j + 1]) {                        //若当前值小于记录值,更新记录值的位置,否则记录值位置不变                        k = j + 1;                    }                } else {                    if (src[k] > src[j + 1]) {                        k = j + 1;                    }                }            }            j = src[i];            src[i] = src[k];            src[k] = j;        }        return true;    }}

堆排序

  • 基本思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进。
//堆排序public static boolean heapSort(int[] src, boolean isAsc) {    if (src == null) {        return false;    } else {        //这里的处理方法是:        //将数组进行堆排序,得到第一个值为最大/小值;        //除去第一个值,对后面的数组再次堆排序,得到第二个值为二次数组的最大/小值,即该值为数组中的第二大/小值        //重复以上的方法直到数组被完全排序        for (int i = 0; i < src.length; i++) {            //依次建堆,得到一个堆后就排除1项重新建堆            buildHeap(src, i, src.length - 1, !isAsc);        }        return true;    }}
  • 堆的定义如下:具有n个元素的序列(h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
//建堆,将数组指定的索引范围内的数据进行建堆private static void buildHeap(int[] src, int startIndex, int endIndex, boolean isBiggest) {    if (src == null || startIndex > endIndex) {        return;    } else {        //计算出当前排序的数组长度        int length = endIndex - startIndex;        //得到当前计算数组的中间位置        int mid = (length - 1) / 2;        for (int i = mid; i >= 0; i--) {            //计算中间位置的左子树位置            int left = i * 2 + 1 + startIndex;            //计算中间位置的右子树位置            int right = left + 1;            //若左子树位置有效,则尝试比较左子树及其父节点并交换            if (left <= endIndex) {                swap(src, left, i + startIndex, isBiggest);            }            //若右子树位置有效,尝试比较右子树及其父节点并交换            if (right <= endIndex) {                swap(src, right, i + startIndex, isBiggest);            }        }    }}

这里的堆排序与定义上的排序是有一些不同的.定义上的堆排序是建成堆后,最后一个数据与堆顶交换;这里是建堆后取走第一个最值,其它的数据直接重新建成一个堆,重复直到堆中只有一个数据为止.(不存在堆顶数据与最后的数据进行交换)


冒泡排序

  • 基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
public static boolean bubbleSort(int[] src, boolean isAsc) {    if (src == null) {        return false;    } else {        boolean isFinish = false;        while (!isFinish) {            isFinish = true;            for (int i = 0; i + 1 < src.length; i++) {                if (swap(src, i, i + 1, isAsc)) {                    //只要进行过交换,就说明还没有排序完成,直到不再交换为止                    isFinish = false;                }            }        }        return true;    }}

快速排序

  • 基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
public static boolean quickSort(int[] src, boolean isAsc) {    if (src == null) {        return false;    } else {        //一趟扫描的递归操作        onceRecursionSort(src, 0, src.length - 1, isAsc);        return true;    }}

快速排序是基于一趟扫描的;一趟扫描是选择一个数值划分出数组的大小区域,每个区域再重复这个过程直到所有区域不再需要划分为止.
一趟快速排序的算法是:

  • 1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
  • 2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
  • 3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]互换;
  • 4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
  • 5)重复第3、4步,直到i>=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)
//一趟快速扫描private static void onceRecursionSort(int[] src, int left, int right, boolean isAsc) {    if (src == null || left > right) {        return;    } else {        //保存初始值        int stickLeft = left;        int stickRight = right;        //选择排序标准值        int key = src[stickLeft];        //进行一次大小值区分的排序        while (left < right) {            //从右边开始向左边扫描            while (left < right) {                //当前左右两个数值是否满足比较条件(此处为左<右)                if (!isCompareCorrect(src[right], key, isAsc)) {                    //右边数大于标准值时,检测索引左移                    right--;                    continue;                }                //否则交换数据,并且左边索引右移(因为左边已经与右边交换了,不再需要检测左边的值)                swap(src, left, right);                left++;                break;            }            //从左边开始向右边扫描            while (left < right) {                if (!isCompareCorrect(key, src[left], isAsc)) {                    left++;                    continue;                }                swap(src, left, right);                right--;                break;            }        }        //当中间值小于初始值时,说明没有完全排序完整,递归进行排序,重复每个区域的排序        if (stickLeft < right) {            onceRecursionSort(src, stickLeft, right, isAsc);        }        if (right + 1 < stickRight) {            onceRecursionSort(src, right + 1, stickRight, isAsc);        }    }}

源码

public class SortAlgorithm {    /**     * 插入排序<br/>     * 基本思想:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。     *     * @param src     */    public static boolean insertSort(int[] src, boolean isAsc) {        if (src != null) {            return insertSort(src, 0, src.length - 1, isAsc);        } else {            return false;        }    }    /**     * 指定数组的某一段进行排序     *     * @param src     * @param startIndex 开始排序的index     * @param endIndex   结束排序的index     * @param isAsc      是否升序     */    private static boolean insertSort(int[] src, int startIndex, int endIndex, boolean isAsc) {        if (src == null || startIndex > endIndex) {            return false;        } else {            int temp = 0;            for (int i = startIndex; i <= endIndex; i++) {                //取下一个位置的数据                for (int k = 0; k < i; k++) {                    swap(src, k, i, isAsc);                }            }            return true;        }    }    /**     * 希尔排序<br/>     * 基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。     *     * @param src     * @param isAsc 是否升序     * @return     */    public static boolean shellSort(int[] src, boolean isAsc) {        if (src == null) {            return false;        } else {            //计算一个增量            int increment = src.length / 4;            if (increment <= 0) {                //增量必须大于等于1                increment = 1;            }            //增量递减进行排序            for (int i = increment; i >= 1; i--) {                int j = 0;                //增量分组进行插入排序                for (; j + i < src.length; j += i) {                    insertSort(src, j, j + i, isAsc);                }                //如果数组不能完全按增量切分,最后部分也可以正常排序                insertSort(src, j, src.length - 1, isAsc);            }            return true;        }    }    /**     * 简单排序<br/>     * 基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。     *     * @param src     * @param isAsc 是否升序     * @return     */    public static boolean simpleSort(int src[], boolean isAsc) {        if (src == null) {            return false;        } else {            for (int i = 0; i < src.length; i++) {                //记录第N个要排序的位置                int j = i;                //记录当前最大/小值的位置                int k = j;                for (; j + 1 < src.length; j++) {                    if (isAsc) {                        if (src[k] > src[j + 1]) {                            //若当前值小于记录值,更新记录值的位置,否则记录值位置不变                            k = j + 1;                        }                    } else {                        if (src[k] > src[j + 1]) {                            k = j + 1;                        }                    }                }                j = src[i];                src[i] = src[k];                src[k] = j;            }            return true;        }    }    /**     * 堆排序<br/>     * 基本思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进。<br/>     * 堆的定义如下:具有n个元素的序列(h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。     *     * @param src     * @param isAsc 是否升序     * @return     */    public static boolean heapSort(int[] src, boolean isAsc) {        if (src == null) {            return false;        } else {            //这里的处理方法是:            //将数组进行堆排序,得到第一个值为最大/小值;            //除去第一个值,对后面的数组再次堆排序,得到第二个值为二次数组的最大/小值,即该值为数组中的第二大/小值            //重复以上的方法直到数组被完全排序            for (int i = 0; i < src.length; i++) {                //依次建堆,得到一个堆后就排除1项重新建堆                buildHeap(src, i, src.length - 1, !isAsc);            }            return true;        }    }    /**     * 冒泡排序<br/>     * 基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。     *     * @param src     * @param isAsc     * @return     */    public static boolean bubbleSort(int[] src, boolean isAsc) {        if (src == null) {            return false;        } else {            boolean isFinish = false;            while (!isFinish) {                isFinish = true;                for (int i = 0; i + 1 < src.length; i++) {                    if (swap(src, i, i + 1, isAsc)) {                        //只要进行过交换,就说明还没有排序完成,直到不再交换为止                        isFinish = false;                    }                }            }            return true;        }    }    /**     * 快速排序<br/>     * 基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。     *     * @param src     * @param isAsc     * @return     */    public static boolean quickSort(int[] src, boolean isAsc) {        if (src == null) {            return false;        } else {            //一趟扫描的递归操作            onceRecursionSort(src, 0, src.length - 1, isAsc);            return true;        }    }    /**     * 递归的一趟扫描<br/>     * 一趟快速排序的算法是:<br/>     * 1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;<br/>     * 2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];<br/>     * 3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;<br/>     * 4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;<br/>     * 5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。<br/>     *     * @param src     * @param left     * @param right     * @param isAsc     */    private static void onceRecursionSort(int[] src, int left, int right, boolean isAsc) {        if (src == null || left > right) {            return;        } else {            //保存初始值            int stickLeft = left;            int stickRight = right;            //选择排序标准值            int key = src[stickLeft];            while (left < right) {                //从右边开始向左边扫描                while (left < right) {                    //当前左右两个数值是否满足比较条件(此处为左<右)                    if (!isCompareCorrect(src[right], key, isAsc)) {                        //右边数大于标准值时,检测索引左移                        right--;                        continue;                    }                    //否则交换数据,并且左边索引右移(因为左边已经与右边交换了,不再需要检测左边的值)                    swap(src, left, right);                    left++;                    break;                }                //从左边开始向右边扫描                while (left < right) {                    if (!isCompareCorrect(key, src[left], isAsc)) {                        left++;                        continue;                    }                    swap(src, left, right);                    right--;                    break;                }            }            //当中间值小于初始值时,说明没有完全排序完整,递归进行排序            if (stickLeft < right) {                onceRecursionSort(src, stickLeft, right, isAsc);            }            if (right + 1 < stickRight) {                onceRecursionSort(src, right + 1, stickRight, isAsc);            }        }    }    /**     * 数组中从指定位置startIndex到endIndex建堆进行排序     * 结过排序后src[startIndex]位置的值为堆中最大/小值(其它位置的值并不确定)     *     * @param src     * @param startIndex 开始位置索引     * @param endIndex   结束位置索引     * @param isBiggest  是否大顶堆     */    private static void buildHeap(int[] src, int startIndex, int endIndex, boolean isBiggest) {        if (src == null || startIndex > endIndex) {            return;        } else {            //计算出当前排序的数组长度            int length = endIndex - startIndex;            //得到当前计算数组的中间位置            int mid = (length - 1) / 2;            for (int i = mid; i >= 0; i--) {                //计算中间位置的左子树位置                int left = i * 2 + 1 + startIndex;                //计算中间位置的右子树位置                int right = left + 1;                //若左子树位置有效,则尝试比较左子树及其父节点并交换                if (left <= endIndex) {                    swap(src, left, i + startIndex, isBiggest);                }                //若右子树位置有效,尝试比较右子树及其父节点并交换                if (right <= endIndex) {                    swap(src, right, i + startIndex, isBiggest);                }            }        }    }    /**     * 在满足要求时交换指定位置的两个索引的值     *     * @param src     * @param firstIndex     * @param secondIndex     * @param isFirstBiggerThanSecond 第一个数是否大于第二个数,当此参数为true时,若scr[firstIndex]>src[secondIndex],交换值,否则不操作返回false     * @return     */    private static boolean swap(int[] src, int firstIndex, int secondIndex, boolean isFirstBiggerThanSecond) {        int temp = 0;        boolean isChanged = false;        if (isFirstBiggerThanSecond && src[firstIndex] > src[secondIndex]) {            isChanged = true;        } else if (!isFirstBiggerThanSecond && src[firstIndex] < src[secondIndex]) {            isChanged = true;        }        if (isChanged) {            temp = src[firstIndex];            src[firstIndex] = src[secondIndex];            src[secondIndex] = temp;        }        return isChanged;    }    /**     * 交换指定位置的两个数值     *     * @param src     * @param firstIndex     * @param secondIndex     */    public static void swap(int[] src, int firstIndex, int secondIndex) {        int temp = 0;        temp = src[firstIndex];        src[firstIndex] = src[secondIndex];        src[secondIndex] = temp;w    }    /**     * 判断当前两个值的比较是否按要求正常     *     * @param firstInt     * @param secondInt     * @param isAsc     是否按升序排序,当此参数为true时,若第一个数&lt;第二个数,返回true,否则返回false     * @return     */    private static boolean isCompareCorrect(int firstInt, int secondInt, boolean isAsc) {        if (isAsc) {            return firstInt < secondInt;        } else {            return firstInt > secondInt;        }    }}
0 0