八种排序java实现

8种排序方法的java实现,均来自于算法导论的伪代码实现。

第一种:插入排序【看注释】

 * 插入排序:原址排序,比较排序
 * 
 * 时间复杂度:o(n²)
 * 
 * page-10
 * 
 *
 */
public class InsertSortArray {

    public static void main(String[] args) {
        int a[] = { 5, 2, 4, 6, 1, 3 };

        // 由于Java中除了基本数据类型是按值传递以外,其他的都是按引用传递的,修改的都是源数据。
        insertionSort(a);

        // 打印输出结果
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
    }

    /**
     * @param a:待排序的数组
     */
    private static void insertionSort(int[] a) {
        // 从第二个数开始插入比较
        for (int j = 1; j < a.length; j++) {
            // key代表即将插入的数字
            int key = a[j];
            // 从插入位置左边第一个位置开始比较
            int i = j - 1;
            // 一直向左移动,key小于谁,就放置在谁的左边第一个位置
            while (i >= 0 && a[i] > key) {
                // key需要插入当前位置的左边,则需要把比他大的向右移动
                a[i + 1] = a[i];
                i--;
            }
            // 跳出循环的时候,a[i]<key,a[i+1]=key是必须的
            a[i + 1] = key;
        }
    }

}

第二种:并归排序

/**
 * 归并排序:非原址排序,比较排序
 * 
 * 
 * 
 * 时间复杂度:o(nlogn)
 * 
 * 
 *
 */
public class MergeSortArray {

    /**
     * 
     * @param array:带排序的数组
     * @param start:需要排序数组的开始下标
     * @param end:需要排序数组的结束下标,array.length-1
     */
    private static void mergeSort(int[] array, int start, int end) {
        if (start < end) {
            // 1、二分数组
            int mid = (start + end) / 2;
            mergeSort(array, start, mid);
            mergeSort(array, mid + 1, end);

            // 2、合并数组
            merge(array, start, mid, end);

        }

    }

    /**
     * 合并数组
     * 
     * @param array
     * @param start
     * @param mid
     * @param end
     */
    private static void merge(int[] array, int start, int mid, int end) {
        // 得到左右两边数组的长度
        int left = mid - start + 1;
        int right = end - mid;

        // 创建一个缓存数组,分别存储左右数组的值
        // 数组的长度比实际的大1
        int left_array[] = new int[left + 1];
        int right_array[] = new int[right + 1];

        for (int i = 0; i < left; i++) {
            left_array[i] = array[start + i];
        }

        for (int i = 0; i < right; i++) {
            right_array[i] = array[mid + 1 + i];
        }

        // 最后一个对于的位置,存放数组结束表标志
        left_array[left] = Integer.MAX_VALUE;
        right_array[right] = Integer.MAX_VALUE;

        int i = 0;
        int j = 0;

        for (int k = start; k <= end; k++) {
            if (left_array[i] <= right_array[j]) {
                array[k] = left_array[i];
                i = i + 1;
            } else {
                array[k] = right_array[j];
                j = j + 1;
            }
        }

    }

    public static void main(String[] args) {
        int a[] = { 5, 2, 4, 7, 1, 3, 2, 6 };
        // 把数组指定的下标支架的数据排序
        // 如果是整个数组,则传入 0和length-1
        mergeSort(a, 3, 4);

        // 打印输出结果
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
    }
}

第三种:堆排序

/**
 * 堆排序:原址排序,比较排序
 * 
 * @author luopan
 *
 */
public class HeapSortArray {

