Java数据结构学习DAY7——排序

Java数据结构学习DAY7——排序

1. 概念

1.1 排序

排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。平时的上下文中,如果提到排序,通常指的是排升序(非降序)。通常意义上的排序,都是指的原地排序(in place sort)。

1.2 稳定性(重要)

两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序算法。

2. 七大基于比较的排序

在这里插入图片描述

2.1. 插入排序

2.1.1 直接插入排序

1、原理

整个区间被分为

  • 有序区间
  • 无序区间
    每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入
2、 实现
  • 代码
package Java42_0321;

import java.util.Arrays;

public class MySort {

    //1.插入排序是一个稳定排序,时间复杂度 O(N^2),空间复杂度O(1)。(只有三个额外的临时变量,bound, v, cur)
    public static  void insetSort(int [] arr) {
        //这个循环控制代码进行N次插入过程
        int bound = 1;
        for (;bound < arr.length ; bound++) {
            //循环内部实现插入一次的过程
            //需要找到待排序区间的一个元素,放哪里合适,并且搬运
            //已排序区间:【 0,bound)
            //带排序区间【bound length)
            //出的 v 就是第一个人元素。要插入的元素
            int v = arr[bound];
            int cur = bound-1;
            for(; cur >= 0; cur--){
                if (arr[cur] < v){ //改成小于,就是降序排序。(不加等于号,排序稳定,若加了等于号就不是稳定排序了)
                    //如果 cur 大于 v
                    //将 v 插入 arr[cur] 的前面
                    //将 arr[cur] 搬运位置
                    arr[cur+1] = arr[cur];
                } else {
                    //此时找到了要排序的位置
                 break;
                }
            }
            arr[cur +1] = v;
        }

    }

