排序算法总结

来源:互联网 发布:纵横造价软件多少钱 编辑:程序博客网 时间:2024/05/20 02:53
本文总结了基本的排序算法,包括选择排序,插入排序,冒泡排序,快速排序,堆排序,归并排序,以及,计数排序和基数排序。前六种排序算法是基于比较算法,时间复杂度的下界为o(nlgn),后两种不是基于比较的排序算法,时间复杂度为线性o(n)。
 
一、堆排序
堆排序的过程是先初始化一个大顶堆,然后堆顶与最后元素交换位置,交换之后再调整为大顶堆。时间复杂度o(nlgn),不稳定排序。
package heap;
/**
 * @author typ
 * 
 */
public class HeapSort {
    /**
     * 初始化时候,建立一个堆
     * 
     * @param a
     */
    public void buildHeap(int[] a) {
        int n = a.length;
        for (int i = n / 2 - 1; i >= 0; --i) { // (n / 2 - 1), 就是最后一个有子节点的元素
            heapify(a, i, n - 1); // 从底往上,不断调整,直到整个数组变成堆
        }
    }
    /**
     * 完成从a[i]开始对堆的调整
     * 
     * @param a
     * @param i
     * @param j
     */
    public void heapify(int[] a, int i, int j) {// j代表待排序列的元素个数
        int left = i * 2 + 1;
        int right = i * 2 + 2;
        if (left > j) { // 没有左子树,那么(也不会有右子树),递归停止
            return;
        }
        int large = left; // large 是左子树和右子树中的较大者
        if (right <= j && a[left] < a[right]) {
            large = right;
        }
        if (a[i] < a[large]) { // 若根元素比 large 小, 交换根和 large
            swap(a, i, large);
            heapify(a, large, j); // 此时 large 树 又可能不是堆了, 那就迭代实现large树成堆
        }
    }
    /**
     * 堆排序
     * 
     * @param a
     */
    public void heapSort(int a[]) {
        buildHeap(a);
        int size = a.length;
        // 每次去第一个元素,即最大元素,与最后元素交换位置。
        for (int i = 0; i < size; i++) {
            swap(a, 0, (size - i - 1));
            heapify(a, 0, size - i - 2);
        }
    }
    public void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
    public static void main(String[] args) {
        int a[] = { 2, 5, 1, 7, 9, 4, 6, 0 };
        HeapSort hSort = new HeapSort();
        hSort.heapSort(a);
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }
    }
}
非递归的heapify,只改动一处,见红色语句
public void heapify(int[] a, int i, int j) {
        while (true) {
            int left = i * 2 + 1;
            int right = i * 2 + 2;
            if (left > j) { // 没有左子树,那么(也不会有右子树),递归停止
                break;
            }
            int large = left; // large 是左子树和右子树中的较大者
            if (right <= j && a[left] < a[right]) {
                large = right;
            }
            if (a[i] < a[large]) { // 若根元素比 large 小, 交换根和 large
                swap(a, i, large);
                i = large;//此处将i值改为large传回,可以代替下面递归语句
                // heapify(a, large, j); // 此时 large 树 又可能不是堆了, 那就迭代实现large树成堆
            }else {
                break;//若根元素比large大,则不需要调整,跳出循环体
            }
        }
    }
二、快速排序
快速排序基于分治策略,是由一趟趟的排序组成。选取一个元素为基准元素,经过交换操作,使得该基准元素左边的元素都小于基准元素,右边的元素都大于基准元素,这样就完成了一趟排序。然后递归基准元素左右两部分。最好时间复杂度o(nlgn),最坏时间复杂度o(n2)。不稳定排序。
package quicksort;
/**
 * @author typ
 * 
 */
public class QuickSort {
    static int[] quickSort(int a[], int start, int end) {
        int i, j;
        i = start;
        j = end;
        int key = a[start];// 取key为序列的第一个元素
        while (i < j) {// 整个while循环完成一趟排序
            while (i < j && key <= a[j])
                // 右侧扫描
                j--;
            if (i < j) { // 找出第一个比key小的,交换位置
                a[i] = a[j];
            }
            while (i < j && a[i] < key)
                // 左侧扫描
                i++;
            if (i < j) { // 找出第一个比key大的,交换位置
                a[j] = a[i];
            }
        }
        a[i] = key;// 注意这步赋值操作,将最后的元素赋key
        if (i - start > 1) {
            quickSort(a, 0, i - 1);// 递归调用,把key前面的完成排序
        }
        if (end - j > 1) {
            quickSort(a, j + 1, end); // 递归调用,把key后面的完成排序
        }
        return a;
    }
    public static void main(String[] args) {
        int a[] = { 6, 5, 4, 3, 2, 1 };
        int b[] = quickSort(a, 0, 5);
        for (int item : b) {
            System.out.println(item);
        }
    }
}
 