    /**
     * 维护最大堆的性质函数
     * 
     * 注意:只能调整指定位置
     * 
     * 时间复杂度:o(logn),o(h)【h是树的高度】
     * 
     * @param array:这个数组存储的可能不是一个具有最大堆性质的,但是经过这个函数以后,返回一个具有最大堆性质的堆。
     * @param index:默认数组的下表是从0开始的
     */
    private static void max_heapify(int array[], int index, int heap_size) {
        int left = 2 * index + 1;// index的左孩子
        int right = 2 * (index + 1);// index的右孩子

        int largest = -1;
        // 左孩子比父结点的值大
        if (left < heap_size && array[left] > array[index]) {
            largest = left;
        } else {
            largest = index;
        }
        // 右孩子比largest点的值大
        if (right < heap_size && array[right] > array[largest]) {
            largest = right;
        }
        if (largest != index) {
            int tmp = array[index];
            array[index] = array[largest];
            array[largest] = tmp;
            max_heapify(array, largest, heap_size);
        }
    }

    /**
     * 传入一个数组,得到一个最大堆数组
     * 
     * @param array
     */
    private static void build_max_heap(int array[]) {
        int heap_size = array.length;
        for (int i = array.length / 2; i >= 0; i--) {
            max_heapify(array, i, heap_size);
        }
    }

    /**
     * 堆排序算法
     * 
     * @param array
     */
    private static void heap_sort(int array[]) {
        // 第一步,创建一个最大堆
        build_max_heap(array);

        int heap_size = array.length;

        // 数组的最大值一定在array[0]上
        for (int i = array.length - 1; i >= 1; i--) {
            // 把array[0]中最大数调整到array[i]中
            int tmp = array[0];
            array[0] = array[i];
            array[i] = tmp;
            // 然后把array[0...length-2]调整成最大堆
            heap_size--;
            max_heapify(array, 0, heap_size);

        }

    }

    public static void main(String[] args) {
        int a[] = { 1, 2, 3, 4, 7, 8, 9, 10, 14, 16 };
        // int a[] = { 35, 10, 20, 9, 8, 19, 17, 7, 6, 5, 4, 16, 15 };

        build_max_heap(a);
        System.out.println("最大堆如下:");
        for (int i = 0; i < a.length; i++) {
            if (i == a.length - 1) {
                System.out.print(a[i]);
            } else {
                System.out.print(a[i] + ",");
            }
        }

        heap_sort(a);
        System.out.println("\n堆排序以后的:");
        for (int i = 0; i < a.length; i++) {
            if (i == a.length - 1) {
                System.out.print(a[i]);
            } else {
                System.out.print(a[i] + ",");
            }
        }
    }
}

第四种:快速排序

/**
 * 快速排序,原址排序,比较排序
 * 
 * 最坏情况的时间复杂度是o(n²),期望时间复杂度是o(nlogn)
 * 
 * 快速排序也采用分治思想:
 * 
 * A[p..r]===>A[p..q-1]和A[q+1..r]
 * 
 * A[p..q-1]<A[q]且A[q+1..r]>A[q+1]
 * 
 * 中间值的选择是随机的,那么程序执行的时间复杂度就是随机的。
 * 
 */
public class QuickSortArray {

    /**
     * 快速排序
     * 
     * @param array
     * @param start:需要排序的开始下标
     * @param end:需要排序的结束下标
     */
    private static void quick_sort(int array[], int start, int end) {
        if (start < end) {
            int q = quick_partion(array, start, end);
            quick_sort(array, start, q - 1);
            quick_sort(array, q + 1, end);
        }
    }

