数据结构算法

本文介绍了几种常见的排序算法,包括直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序以及基数排序,详细阐述了每种排序算法的原理、时间复杂度和空间复杂度,并提供了Java代码示例。这些排序算法在不同场景下有各自的优势,理解其工作方式有助于优化编程实践。
摘要由CSDN通过智能技术生成

直接插入排序

1.从第一个元素开始,该元素可以认为已经被排序
2.取下一个元素tem,从已排序的元素序列从后往前扫描
3.如果该元素大于tem,则将该元素移到下一位
4.重复步骤3,直到找到已排序元素中小于等于tem的元素
5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置

例如:从数字5开始排序
在这里插入图片描述
时间复杂度:最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序
      最好情况下为O(N),此时待排序列为升序,或者说接近升序。
空间复杂度:O(1)

代码:

 public static void main(String[] args) {
        int[] arr = {5, 2, 8, 3, 1, 6};
        System.out.println("Before sorting: " + Arrays.toString(arr));
        insertionSort(arr);
        System.out.println("After sorting: " + Arrays.toString(arr));
    }
    public static void insertionSort(int[] arr) {
    int n = arr.length;
    for (int i = 1; i < n; ++i) {
        int key = arr[i];
        for(int j = i -1 ;j>=0;j--){
                //如果当前元素小于他前边的那个元素,当前这个元素目前所在位置放置他前一个元素
                if(key<arr[j]){
                    arr[j+1] = arr[j];
                    arr[j] = key;
                }else {
                    break;
                }
            }

    }
}

希尔排序

1.先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作…
2.当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。
在这里插入图片描述
时间复杂度平均:O(N^1.3)
空间复杂度:O(1)

代码:

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

    public static void ShellSort(int[] array)
    {
        int n = array.length;
        int inc;//希尔增量
        //这里采用朴素希尔增量,就是每次增量都是原来的一半,直到增量为1为止
        for (inc = n / 2; inc >= 1; inc = inc / 2)
        {//每一次循环都通过不断缩短增量达到排序的效果
            //在一次循环内,inc的值是固定的
            //下面的内容和插入排序的原理是一样的,只不过每个待排序元素的间隔是inc
            for (int i = inc; i < n; i++)
            {//i为什么是从inc开始,而不是从0开始?
                //因为插入排序中把排序元素分为两组,A组为已排好序的,B组为未排好序要插入的
                //A组开始时往往是第一个元素(0),那么B组的第一个元素就是整个待拍序列的第二个元素了(inc)
                int temp = array[i];//temp存储要插入的值
                int j;
                for (j = i-inc; j >= 0 && array[j] > temp; j = j - inc)
                {//j从i-inc开始往前遍历,每一步的距离是inc
                    array[j+inc] = array[j];//如果当前遍历到的元素(这里说的遍历到的元素是(array[j])比待插入元素temp小,
                        //这个元素往后移动一位,后边的元素被元素覆盖
                        //一旦不满足条件,1.说明要么遍历到元素比temp小,这个时候所有比temp大的元素都后移完了
                        //2.遍历到头了,此时第一个元素就是要插入的地方
                }
                array[j+inc] = temp;
                //那么此时array[j+inc]也就是要插入的地方,把temp插入进去
                System.out.println(Arrays.toString(array));
            }
        }
//        System.out.println(Arrays.toString(array));
    }

直接选择排序

每次从待排序列中选出一个最小值,然后放在序列的起始位置,直到全部待排数据排完即可。
实际上,我们可以一趟选出两个值,一个最大值一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

  public static void main(String[] args) {

        int arrLength = 8;      //测试数组的长度
        /*
         * 获得随机arrLength个数的数组
         */
        int[] arr = new int[arrLength];
        for(int i = 0;i < arrLength;i++){
            //随机情况
            arr[i] = (int) (Math.random() * arrLength);
        }


        //排序前的输出
        for(int a:arr){
            System.out.print(a+" ");
        }
        System.out.println();


        choiceSort(arr);
        //排序后的输出
        for(int a:arr){
            System.out.print(a+" ");
        }

    }


    /**
     * 直接选择排序,每次选最小的放到最前
     */
    public static void choiceSort(int[] arr){

        if(arr == null || arr.length < 1){
            return;
        }

        int min = 0;
        int temp;
        /*只需要进行 n - 1 轮选择*/
        for(int i = 0;i<arr.length - 1;i++){
            min = i;                    //初始化当前最小的
            for(int j = i + 1;j<arr.length;j++){
                if(arr[min] > arr[j]){
                    min=j;                //记住最小元素下标
                }

            }

            //一轮后,当最小元素的下标不为i时交换位置
            if(min != i){
                temp = arr[i];
                arr[i] = arr[min];
                arr[min] = temp;
            }
            System.out.println(Arrays.toString(arr));
        }




    }

堆排序:

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