三、插入排序
不断地将后续元素插入到前面已经排好序的元素中。时间复杂度o(n2),稳定排序。
package insertsort;
/**
 * @author typ
 * 
 */
public class InsertSort {
    static void insertSort(int a[]) {
        int i, j;
        for (i = 1; i < a.length; i++) {
            int key = a[i];// key的引入,减少交换次数
            for (j = i; j > 0 && a[j - 1] > key; j--) {
                a[j] = a[j - 1];
            }
            a[j] = key;
        }
    }
    public static void main(String[] args) {
        int a[] = { 6, 5, 4, 3, 2, 1 };
        insertSort(a);
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }
    }
}
四、选择排序
第i次循环将选出第i大的元素排列到i位置时间复杂度o(n2),不稳定。
package selectsort;
/**
 * @author typ
 * 
 */
public class SelectSort {
    static void selectSort(int a[]) {
        int size = a.length, k, tmp;
        for (int i = 0; i < size - 1; i++) {
            k = i;//用k来记录每次最小的数的位置
            for (int j = i; j < size; j++) {
                if (a[j] < a[k]) {
                    k = j;
                }
            }
            if (k != i) {
                tmp = a[i];
                a[i] = a[k];
                a[k] = tmp;
            }
        }
    }
    public static void main(String[] args) {
        int a[] = { 6, 5, 4, 3, 2, 1 };
        selectSort(a);
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }
    }
}
五、归并排序
对有序的两个序列进行两两合并,最小的有序序列只含有单个元素。时间复杂度o(nlgn)。稳定排序。
package mergesort;
/**
 * @author typ
 * 
 */
public class MergeSort {
    /**
     * 递归实现归并排序,调用merge函数(合并两个有序集)
     * 
     * @param a
     * @param b
     * @param low
     * @param high
     */
    static void mergeSort(int a[], int b[], int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            mergeSort(a, b, low, mid);
            mergeSort(a, b, mid + 1, high);
            merge(a, b, low, mid, high);// 合并两个有序集
        }
    }
    /**
     * 合并两个有序集
     * 
     * @param a
     * @param b
     * @param min
     * @param mid
     * @param max
     */
    static void merge(int a[], int b[], int min, int mid, int max) {
        int i = min, j = mid + 1, k = min;
        while (i <= mid && j <= max) {
            if (a[i] < a[j])
                b[k++] = a[i++];
            else
                b[k++] = a[j++];
        }
        while (i <= mid) {
            b[k++] = a[i++];
        }
        while (j <= max) {
            b[k++] = a[j++];
        }
        for (int m = min; m <= max; m++) {
            a[m] = b[m];
        }
    }
    public static void main(String[] args) {
        int a[] = { 5, 2, 7, 1, 9, 4, 6, 0 };
        int b[] = new int[a.length];
        int size = a.length;
        mergeSort(a, b, 0, size - 1);
        for (int i = 0; i < size; i++) {
            System.out.println(a[i]);
        }
    }
}
六、冒泡排序
第i趟排序,使得第i小的元素排到i处,时间复杂度o(n2)。稳定排序。
package bubblesort;
/**
 * @author typ
 * 
 */
