数据结构学习总结(八)排序

排序分类

稳定性: 排序后两个相等的键值的顺序和排序之前相同,则该排序方法稳定,否则不稳定。
稳定的排序算法:冒泡排序、简单选择排序、直接插入排序、归并排序。
不稳定的排序算法:希尔排序、堆排序、快速排序。

内排序与外排序: 根据在排序过程中待排序的记录是否全部被放置在内存中,将排序分为内排序与外排序两种,外排序需要在内外存之间多次交换数据才能进行。下面介绍的排序算法都是内排序。

根据排序过程中借助的主要操作,将内排序分为:插入排序、交换排序、选择排序、和归并排序四类。

插入排序类

直接插入排序

基本思想:将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。
直接插入排序

public int[] insert_sort(int[] sourceArray) {
    // 对 arr 进行拷贝,不改变参数内容
    int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

    // 从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
    for (int i = 1; i < arr.length; i++) {

        // 记录要插入的数据
        int tmp = arr[i];

        // 从已经排序的序列最右边的开始比较,找到比其小的数
        int j = i;
        while (j > 0 && tmp < arr[j - 1]) {
            arr[j] = arr[j - 1];
            j--;
        }

        // 存在比其小的数,插入
        if (j != i) {
            arr[j] = tmp;
        }

    }
    return arr;
}

希尔排序

希尔排序也称递减增量排序算法是插入排序的一种更高效的改进版本。

直接插入排序在记录本身就是基本有序时,我们只需要少量的插入操作,就可以完成整个记录集的排序工作,此时插入效率很高;还有就是在记录数比较少时,直接插入的优势也比较明显。

希尔排序就是基于直接插入的这两点不足来改进的。

基本思想:先将整个待排序的记录序列分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了,然后对这些子序列分别进行直接插入排序,待 整个序列都基本有序 时,再对全体记录进行一次直接插入排序。

所谓基本有序,就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间。

希尔排序

public int[] shell_sort(int[] sourceArray) {
    // 对 arr 进行拷贝,不改变参数内容
    int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

    int gap = 1;
    while (gap < arr.length) {
        gap = gap * 3 + 1;
    }

    while (gap > 0) {
        for (int i = gap; i < arr.length; i++) {
            int tmp = arr[i];
            int j = i - gap;
            while (j >= 0 && arr[j] > tmp) {
                arr[j + gap] = arr[j];
                j -= gap;
            }
            arr[j + gap] = tmp;
        }
        gap = (int) Math.floor(gap / 3);
    }

    return arr;
}

选择排序类

简单选择排序

基本思想:通过 n-i 次关键字间的比较,从 n-i+1 个记录中选出关键字最小的记录,并和第 i(1 ≤ i ≤ n)个记录交换之。选择排序

public int[] selection_sort(int[] sourceArray) {
    int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

    // 总共要经过 N-1 轮比较
    for (int i = 0; i < arr.length - 1; i++) {
        int min = i;

        // 每轮需要比较的次数 N-i
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[min]) {
                // 记录目前能找到的最小值元素的下标
                min = j;
            }
        }

        // 将找到的最小值和i位置所在的值进行交换
        if (i != min) {
            int tmp = arr[i];
            arr[i] = arr[min];
            arr[min] = tmp;
        }

    }
    return arr;
}

堆排序

堆排序是简单选择排序的一种改进算法。

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或大于)它的父节点。分为两种方法:

大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列。

基本思想:将待排序的序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的 n-1 个序列重新构造成一个大顶堆。如此反复执行,便能得到一个有序序列。

堆排序

public class HeapSort {

    public int[] sort(int[] sourceArray) {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int len = arr.length;

        buildMaxHeap(arr, len);

        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
        return arr;
    }

    private void buildMaxHeap(int[] arr, int len) {
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
            heapify(arr, i, len);
        }
    }

    private void heapify(int[] arr, int i, int len) {
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
            largest = left;
        }

        if (right < len && arr[right] > arr[largest]) {
            largest = right;
        }

        if (largest != i) {
            swap(arr, i, largest);
            heapify(arr, largest, len);
        }
    }

    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

交换排序类

冒泡排序

