经典排序(学习笔记)

二分插入排序

public static void insertSort(int[] array) {
    // 从倒数第二位开始,遍历到底0位,遍历 N-1 次
    for (int i = array.length - 2; i >= 0; i--) {
        // 存储当前抽离的元素
        int temp = array[i];
        int index = searchIndex(array, i + 1, array.length - 1, temp);

        // #1. 根据插入的索引位置,进行数组的移动和插入
        int j = i + 1;
        while (j <= index) {
            array[j - 1] = array[j];
            j++;
        }
        array[j - 1] = temp;
    }
}

冒泡排序

四点核心思想
  1. 指向数组两个相邻元素(最初是数字头两个元素),并比较大小

  1. 如果前比后大,则交换位置

  1. 后比前大,不交换。

  1. 依次向后移动,每次循环将最大元素移动至最后一个位置(比较1、2,2、3...)。

// 冒泡排序
public static void bubbleSort(int[] array) {
// 1. 每次循环,都能冒泡出剩余元素中最大的元素,因此需要循环 array.length 次
for (int i = 0; i < array.length; i++) {
    // 2. 每次遍历,只需要遍历 0 到 array.length - i - 1中元素,因此之后的元素都已经是最大的了
    for (int j = 0; j < array.length - i - 1; j++) {
        //3. 交换元素
        if (array[j] > array[j + 1]) {
            int temp = array[j + 1];
            array[j + 1] = array[j];
            array[j] = temp;
        }
    }
}
}

选择排序

四点核心思想
  1. 利用两个变量,一个储存当前最大值,一个储存当前最大值所在的索引

  1. 一次比较后面的元素,如果发现比当前最大值大则更新最大值,并且更新索引

  1. 直到遍历结束,将最大值放在数组最右边,也就是交换最右边元素和当前最大值

  1. 重复以上。

冒泡排序和选择排序时间复杂度都是O(N^2)但选择排序比冒泡快一倍左右

public void selectSort(int[] arr){
        //1.每次循环都将最大值排到数组最右边,循环array.length-1次
        for(int i=0;i<arr.length-1;i++){
            int ptr = 0;//取数组第一个元素
            for(int j = 1;j<arr.length-i;j++){
                //用第一个元素与后面元素依次比较
                if(arr[j]>arr[ptr]){
                    ptr = j;
                }
            }
            int temp = arr[arr.length-i-1];
            arr[arr.length-i-1] = arr[ptr];
            arr[ptr] = temp;
        }
    }

插入排序

核心规则四点

  1. 第一轮,抽离数组末尾倒数第二个元素,作为临时元素。

  1. 用临时元素与数组后面的元素进行比较:如果_后面的元素_值小于临时元素,则后面的元素左移。

  1. 如果后面的元素大于临时元素,或者已经移动到数组末尾,则将临时元素插入当前空隙中。

  1. 重复上面步骤,完成排序。

核心为通过每次迭代,将部分元素排序,以达到最终全部排序的效果。

第一次迭代

第二次迭代

第三次迭代

每次迭代多讲尾部部分数字排好序

最差情况时间复杂度O((N^2+N)/2)

忽略常数O(N^2)