public class BubbleSort {
    /**
     * 冒泡排序
     * 
     * @param a
     */
    static void bubbleSort(int a[]) {
        int size = a.length;
        for (int i = 1; i < size; i++) {
            for (int j = 0; j < size - i; j++) {
                if (a[j] > a[j + 1]) {
                    swap(a, j);
                }
            }
        }
    }
    /**
     * 交换a[j]和a[j+1]
     * 
     * @param a
     * @param j
     */
    static void swap(int a[], int j) {
        int tmp;
        tmp = a[j];
        a[j] = a[j + 1];
        a[j + 1] = tmp;
    }
    public static void main(String[] args) {
        int a[] = { 5, 2, 7, 1, 9, 4, 6, 0 };
        bubbleSort(a);
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }
    }
}
七、计数排序
可以在线性时间内完成排序,要求序列的取值小于某个自然数k。时间复杂度o(n),稳定排序。
package countsort;
/**
 * 计数排序,数组的取值范围为0-k,没输入一个元素x,确定小于x的元素的个数,直接将x放到输出数组的指定位置
 * 
 * @author typ
 * 
 */
public class CountSort {
    static void countSort(int a[], int k) {
        int c[] = new int[k];// c[i]存放数组a中小于等于i的元素的个数
        int b[] = new int[a.length];// b用于存放排好序的a的元素。
        for (int i = 0; i < a.length; i++) {// c[i]为数组a中值等于i的元素的个数
            c[a[i]] = c[a[i]] + 1;
        }
        for (int i = 1; i < k; i++) {// c[i]为数组a中值小于等于i的元素的个数
            c[i] = c[i] + c[i - 1];
        }
        for (int i = a.length - 1; i >= 0; i--) {// b中存放排好序的a的元素。为保证排序稳定性,从大到小循环
            int index1 = a[i];
            int index2 = c[a[i]] - 1;
            b[index2] = a[i];// (a中的每个元素在b中的位置已经定下了,在c中保存着,即a[i]应该放在b中的c[a[i]]-1处)
            c[index1] -= 1;
        }
        for (int i = 0; i < b.length; i++) {// 将排好序的数组b赋值给数组a
            a[i] = b[i];
        }
    }
    public static void main(String[] args) {
        int k = 10;// 代表数组的最大值为k
        int a[] = { 3, 1, 4, 4, 2, 1, 7, 8 };
        countSort(a, k);
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }
    }
}
 
八、基数排序
通过对元素由低位到高位的按位排序,实现整体的排序。每次的位排序要求排序是稳定的,通常调用计数排序作为基数排序位排序算法。可以实现线性时间o(n)。稳定
package radixsort;
/**
 * 基数排序,通过对序列元素每一位的排序,最终形成元素的排序,并不对元素直接进行大小比较。
 * 该基数排序中,用到的中间排序算法(对位的排序算法)为计数排序
 * 
 * @author typ
 * 
 */
public class RadixSort {
    /**
     * 被调函数-完成计数排序
     * @param a
     * @param k
     * @param num
     */
    static void countSort(int a[], int k, int num) {
        int c[] = new int[k];// c[i]存放数组a中小于等于i的元素的个数
        int b[] = new int[a.length];// b用于存放排好序的a的元素。
        for (int i = 0; i < a.length; i++) {// c[i]为数组a中元素的相应位数值等于i的元素的个数
            int index = a[i] / num % 10;// index为a[i]的某位
            c[index] = c[index] + 1;
        }
        for (int i = 1; i < k; i++) {// c[i]为数组a中值小于等于i的元素的个数
            c[i] = c[i] + c[i - 1];
        }
        for (int i = a.length - 1; i >= 0; i--) {// b中存放排好序的a的元素。
            int index1 = a[i] / num % 10;
            int index2 = c[a[i] / num % 10];
            b[index2 - 1] = a[i];// (a中的每个元素在b中的位置已经定下了,在c中保存着,即a[i]应该放在b中的c[a[i]]-1处)
            c[index1] -= 1;
        }
        for (int i = 0; i < b.length; i++) {// 将排好序的数组b赋值给数组a
            a[i] = b[i];
        }
    }
    /**
     * 基数排序的主调函数
     * @param a
     * @param k
     * @param num
     */
    static void radixSort(int a[], int k, int num) {
        for (int i = 0; i < num; i++) {
            countSort(a, k, (int) Math.pow(10, i));// 对a中元素的每一位都调用一次计数排序,且由低位到高位
        }
    }
    public static void main(String[] args) {
        int k = 9;// 代表数组的最大值为k
        int num = 3;// 表示a数组中数据的最大位数
        int a[] = { 23, 231, 524, 114, 332, 121, 227, 448 };
        radixSort(a, k, num);
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }
    }
}
 
 
 
 
 
 
 
 

原创粉丝点击