排序算法(上)

排序的基本概念

算法优劣的描述

对于一个排序算法一般从以下三个方面来评估算法的优劣:

  • 时间复杂度:主要分析关键字的比较次数和记录的移动次数
  • 空间复杂度:分析排序算法所需要的额外内存
  • 稳定性:若两个记录的关键字的值相等,且排序后二者的相对次序没有发生变化,则次排序算法是稳定的,否则就不是稳定的。
内排和外排

内部排序和外部排序:整个过程不需要借助外部存储器,均在内存中完成,成为内部排序,否则成为外部排序。
外部排序需要将带排序的数据一部分一部分的读到内存,然后按照内部排序的方法,对该部分进行排,并将拍好的数据输出到外部存储器,不断重复这一过程,直到所有记录被处理;
将上一步排好序的记录一组一组的进行归并排序。

内排的分类

这里写图片描述

选择排序算法

选择排序算法是最简单的一种排序算法:首先选出数组中最小的那个 元素,然后和第一个元素进行交换(如果第一个元素就是最小的,那么和自己交换);然后在余下的元素中选出最小的元素和第二个元素交换,如此往复,直到所有的元素拍完序。因为这种方法在不断地选择剩余元素中最小的元素,因此叫做选择排序。

这里写图片描述

public class SelectSort {
    public static void selectSort(DataWrap[] data) {
        int N = data.length;// 数组长度
        for (int i = 0; i < N; i++) {
            int min = i;// 假定i为数组中的最小值
            for (int j = i + 1; j < N; j++) {
                // 如果第i位置的元素大于第j位置的元素,交换他们
                if (data[min].compareTo(data[j]) > 0) {
                    min = j;
                }
            }
            swap(data, i, min);
        }
    }

    // 交换两个元素
    private static void swap(DataWrap[] data, int i, int j) {

        DataWrap temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    public static void main(String[] args) {
        DataWrap[] data = { new DataWrap(21, " "), new DataWrap(49, " "),
                new DataWrap(30, " "), new DataWrap(16, " "),
                new DataWrap(30, "*"), new DataWrap(9, " ") };

        System.out.println("排序之前:\n"+java.util.Arrays.toString(data));
        selectSort(data);
        System.out.println("排序之后:\n"+java.util.Arrays.toString(data));
    }

}

待排序的数据如下:

public class DataWrap implements Comparable<DataWrap>{

    int data;
    String flag;

    public DataWrap(int data,String flag){
        this.data = data;
        this.flag = flag;
    }

    public String toString(){
        return data + flag;
    }

    //根据data的大小决定DataWrap的大小
    public int compareTo(DataWrap o) {
        return this.data > o.data ? 1 : (this.data == o.data ? 0 : -1);
    }

}

对于长度为N的数组,选择排序大约需要N*N/2次比较和N次交换。
选择排序有以下三个鲜明的特点:

  1. 运行时间和输入无关:上一次的循环对于本次的选择没有影响。其他算法更善于利用数据输入的初始状态。
  2. 数据移动是最小的:每次循环都会改变两个数组元素的值,因此只是用了N次交换。
  3. 选择排序是不稳定的。

堆排序算法

假设有n个元素的序列k1,k2,k3……kn-1,当且仅当满足如下 关系时成为小顶堆(小根堆),ki<=k2i+1且ki<=k2i+2(其中i=0,1,2,…,(n-1)/2);
当且仅当满足如下 关系时成为大顶堆(大根堆),ki>=k2i+1且ki>=k2i+2(其中i=0,1,2,…,(n-1)/2).
建堆的过程如下:
4. 先将数据转换为完全二叉树;
5. 将完全二叉树的最后一个非叶子节点,也即最后一个节点的父节点与它的左右子节点(如果存在)比较,将三者的最大值设为父节点;
6. 重复步骤二,向前处理上一个节点;
7. 如果某个节点交换后该节点又有子节点,那么还需要比较其子节点;
堆排序的步骤如下:

  1. 建堆;
  2. 将堆顶元素与最后一个元素交换。

这里写图片描述

public class HeapSort {

    public static void heapSort(DataWrap[] data) {
        int N = data.length;
        // 循环建堆
        for (int i = 0; i < N; i++) {
            buildMaxHeap(data, N - 1 - i);
            // 交换堆顶元素和最后一个元素
            swap(data, 0, N - 1 - i);
            System.out.println(java.util.Arrays.toString(data));
        }
    }

    // 对数组建堆
    private static void buildMaxHeap(DataWrap[] data, int lastIndex) {

        // 从最后一个节点的父节点开始
        for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
            // k保存当前正在判断的节点
            int k = i;
            // 如果当前k节点的子节点存在
            while (k * 2 + 1 <= lastIndex) {
                // k节点的左子节点索引
                int bigger = k * 2 + 1;
                // 如果右子节点比较大
                if(bigger < lastIndex){
                    if (data[bigger].compareTo(data[bigger + 1]) < 0) {
                        bigger = bigger + 1;
                    }
                }
                // 如果k节点的值小于其较大子节点的值
                if (data[k].compareTo(data[bigger]) < 0) {
                    swap(data, k, bigger);
                    k = bigger;
                } else {
                    break;
                }
            }
        }
    }

    // 交换两个元素
    private static void swap(DataWrap[] data, int i, int j) {

        DataWrap temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    public static void main(String[] args) {
        DataWrap[] data = { new DataWrap(21, " "), new DataWrap(49, " "), new DataWrap(30, " "), new DataWrap(16, " "),
                new DataWrap(30, "*"), new DataWrap(9, " ") };

        System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
        heapSort(data);
        System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
    }
}

堆排序有以下三个鲜明的特点:

  1. 对于具有n个元素的数组,需要进行n-1次建堆,建堆的时间复杂度为log2(n),每次建堆后需要进行一次交换,因此堆排序的时间复杂度为nlog2(n);
  2. 堆排序的空间利用率很高,为O(1);
  3. 堆排序是不稳定的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值