    /**
     * 计算下标q成为快速排序最关键的一部分
     * 
     * @param array
     * @param start
     * @param end
     * @return 返回分割下标q
     */
    private static int quick_partion(int array[], int start, int end) {
        // 1、选取用于分割数组为两部分的中间值【真的是随机选取】
        int x = array[end];
        // 2、选取一个下标
        int i = start - 1;
        // 3、循环寻找适当的下标q【只能循环到end-1,因为end已经被使用了】
        for (int j = start; j < end; j++) {
            if (array[j] <= x) {// 找到比中间值x小的值
                i = i + 1;
                int tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
        // 4、在循环结束的时候,需要把中间值移动到适当的位置
        int tmp = array[end];
        array[end] = array[i + 1];
        array[i + 1] = tmp;
        return i + 1;
    }

    public static void main(String[] args) {
        // int a[] = { 1, 2, 3, 4, 7, 8, 9, 10, 14, 16 };
        int a[] = { 35, 10, 20, 9, 8, 19, 17, 7, 6, 5, 4, 16, 15 };

        quick_sort(a, 0, a.length - 1);

        System.out.println("快速排序以后:");
        for (int i = 0; i < a.length; i++) {
            if (i == a.length - 1) {
                System.out.print(a[i]);
            } else {
                System.out.print(a[i] + ",");
            }
        }
    }
}

第五种:快速排序的随机化版本

/**
 *
 * 快速排序的随机化版本
 * 
 * 与始终选取array[end]作为中间值不同,我们采用一种随机抽样技术,使得数组分割更加的平衡
 * 
 * i = RANDOM(start,end):从start到end之间随机选取一个下标
 * 
 * 然后,exchange array[i] 和 array[end]
 * 
 * 接下来就直接使用QuickSortArray的程序即可。
 *
 */
public class QuickRandomSortArray {
    public static void main(String[] args) {

    }
}

第六种:计数排序

/**
 * 计数排序:假设n个输入元素中的每个都在0-k区间,并且假设数组元素属于一个小区间内的整数
 * 
 * 对于每一个输入元素x,确定小于x的元素个数。
 * 
 * 利用这个信息,就可以直接把x放到它的输出数组的位置上了
 * 
 * 额外的tmpArray浪费了很多的空间,如果分布范围很大,但是很稀疏,则就有空间浪费的问题了。
 *
 */
public class CountingSortArray {

    /**
     * 计数排序
     * 
     * @param inArray:输入待排序数组
     * @param outArray:输出结果数组
     * @param k:数组的范围
     */
    private static void counting_sort(int inArray[], int outArray[], int k) {
        // 1、创建一个临时数组,并且全部赋值为0
        int tmpArray[] = new int[k + 1];
        for (int i = 0; i <= k; i++) {
            tmpArray[i] = 0;
        }

        // 2、统计输入数组的每个元素的频率
        for (int i = 0; i < inArray.length; i++) {
            tmpArray[inArray[i]] = tmpArray[inArray[i]] + 1;
        }

        // 3、统计出小于或者等i的个数
        for (int i = 1; i <= k; i++) {
            tmpArray[i] = tmpArray[i - 1] + tmpArray[i];
        }

        // 4、按照小于等于i的个数,确定每个元素的位置
        for (int i = inArray.length - 1; i >= 0; i--) {
            // 【注意】由于outArray在按照0的下标开始的,但是统计次数的时候是从1开始的,所以需要减去1
            outArray[tmpArray[inArray[i]] - 1] = inArray[i];
            tmpArray[inArray[i]] = tmpArray[inArray[i]] - 1;// 把统计数次减1,是为处理相同元素的手段
        }

    }

    public static void main(String[] args) {
        int a[] = { 2, 5, 3, 0, 2, 3, 0, 3 };

        int out[] = new int[a.length];

        counting_sort(a, out, 5);

        System.out.println("计数排序以后:");
        for (int i = 0; i < out.length; i++) {
            if (i == out.length - 1) {
                System.out.print(out[i]);
            } else {
                System.out.print(out[i] + ",");
            }
        }
    }
}

第七种:基数排序

/**
 * 基数排序:
 * 
 * 数组所有元素中位数最高d
 * 
 * 按照最低位有效进行排序,然后合并在一起。
 *
 */
public class RadixSortArray {

    /**
     * 计算得到数值指定位置的数字
     * 
     * @param num
     * @param pos:pos=1代表个位,pos=2代表十位以此类推
     * @return
     */
    private static int getNumInpos(int num, int pos) {
        int tmp = 1;
        for (int i = 0; i < pos - 1; i++) {
            tmp *= 10;
        }
        return (num / tmp) % 10;
    }

    /**
     * 
     * @param array
     * @param d
     */
    private static void radix_sort(int array[], int d) {
        int tmp[] = new int[array.length];

        // 下标0-9,分别代表0-9这10个数
        int radix[] = new int[10];

        // 从低位向最高位遍历,1代表个位,2代表十位,以此类推
        for (int i = 1; i <= d; i++) {
            // 1、把数组复制到缓存数组中
            System.arraycopy(array, 0, tmp, 0, array.length);
            Arrays.fill(radix, 0);

            // 2、遍历数组,获取个十百千万...关键字
            for (int j = 0; j < tmp.length; j++) {
                int key = getNumInpos(tmp[j], i);
                radix[key]++;
            }

            // 3、统计出小于或者等于的个数
            for (int j = 1; j < radix.length; j++) {
                radix[j] = radix[j - 1] + radix[j];
            }

            // System.out.print("第" + i + "位个数统计:");
            // for (int j = 0; j < radix.length; j++) {
            // System.out.print(radix[j] + " ");
            // }

            // 4、按照小于等于的个数,更新array的元素位置
            for (int m = array.length - 1; m >= 0; m--) {
                int key = getNumInpos(tmp[m], i);
                // 和计数排序是一样的,调整数组元素该存放的位置上即可
                array[radix[key] - 1] = tmp[m];
                radix[key]--;// 遇到相同的元素,我们需要把次数减1,然后供下次出现填充
            }

            // System.out.println();//换行调试
        }

    }

    public static void main(String[] args) {

        int array[] = { 329, 457, 657, 839, 436, 720, 355 };
        radix_sort(array, 3);
        System.out.println("计数排序以后:");
        for (int i = 0; i < array.length; i++) {
            if (i == array.length - 1) {
                System.out.print(array[i]);
            } else {
                System.out.print(array[i] + ",");
            }
        }
    }
}

第八种:桶排序

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

/**
 * 
 * 桶排序:假设输入数据服从均匀分布,这样的话排序的时间复杂度是:o(n)
 * 
 * 也就是说数组元素分布在:[0,1)区间
 * 
 * 桶排序将[0,1)区间划分为n个相同大小的区间,称为桶
 * 
 * 
 *
 */
public class BucketSortArray {

    /**
     * 桶排序数组
     * 
     * @param array
     */
    private static void bucket_sort(int array[]) {
        // 1、计算待排数组的最大值和最小值
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < array.length; i++) {
            max = Math.max(max, array[i]);
            min = Math.min(min, array[i]);
        }

        // 2、动态计算桶的数量,并且初始化
        int buckets = (max - min) / array.length + 1;
        ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<ArrayList<Integer>>(buckets);
        for (int i = 0; i < buckets; i++) {
            bucketArr.add(new ArrayList<Integer>());
        }

        // 3、将元素放进桶中,按照元素的大小进桶
        for (int i = 0; i < array.length; i++) {
            int num = (array[i] - min) / array.length;
            bucketArr.get(num).add(array[i]);
        }

        // 4、对每个桶分别排序
        for (int i = 0; i < bucketArr.size(); i++) {
            Collections.sort(bucketArr.get(i));
        }

        // 5、合并桶,并返回数组
        ArrayList<Integer> tmp;
        int index = 0;
        for (int i = 0; i < bucketArr.size(); i++) {
            tmp = bucketArr.get(i);
            for (int j = 0; j < tmp.size(); j++) {
                array[index] = tmp.get(j);
                index++;
            }
        }
    }

    public static void main(String[] args) {
        int a[] = { 102456, 9012, 39867, 68957, 83556, 19702 };

        bucket_sort(a);

        System.out.println("桶排序以后:");
        for (int i = 0; i < a.length; i++) {
            if (i == a.length - 1) {
                System.out.print(a[i]);
            } else {
                System.out.print(a[i] + ",");
            }
        }
    }
}

算法导论的前8章主要讲都是这些排序伪代码为例子。仅此记录笔记。一个星期的成果。

猜你喜欢

转载自blog.csdn.net/qq_36006553/article/details/72247541