    public static void main(String[] args) {
        int[] arr={9,5,3,7,2,8};
        insetSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

-结果
在这里插入图片描述

3、性能分析

插入排序是一个稳定排序
时间复杂度 O(N^2)
空间复杂度O(1)(只有三个额外的临时变量,bound, v, cur)

2.1.2 希尔排序

1、 原理

希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

  • 希尔排序是对直接插入排序的优化。
  • 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
2、 实现
  • 代码实现
package Java42_0323;
import java.util.Arrays;
public class MySort {

    //2.希尔排序,空间复杂度 O(1),不稳定排序。
    public static void shellSort(int[] arr){
        //现制定 gap 序列,此处使用希尔序列
        int gap = arr.length/2;
        while(gap >= 1) {
            //通过这个辅助方法,进行分组插排
            _shellSort(arr,gap);
            gap = gap / 2;
        }

    }
    public static void _shellSort(int[] arr,int gap ) {
        //分组的时候,同组中的相邻元素的下表差就是 gap
        //注意取的顺序,先取 0 组元素中的第 1 个元素,尝试往前插入排序
        //注意取的顺序,先取 1 组元素中的第 1 个元素,尝试往前插入排序
        //注意取的顺序,先取 2 组元素中的第 1 个元素,尝试往前插入排序
        //....
        //再取第 0 组元素中的 第2 个元素,尝试往前插入排序
        for(int bound = gap; bound < arr.length; bound++) {
            //循环内部完成比较搬运的 过程
            //比较搬运的是组呢你的元素,不同组之间不能相互会影响
            int v= arr[bound];
            int cur = bound - gap ;

            for(;cur >= 0; cur -= gap) {
                if (arr[cur] > v) {
                    //需要进行搬运
                    //v 要插入到 arr[cur] 的前面
                    //就得把 、arr[cur] 往后搬运一个格子 给 v 腾出一个位置
                    arr[cur + gap] = arr[cur];
                } else {
                    break;
                }
            }
            //若发现 v 比 arr[cur] 大,就把 v 放到arr[cur]的后米N
            //后面的位置不是 arr[cur]+1 而是 arr[cur]+ gap;
            arr[cur +gap] = v;
        }
            public static void main(String[] args) {
        int[] arr = {4,5,7,8,9,2,3};
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
 }
  • 结果
    在这里插入图片描述
3、性能分析

希尔排序是一个不稳定排序
时间复杂度:
1.最好,O(n);
2.平均 O(n^1.3);
3.最坏O(n^2)
空间复杂度:O(1)

2.2. 选择排序

2.2.1 直接选择排序

1、原理

每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完 。

2、 实现
  • 代码实现
package Java42_0323;
import java.util.Arrays;
public class MySort {
  //3.选择排序,核心思路:基于打擂台的思想,每次从待排序区间,取出最小值,
    //放到擂台上(擂台就是待排序区间的最开始的位置)
    //空间复杂度 O(1),不稳定排序
    public static void selectSort(int[] arr) {
        //[0,bound)已排序区间
    //[bound arr,length) 带排序区间
        int bound = 0;
        for (; bound < arr.length; bound++) {
            //里层循环打擂台
            for(int cur = bound + 1; cur <arr.length; cur++) {
                //擂台就是bound 位置的元素
                //取cur位置和擂台进行比较
                if (arr[cur] < arr[bound]) {
                    //新元素胜出了,就需要交换两个元素的位置
                    swap(arr, cur, bound);
                }
            }
        }
}
    public static void swap(int[] arr, int cur, int bound) {

        int tem = arr[cur];
         arr[cur] = arr[bound];
         arr[bound] = tem;

}

    public static void main(String[] args) {
        int[] arr = {4,5,7,8,9,2,3};
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}


  • 结果

在这里插入图片描述

3、性能分析

直接选择排序是一个不稳定排序
时间复杂度:O(n^2)
空间复杂度: O(1)

2.2.2 堆排序

1、 原理

基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆。

2、实现
  • 代码实现
package Java42_0323;

import java.util.Arrays;
import java.util.Stack;

public class MySort {
    //4、堆排序
    //堆排序可以理解为是一种选择排序的优化~
    //直观上。如果是升序排序的话,我们可以建立一个小堆。每次取堆顶元素,循环取 N 次。就得到了一个有序排序
    //建立一个大堆,把对顶元素(当前最大值),和数组最后一个元素交换(最大值来到了数组末尾)。
    //前面的部分从 0 进行调整。
    //1、建堆,
    //2、先把根节点和最后一个元素进行交换,并且把最后一个元素从堆上删除。此时最后一个元素就是已排序区间,前面是待区间
    //3、继续 2 步骤,若干次之后,排序完成
    public static void heapSort(int[] arr) {
        //1、先进行建堆
        createHeap(arr);
        //2. 循环进行交换堆顶元素和最后一个元素的过程,并且删除最后一个元素
        int heapSize = arr.length;
        for(int i = 0; i < arr.length; i++) {
            swap(arr,0,heapSize-1);
            //删除最后一个元素
            heapSize--;
            //从 0 这个元素进行向下调整
            shiftDown(arr,heapSize,0);
        }
    }
    public static void shiftDown(int[] arr, int size, int index) {
        int parent = index;
        int child = 2 * parent + 1;
        while (child  < size) {
            //第一次比较,找出左右子树的最大值
            if (arr[child + 1] < size && arr[child + 1] > arr[child]) {
                child = child + 1;
            }
            //希望这次调整结束后,无论如何 child 都指向左右子树的最大值
            //第二次比较,拿刚刚的 child 位置元素和parent 进行比较
            //若不符合。则交换
            if (arr[parent] < arr[child]) {
                int tem = arr[parent];
                arr[parent] = arr[child];
                arr[child] = tem;

            } else {
                break;
            }
            //更新循环变量
            parent =child;
            child = 2* parent+1;
        }
}
    public static void createHeap(int[] arr) {
      for (int i = (arr.length - 1-1) / 2; i >= 0; i--) {
          shiftDown(arr, arr.length, i);
      }
}

    public static void main(String[] args) {
        int[] arr = {4,5,7,8,9,2,3};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

  • 结果

在这里插入图片描述

3、性能分析

时间复杂度 :O(n * log(n))
空间复杂度:O(1)
堆排序是不稳定排序。

2.3 交换排序

2.3.1 冒泡排序

1、原理

在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序。

2、实现
  • 代码
package Java42_0323;

import java.util.Arrays;

public class MySort {
  //5、冒泡泡排序
    //时间复杂度O(N^2)
    //空间复杂度O(1)
    //稳定排序

    //基本思路
    //比较两个相邻的元素,看是否符合升序要求,不符合就交换,
    //一趟遍历下来就能找到最大值(从前往后遍历)
    //一趟遍历下来就能找到最小值(从后往前遍历)
    public static void BubbleSort(int[] arr) {

        //[0,bound)已排序区间
        //[bound,length)待排序区间
        int bound = 0;
        for (; bound < arr.length; bound++) {

            for(int cur = arr.length-1; cur > bound; cur--) {
                if (arr[cur]<arr[cur-1]) {
                    //不符合升序,交换
                    swap(arr, cur, cur-1);
                }
            }
        }
    }
        public static void main(String[] args) {
        int[] arr = {4,5,7,8,9,2,3};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
  • 结果

在这里插入图片描述

3、性能分析

冒泡排序是稳定排序
时间复杂度为:O(N^2)
空间复杂度为:O(1)

2.4.2 快速排序

1、 原理
  • 从待排序区间选择一个数,作为基准值(pivot);
  • Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
  • 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 = 1,代表已经有序,或者小区间的长度 = 0,代表没有数据。
2、实现
  • 代码

package Java42_0323;
import java.util.Arrays;
public class MySort {

//6、快速排序
    public static void quickSort(int[] arr) {
        //使用一个附中方法进行递归
        //辅助方法多多了两个参数,用来针对数组上的哪个区间
        _quickSort(arr,0,arr.length-1);
    }
    public static void _quickSort(int[] arr, int left, int right) {
        if(left >= right) {
            return;
        }
        //使用 partition 方法来进行整理
        //index是left和right的重合位置
        int index= partition (arr,left, right);
        _quickSort(arr,left,index -1);
        _quickSort(arr,index+1,right);
    }
    public static  int partition(int[] arr, int left, int right) {
        //选取基准值
        int v = arr[right];
        int i = left;
        int j = right;

        //如果是先从左往右,后重右往左找,重合的时候 ij 指向的一定是比 ij 大的元素
        //如果是先重右往左找,后从左往右 重合的时候 ij 指向的一定是比 ij 小的元素

        while(i <j) {
            //从做往右找一个比基准值大的元素
            while(i < j &&arr[i] <= v) {
                i++;
            }
            //从右往左找一个比基准值小的元素
            while(i <j && arr[j] >= v) {
                j--;
            }
            swap(arr, i, j);
        }
        //若 i 和 j 重叠了。此时需要把当前基准值元素和 i j重叠位置进行交换
        //当 i  和 j 重合是的时候, i j 一定指向的元素是比基准值大的元素。!!!
        swap(arr, i,right);
        return i;
    }

    public static void main(String[] args) {
        int[] arr = {4,5,7,8,9,2,3};
        quickSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
  • 结果

在这里插入图片描述

3、性能分析

快速排序是不稳定的排序;
时间复杂度 :O(n * log(n))
空间复杂度:O(log(n))

2.4 归并排序

1、原理

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide
and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子
序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

2、实现
  • 代码
package Java42_0323;
import java.util.Arrays;

public class MySort {
//7.归并排序
    public static void mergeSort(int[] arr) {
        //创建一个新的方法辅助递归,新方法中多了两个参数、
        //表示是针对当前数组中的哪个部分进行排序
        //也是一个前闭后开区间
        _mergeSort(arr,0,arr.length);
    }
    //[left ,right) 前闭后开区间
    //right - left 区间中的元素个数
    public static void _mergeSort(int[] arr,int left, int right) {
        if (right - left <= 1) {
            //如果当前待排序区间里只有 1 个元素或者没有元素
            //就直接返回。不需要任何排序动作
            return;
        }
        //先把当前 [left,right) 区间一分为二
        int mid = (left + right) / 2;
        // 分成了两个区间
        // [left, mid), [mid,right)
        //当左侧区间的 _mergeSort 执行完毕后,
        // 就认为 [left, mid)就已经是有序区间了
        _mergeSort(arr, left, mid);
        //当右侧区间的 _mergeSort 执行完毕后,
        // 就认为 [mid, right)就已经是有序区间了
        _mergeSort(arr,mid,right);
        //接下来把两个有序数组合并到一起
        merge(arr,left,mid,right);
    }
    // merge 方法本身功能是把两个有序数组合并成一个有序数组
    //待合并的两个数组就分别是:
    //[left,mid)、[mid,right)
    public static void merge(int[] arr, int left, int mid, int right) {
        //创建一个临时的数组,用来存放临时的合并结果。
        //希望这个数组能存下合并后的结果 right - left
        int[] tem = new int[right - left];
        //当前要把新的元素放到 tem 数组的哪个下标(temSize)上
        int temSize = 0;
        int l = left;
        int r = mid;
        while (l <mid && r <right) {
            //归并排序是稳定排序!!!
            // 前提是,此处的判断条件不要写成 arr[l] < arr[r];
            if (arr[l] <= arr[r]) {
                //arr[l] 比较小,就把这个元素先插入到 tem 数组末尾
                tem[temSize] = arr[l];
                temSize++;
                l++;
            } else {
                //arr[r] 比较小,就把这个元素插入到 tem 数组的末尾
                tem[temSize] = arr[r];
                temSize++;
                r++;
            }
        }
        //当其中一个数组遍历完了之后,就把另外一个数组的剩余部分都拷贝过来
        while (l < mid) {
            //剩下的是左半边数组
            tem[temSize] = arr[l];
            temSize++;
            l++;
        }
        while (r < right) {
            //剩下的是右半边数组
            tem[temSize] = arr[r];
            temSize++;
            r++;
        }
        //最后一步,再把临时空间的内容都拷贝回参数数组中
        //需要把 tem 中的内容拷贝回 arr 的 [left ,right) 这一段空间里
        // [left ,right) 很可能不是从 0 开始的
        for (int i = 0; i < tem.length; i++) {
            arr[left +i] = tem[i];
        }

    }




    public static void main(String[] args) {
        int[] arr = {4,5,7,8,9,2,3};
        mergeSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
  • 结果

在这里插入图片描述

3、性能分析

归并排序是稳定排序;
时间复杂度:O(NlogN);
空间复杂度:O(N).

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值