重复第二步,直到所有元素均排序完毕。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

   public static void heapSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        // 建堆。
        buildHeap(arr);
        int len = arr.length;
        while (len > 1) {
            // 把堆顶和最后一个元素交换。
            swap(arr, 0, len - 1);
            // 交换完之后,逻辑上去掉最后一个元素。
            len--;
            // 重新调整堆的顺序。
            heapfy(arr, 0 , len);

            // 把每一趟排序的结果也输出一下。
            print(arr);
        }
    }

    private static void buildHeap(int[] arr) {
        // 最后一个非叶子结点:2i + 1 >= arr.length  -->  i >= (arr.length - 1) / 2
        for (int i = (arr.length - 1) / 2 - 1; i >= 0; i--) {
            heapfy(arr, i, arr.length);
        }
    }

    // 调整堆的顺序,保持大顶堆。
    private static void heapfy(int[] arr, int i, int len) {
        while (true) {
            int maxPostion = i;
            int leftChild = 2 * i + 1;  // 左孩子索引。
            int rightChild = 2 * i + 2; // 右孩子索引。

            // 若左孩子大于最大值,则更新最大值。
            if (leftChild < len && arr[leftChild] > arr[maxPostion]) {
                maxPostion = leftChild;
            }

            // 若右孩子大于最大值,则更新最大值。
            if (rightChild < len && arr[rightChild] > arr[maxPostion]) {
                maxPostion = rightChild;
            }

            if (maxPostion == i) {
                break;  // 若已经是大顶堆了,则退出循环。
            } else {
                swap(arr, i, maxPostion);   // 若不是大顶堆,则交换位置。
                i = maxPostion;
            }
        }
    }

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

    public static void main(String[] args) {
        int[] arr = {6, 9, 1, 4, 5, 8, 7, 0, 2, 3};

        System.out.print("排序前:  ");
        print(arr);

        heapSort(arr);

        System.out.print("排序后:  ");
        print(arr);
    }

    // 打印数组
    public static void print(int[] arr) {
        if (arr == null)    return;

        for(int i : arr) {
            System.out.print(i + " ");
        }
        System.out.println();
    }

冒泡排序

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大\小的元素会经由交换慢慢"浮"到数列的顶端。

算法步骤

比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
在这里插入图片描述

 public static void main(String[] args) throws Exception {
        int[] arr = {11,44,23,67,88,65,34,48,9,12};
          sort(arr);
    }
    public static int[] sort(int[] sourceArray) throws Exception {
        // 对 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;
            }
            System.out.println(Arrays.toString(arr));
        }

        return arr;
    }

在这里插入图片描述在这里插入图片描述

基数排序

在这里插入图片描述
在这里插入图片描述

public class BasicData {
    public static void radixSort(int[] arr) {
        // 找到数组中的最大值,确定排序的轮数
        int max = Arrays.stream(arr).max().getAsInt();

        // 对每个位数进行排序
        for (int exp = 1; max / exp > 0; exp *= 10) {
            countingSort(arr, exp);
        }
    }

    private static void countingSort(int[] arr, int exp) {
        int n = arr.length;
        int[] output = new int[n];
        int[] count = new int[10];

        // 统计每个桶中元素的个数
        for (int i = 0; i < n; i++) {
            int index = (arr[i] / exp) % 10;
            count[index]++;
        }

        // 将count[i]更新为该位数小于等于i的元素个数
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // 从后向前遍历原数组,根据当前位数将元素放入对应的桶中
        for (int i = n - 1; i >= 0; i--) {
            int index = (arr[i] / exp) % 10;
            output[count[index] - 1] = arr[i];
            count[index]--;
        }

        // 将排序好的数组复制回原数组
        System.arraycopy(output, 0, arr, 0, n);
    }

    public static void main(String[] args) {
        int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
        System.out.println("Original array: " + Arrays.toString(arr));
        radixSort(arr);
        System.out.println("Sorted array: " + Arrays.toString(arr));
    }

总结:

冒泡排序(Bubble Sort): 冒泡排序是一种简单的排序算法,它反复地交换相邻的元素,将较大的元素逐渐“冒泡”到右侧。虽然冒泡排序易于理解,但在大型数据集上的效率相对较低。

选择排序(Selection Sort): 选择排序是一种简单的排序算法,它每次从未排序的部分选择最小(或最大)的元素,然后放到已排序部分的末尾。尽管选择排序的时间复杂度较高,但它在某些情况下仍然有用。

插入排序(Insertion Sort): 插入排序通过构建一个已排序的部分,并逐步将未排序的元素插入到正确的位置。它在小型数据集上表现不错,但对于大型数据集性能较差。

快速排序(Quick Sort): 快速排序是一种高效的分治算法,通过选择一个基准元素,将数组分为小于基准和大于基准的两部分,然后递归地对这两部分进行排序。快速排序在平均情况下表现出色,但最坏情况下可能导致性能下降。

归并排序(Merge Sort): 归并排序是一种稳定的排序算法,它将数组分成两部分,分别排序后再将它们合并在一起。归并排序的时间复杂度较稳定,适用于大型数据集。

堆排序(Heap Sort): 堆排序利用了二叉堆的性质,将数组看作一个二叉树,并使用堆来进行排序。虽然堆排序的平均性能较好,但实现相对复杂。

希尔排序(Shell Sort): 希尔排序是插入排序的改进版本,通过间隔较远的元素进行排序,逐步减小间隔,最终实现整体的有序。希尔排序在中等大小的数据集上效果较好

在计算机科学中,排序算法是解决数据排序问题的基本工具之一。不同的排序算法具有不同的特点和性能,选择适合场景的排序算法可以在实际应用中提高效率。冒泡、选择、插入排序等简单算法易于理解,适用于小规模数据。而快速排序、归并排序、堆排序等更为高效的算法适用于大规模数据集。希尔排序则在中等规模数据中表现良好。理解这些排序算法的工作原理和特性,有助于我们更好地优化代码,提高程序的性能。无论是优化代码执行还是进行算法分析,掌握排序算法都是计算机科学中的重要一步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Circ.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值