public void insertSort(int[] arr){
        for(int i=0;i<arr.length-1;i++){
            int ptr=arr.length-i-2;//取未排序数组的倒数第二个元素
                for(int j=ptr;j<arr.length-1;j++){
                    if(arr[j]>arr[j+1]){
                        int temp = arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }

二分插入排序

将插入排序和二分查找法结合在一起可以进一步提高排序效率

二分插入法

在原来基础上,核心加入二分查找位置的函数

// 查找应该插入的索引位置
public static int searchIndex(int[] array, int left, int right, int aim) {
    // 循环查找节点位置
    while (left < right) {
        int middle = (left + right) / 2;//middle取数组中间的数,注意l+r为奇数情况
        int value = array[middle];
        if (value < aim) {
            left = middle + 1;
        } else {
            right = middle - 1;
        }
    }
    // #1. 如果最终元素仍然大于目标元素,则将索引位置往左边移动一个
    if(array[left] > aim){
        return left -1;
    }
    // 否则就是当前位置
    return left;
}
// 查找应该插入的索引位置
public static int searchIndex(int[] array, int left, int right, int aim) {
    // 循环查找节点位置
    while (left < right) {
        int middle = (left + right) / 2;//middle取数组中间的数,注意l+r为奇数情况
        int value = array[middle];
        if (value < aim) {
            left = middle + 1;
        } else {
            right = middle - 1;
        }
    }
    // #1. 如果最终元素仍然小于目标元素,则将索引位置往右边移动一个
    if(array[left] < aim){
        return left + 1;
    }
    // 否则就是当前位置
    return left;
}
二分插入排序代码
// 插入排序
public static void insertSort(int[] array) {
    // 从倒数第二位开始,遍历到底0位,遍历 N-1 次
    for (int i = array.length - 2; i >= 0; i--) {
        // 存储当前抽离的元素
        int temp = array[i];
        int index = searchIndex(array, i + 1, array.length - 1, temp);

        // #1. 根据插入的索引位置,进行数组的移动和插入
        int j = i + 1;
        while (j <= index) {
            array[j - 1] = array[j];
            j++;
        }
        array[j - 1] = temp;
    }
}

归并排序

递归思想
  1. 基准条件(递归结束条件)

  1. 递归公式(考虑递归如何执行下去)

递归举例,斐波那契数列n=(n-1)+(n-2)

public int fibo(int n){
        if(n<0){
            return 0;
        }
        if(n==0){
            return 0;
        }
        if(n==1||n==2){
            return 1;
        }else {

            return fibo(n-1)+fibo(n-2);
        }
    }
分治思想

分治思想是通过递归,将原问题分成几个规模较小但是类似于原问题的子问题,通过递归方式来求解这些小问题,最后将子问题合并来得到原问题的解。

归并排序核心思路:

将大数组分为小数组,将每个小数组排好列,再将小数组组合成大数组

拆分过程中核心逻辑
  1. 如何递归进行数组拆分

  1. 如何将原数组拆分为两个子数组

首先创建两个函数

// 归并排序,返回排好序的数组
public static int[] mergeSort(int[] array) {
}

// 拷贝原数组的部分内容,从 left 到 right
public static int[] subArray(int[] source, int left, int right) {
}

然后完成第二个函数

// 拷贝原数组的部分内容,从 left 到 right
public static int[] subArray(int[] source, int left, int right) {
    // 创建一个新数组
    int[] result = new int[right - left];//新数组长度为right-left,从索引l到索引r-1
    // 依次赋值进去
    for (int i = left; i < right; i++) {
        result[i - left] = source[i];
    }
    return result;
}

难点mergeSort类,如何实现递归拆分数组先找基准条件:数组元素只有一个,则直接返回

// 归并排序,返回排好序的数组
public static int[] mergeSort(int[] array) {
  if(array.length == 1){
    return array;
  }
}

如果数组元素不止一个,则将数组从中间进行拆分,分别调用mergSort

// 归并排序,返回排好序的数组
public static int[] mergeSort(int[] array) {
    // 为了方便查看结果,我们将每个数组进行打印
    System.out.println(Arrays.toString(array));
    if (array.length == 1) {
        return array;
    }

    int middle = array.length / 2;
    // #1. 处理 0 到 middle 左侧数组部分(不包括middle)
    int[] left = mergeSort(subArray(array, 0, middle));
    // #2. 处理 middle 到 array.length 右侧数组部分(不包括索引array.length)
    int[] right = mergeSort(subArray(array, middle, array.length));

    // TODO处理合并问题
    return array;
}

治---如何进行合并排序

观察可知,首先我们需要观察两个需要被排序的数组,数组内部已经被排好序

所以现在应进行两个有序数组的合并排序

将两个数组的数组指针都指向数组头,比较大小,将小的值存入新数组,指针后移,指向较大值的指针不变,以此类推

时间复杂度

两个数组只遍历一次,复杂度为O(log(N))

// 归并排序
  public static int[] mergeSort(int[] array) {
    // 为了方便查看结果,我们将每个数组进行打印
    if (array.length == 1) {
      return array;
    }

    int middle = array.length / 2;
    // 处理 0 到 middle 左侧数组部分
    int[] left = mergeSort(subArray(array, 0, middle));
    // 处理 middle 到 array.length 右侧数组部分
    int[] right = mergeSort(subArray(array, middle, array.length));

    // TODO处理合并问题->两个有序数组合并为一个有序数组
    int l = 0;
    int r = 0;
    int index = 0;
    // 依次比较左右两个数组
    while (l < left.length && r < right.length) {
        //当其中一个达到数组末尾时跳出
      array[index] = Math.min(left[l], right[r]);
      index++;
      if (left[l] < right[r]) {
        l++;
      } else {
        r++;
      }
    }

    // 右侧数组已经遍历完成,左侧有剩余
    if (l < left.length) {
      for(int i = l; i < left.length; i++){
        array[index] = left[i];
        index++;
      }
    }

    // 左侧数组已经遍历完成,右侧有剩余
    if(r < right.length){
      for(int i = r; i < right.length; i++){
        array[index] = right[i];
        index++;
      }
    }

    return array;
  }

  // 拷贝原数组的部分内容,从 left 到 right
  public static int[] subArray(int[] source, int left, int right) {
    // 创建一个新数组
    int[] result = new int[right - left];
    // 依次赋值进去
    for (int i = left; i < right; i++) {
      result[i - left] = source[i];
    }
    return result;
  }

重点快速排序

(暂未完善代码后续抽时间完善)

一、取一个数作为基准数(_为了方便,取数组第一个元素)
二、取数组的最左边数组指针 i 以及最右边数组指针 j _
三、先让右边的指针 j 从右向左移动,并且对比每个元素于基数的大小,若某元素比基数小,则j停止移动.
四、移动最左边指针并且当遇到元素比基数大时停止
五、交换j与i指向的值(实现左边为比基数小的数,右边为比基数大的数)
六、重复3-5直到i与j重合,再将基数与i指向的值交换,此时基数左边的全比基数小,右边全比基数大
七、然后再分别将左,右无序数组再按以上方法进行排序

最后,此文章为学习过程中的笔记或自己的理解,学习资料来源为 优课达 以及 csdn 博主文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CAROL嘉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值