基本思想:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
冒泡排序

public int[] bubble_sort(int[] sourceArray) {
    // 对 arr 进行拷贝,不改变参数内容
    int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

    for (int i = 1; i < arr.length; i++) {
        // 设定一个标记,若为true,则表示此次循环没有进行交换,也就是待排序列已经有序,排序已经完成。
        boolean flag = true;

        for (int j = 0; j < arr.length - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;

                flag = false;
            }
        }

        if (flag) {
            break;
        }
    }
    return arr;
}

快速排序

快速排序是 C.R.A.Hoare 于 1962 年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。

分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。

快速排序是冒泡排序的升级版本,它们同属于交换排序类。即它也是通过不断比较和移动交换来实现排序的,只不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。

基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可以分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

快速排序

public class QuickSort {

    public int[] sort(int[] sourceArray) {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        return quickSort(arr, 0, arr.length - 1);
    }

    private int[] quickSort(int[] arr, int left, int right) {
        // 使用尾递归优化
        while (left < right) {
            int partitionIndex = partition(arr, left, right);
            quickSort(arr, left, partitionIndex - 1);
            left = partitionIndex + 1;
        }
        return arr;
    }

    private int partition(int[] arr, int left, int right) {
        // 设定基准值(pivot)
        int pivot = left;
        int index = pivot + 1;
        for (int i = index; i <= right; i++) {
            if (arr[i] < arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }
        swap(arr, pivot, index - 1);
        return index - 1;
    }

    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。

原理:假设初始序列含有 n 个记录,则可以看成是 n 个有序的子序列,每个子序列的长度为 1,然后两两归并,得到长度为 2 或 1 的有序子序列;再两两归并,… ,如此重复,直到得到一个长度为 n 的有序序列为止,这种排序方法称为 2 路归并排序。

归并排序

public class MergeSort {

    public int[] sort(int[] sourceArray) {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        if (arr.length < 2) {
            return arr;
        }
        int middle = (int) Math.floor(arr.length / 2);

        int[] left = Arrays.copyOfRange(arr, 0, middle);
        int[] right = Arrays.copyOfRange(arr, middle, arr.length);

        return merge(sort(left), sort(right));
    }

    protected int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        int i = 0;
        while (left.length > 0 && right.length > 0) {
            if (left[0] <= right[0]) {
                result[i++] = left[0];
                left = Arrays.copyOfRange(left, 1, left.length);
            } else {
                result[i++] = right[0];
                right = Arrays.copyOfRange(right, 1, right.length);
            }
        }

        while (left.length > 0) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        }

        while (right.length > 0) {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }

        return result;
    }

}

文章摘自 https://github.com/hustcc/JS-Sorting-Algorithm

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一些关于数据结构、内部算法与排序课程设计的建议。 首先,您需要确定该课程设计的目标和学习内容。您可以考虑以下几个方面: 1. 数据结构的基本原理和常用数据结构,如线性表、栈、队列、树、图等。 2. 内部算法的基本概念和分类,如贪心算法、分治算法、动态规划算法等。 3. 排序算法的基本原理和分类,如冒泡排序、选择排序、插入排序、快速排序、归并排序等。 4. 采用的编程语言和开发工具,如C++或Java编程语言,Visual Studio或Eclipse开发工具等。 5. 可能遇到的问题和解决方案,如算法复杂度分析、算法实现错误、排序算法优化等。 接下来,您可以根据以上考虑的内容,制定课程设计的详细计划。以下是一个可能的课程设计计划: 1. 第一周:介绍数据结构的基本原理和常用数据结构,讲解线性表、栈、队列、树、图等数据结构的实现和应用。 2. 第二周:学习内部算法的基本概念和分类,讲解贪心算法、分治算法、动态规划算法等算法的实现和应用。 3. 第三周:介绍排序算法的基本原理和分类,讲解冒泡排序、选择排序、插入排序、快速排序、归并排序排序算法的实现和应用。 4. 第四周:实现一个基于数据结构和内部算法的实际应用,如一个简单的迷宫求解器或文本编辑器。 5. 第五周:总结和回顾整个课程设计的过程,讨论可能的改进和优化方案。 希望这些建议能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值