Wiesler

导航

八种基本排序(选择排序selectSort,冒泡排序bubbleSort,插入排序insertSort,shell希尔排序shellSort,快速排序quickSort,归并排序mergeSort,堆排序heapSort,基数排序radixSort )

摘要:选择排序selectSort,冒泡排序bubbleSort,插入排序insertSort,shell希尔排序shellSort,快速排序quickSort,归并排序mergeSort,堆排序heapSort,基数排序radixSort 

下述方法都是置于class里的静态方法,由main来调用的

public class XXXX {
    public static void main(String[] args) {
        int[] arr = {34, 5, 7, 12, 5, 34, 22, 11, 17, 76, 3, 5, 2, 87, 33, 51};
        xxxSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    /*你的排序算法的代码在这个位置定义*/   
}

 

选择排序:每个元素都和自己后边的相邻元素做比较,外循环和冒泡排序一样

    //选择排序
    public static void selectSort (int[] arr) {
        for (int i = 0; i < arr.length - 1; i ++) {
            for (int j = i + 1; j < arr.length; j ++) {
                if (arr[i] > arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }

 

冒泡排序:相邻元素比较,外循环表示要比较几轮,内循环表示每一轮的比较次数

   public static void bubbleSort (int[] arr) {
        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]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

 

插入排序: 假定索引为0的元素已经排好序了,后边的元素和前边的元素比较,如果后边的元素小就插入在前边

    public static void insertSort(int arr[]){
        int length = arr.length; // 数组长度
        for (int i = 1; i < length; i ++) { // i从下标为1的元素开始遍历. 插入排序就是假定索引为0的元素已经排好序了,所以从索引为1的开始
            for (int j = i - 1; j >= 0; j --) { // 与自己之前的元素比较,如果之前的元素大于自己,那么就替换
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

 

 

希尔排序:优化了的插入排序。将整个数组,分割成相同长度的几份,分别比较对应的数字大小;然后再进行分割,再比较大小....; 最后是简单的插入排序, 省去了一些比较时间

    public static void shellSort(int[] arr) {
        for (int increment = arr.length / 2; increment > 0; increment /= 2) {//设置了每次比较分组的步长
            for (int i = increment; i < arr.length; i += increment) {//以下代码都和插入排序一样了, 只不过现在互相比较的两个数字的间隔是increment而不是1
                for (int j = i - increment; j >= 0; j -= increment) {
                    if (arr[j] > arr[j + increment]) {
                        int temp = arr[j];
                        arr[j] = arr[j + increment];
                        arr[j + increment] = temp;
                    }
                }
            }
        }
    }

 

快速排序:把整个数组第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。 

整个方法由三个方法组成,getMiddle计算中轴,divide分割数组, quickSort启动排序

    //通过动态的挪移元素位置来查找中轴. 中轴的意义就是要将数组分成两部分,左边的元素都小于中轴,右边的元素都大于中轴
    public static int getMiddle (int[] arr, int low, int high) {
        int temp = arr[low]; // 假定中轴的位置刚开始为数组的初始位置,同时也保存了这个中轴的元素,作为比较
        while (low < high) { //挪移过程中会随时改变low低位和high高位的位置,限制循环的截止条件
            while (low < high && arr[high] >= temp) { //如果高位的元素 >= 假定中轴的元素,合乎我们的目的
                high --;//让高位往前挪一步,考察低一位的元素
            }
            arr[low] = arr[high];//一旦发现高位的元素 < 假定中轴的元素,那么就把这个元素挪到低位的位置
            //同时也结束了我们对高位元素的考察
            //注意这时候中轴的这个元素也等于高位的元素,高位的那个元素还在那里,替换并没有完成
            while (low < high && arr[low] < temp) {//从低位元素开始考察,如果低位元素 < 假定中轴元素,合乎我们的目的
                low ++; //让低位往后挪一步,考察更高位的元素
            }
            arr[high] = arr[low];//如果一旦有低位的元素 >= 中轴的元素,那么就把这个元素挪到高位的位置
            //注意这个时候替换才完成,将假定中轴的元素赋给了那个高位
        }//那么本次的挪移就结束了,每次调用该方法最多都会调整两个元素的位置,找到一个高位里面不合要求的,找到一个低位里不合要求的,
        //把两个互换
        arr[low] = temp;//对于元素的一轮考察,挪移结束以后,那么中轴的位置还是在这个位置
        return low;
    }
    public static void divide (int[] arr, int low, int high) {
        if (low < high) {
            int middle = getMiddle(arr, low, high);
            divide(arr, low, middle - 1);
            divide(arr, middle + 1, high);
        }
    }
    public static void quickSort (int[] arr) {
        if (arr.length > 0) {
            divide(arr, 0, arr.length - 1);
        }
    }

 

 

归并排序:

将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

整个方法由两个方法构成,mergeSort(arr, low, high) 也就是arr从0到arr.length-1来调用,merge(arr, low, mid, high) 归并数组的方法

    /**
     * 归并排序
     * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
     * 时间复杂度为O(nlogn)
     * 稳定排序方式
     * @param arr 待排序数组
     * @return 输出有序数组
     */
    public static int[] mergeSort(int[] arr, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            mergeSort(arr, low, mid);
            // 右边
            mergeSort(arr, mid + 1, high);
            // 左右归并
            merge(arr, low, mid, high);
        }
        return arr;
    }

    /**
     * 将数组中low到high位置的数进行排序
     * @param arr 待排序数组
     * @param low 待排的开始位置
     * @param mid 待排中间位置
     * @param high 待排结束位置
     */
    public static void merge(int[] arr, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;
        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (arr[i] < arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }
        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = arr[i++];
        }
        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = arr[j++];
        }
        // 把新数组中的数覆盖nums数组, 这一步也可以在Java中由System.arraycopy方法完成
        // System.arraycopy(temp, 0, nums, low, temp.length);
        for (int k2 = 0; k2 < temp.length; k2++) {
            arr[k2 + low] = temp[k2];
        }
    }

 

 

堆排序:

* 根据现有的数组,建立一个堆,堆顶的元素最大,最下层的元素最小,然后将堆顶的元素和最下层最小的元素交换位置。
* 这时最大的元素跑到了堆的末尾,从堆中去除这个最大的元素,记录这个元素为最大,需要被放置在排序后的数组的末尾,其余的元素都要在它之前插入。
* 然后再把堆重新调整(让现在的堆中最大的元素跑到堆顶,最小的元素位于堆底)再把堆顶元素取出
* 就是每次取出一个最大的元素,调整堆,插入最后的数组的前边

整个方法由heapSort(arr)来调用,配合的方法有 getLeftChildIndex, getRightChildIndex, getParentIndex, buildMaxHeapify和maxHeapify

    //注意:<<左移运算就是相当于十进制的*2,>>右移运算就是相当于十进制的/2, 带符号右移,正数高位补0,负数高位补1
    //父节点索引
    public static int getParentIndex (int current) {
        return (current - 1) >> 1;//简单来说就是 i / 2, 因为是int型的,奇数除不尽就直接省略小数部分
    }
    //左子节点索引
    public static int getLeftChildIndex (int current) {
        return (current << 1) + 1;//简单说就是current * 2 + 1
    }
    //右子节点索引
    public static int getRightChildIndex (int current) {
        return (current << 1) + 2;//简单说就是current * 2 + 2
    }
    public static void buildMaxHeapify(int[] arr) {
        //没有子节点的才需要创建最大堆,从最后一个的父节点开始
        int startIndex = getParentIndex(arr.length - 1);
        //从尾端开始创建最大堆,每次都是正确的堆
        for (int i = startIndex; i >= 0; i --) {
            maxHeapify(arr, arr.length, i);
        }
    }
    /**
     * 创建最大堆
     * @param arr
     * @param heapSize 需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了
     * @param index 当前需要创建最大堆的位置
     */
    public static void maxHeapify(int[] arr, int heapSize, int index) {//最开始传入的index是数组最后元素的索引,假想该位置元素最大
        //当前点与左右子节点比较
        int left = getLeftChildIndex(index);//找出该节点的左子节点索引
        int right = getRightChildIndex(index);//找出该节点的右子节点索引
        int largest = index;//假定index位置的元素最大
        if (left < heapSize && arr[largest] < arr[left]) {//发现这个假定为最大值的父节点比左子节点元素小
            largest = left;
        }
        if (right < heapSize && arr[largest] < arr[right]) {//发现这个假定为最大值的父节点比右子节点元素小
            largest = right;
        }
        //经过比较,如果子节点索引表示的元素大,那么largest就应该等于这个最大的子节点,交换数据
        if (largest != index) {
            int temp = arr[largest];
            arr[largest] = arr[index];
            arr[index] = temp;
            maxHeapify(arr, heapSize, largest);//再继续调整堆的元素,这个时候就把上一轮选出的最大值置于父节点位置
        }
    }
    /**
     * 排序,最大值放末尾,arr虽然是最大堆,在排序后就成了递增的了
     * @param arr 需要排序的数组
     * */
    public static void heapSort (int[] arr) {//对该函数的解释如下图
        //调用了buildHeapify生成了最大堆之后的特性决定了,堆顶也就是最大元素在左边,堆底在右边
        //末尾与头交换,交换后调整最大堆
        for (int i = arr.length - 1; i > 0; i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;//堆顶堆底元素互换,此时堆顶位于最右边,符合我们的排序期望
            maxHeapify(arr, i, 0);//那么剩余的元素再调整堆
        }
    }
/*
调用buildHeapify之后生成的数组是[90, 89, 14, 58, 87, 11, 7, 13, 56, 5, 32, 2, 1, 7, 4, 5, 3]
它表示的堆如下
                                    90
                    89                              14

            58              87             11               7

        56      5       32      2       1       7       4       5

 3
可以看出堆中最大的元素排到了最左边
那么我们之后调用heapSort的时候,
    1 把最左边也就是堆顶的元素和堆末尾的元素调换位置
    2 这时候最大的元素就在最右边了,那么剩余的元素组成的堆,很显然应该让最大的元素位于堆顶,所以让剩余的元素重新调整堆的结构

*/

 

 

基数排序: 基数排序又称桶排序。先按照数组中数字的个位数排列,再按照十位数排列....

    public static void radixSort (int[] arr) {
        int maxDigit = 0;//获取数组中的数最多有几位数
        for (int i: arr) {//将数组中的每个数转成字符串,记录并比较他们的长度即可
            maxDigit = Math.max(maxDigit, Integer.toString(i).length());
        }
        int[][] bucket = new int[10][arr.length];//数组的第一位表示
        int[] order = new int[10];//数组order[i] 用来表示该位是i的数的个数
        int n = 1;//一个参数取每个位数时使用,一个整数/1 % 10 表示取个位数,整数/10 % 10 表示取十位数...
        for (int m = 1; m <= maxDigit; m ++, n *= 10) {//m 表示先从哪个位数开始排序,1表示个位数,一直到maxDigit最大数位
            //每次循环结束都要   m++ 比较下一个数位,同时n *= 10 方便取出这个数位的数字
            for (int i = 0; i < arr.length; i ++) {
                int lsd = ((arr[i] / n) % 10);//取出数组中每个数的每一位,从个位数...十位数...百位数...
                bucket[lsd][order[lsd]] = arr[i];//每个数应该中放置在二维数组中的什么位置
                order[lsd] ++;//该位是lsd的数的个数+1
            }
            for (int i = 0, k = 0; i < 10; i ++) {// 一轮排序结束,将桶,也就是二维数组中保存的数重新写入原数组,以供下一轮排序使用
                //i 表示的是 每一个数位的数都是从0~9, 也就是说,十位数是0~9的循环遍历一遍; k 表示的是数组的索引,循环赋值数组0 ~ arr.length
                if (order[i] != 0) {//该数位表示的数的个数不是0,也就是说,比如个位数是8的元素存在
                    for(int j = 0; j < order[i]; j ++, k ++) {
                        arr[k] = bucket[i][j];
                    }
                }
                order[i] = 0;//因为order[]存放的是每一轮比较后每个数位的个数,所以一轮比较结束,并重新赋值后要将该数位清空
            }
        }
    }

................... 

 

posted on 2016-09-25 08:54  Wiesler  阅读(1253)  评论(0)    收藏  举报