基础篇1-排序

目录

学习目标

冒泡排序

选择排序

插入排序

希尔排序(了解)

快速排序

单边快排

双边快排


最近在准备面试,从今天开始整理并记录一下准备过程中学习的内容。

学习目标

掌握基本的排序算法。

1.掌握常见的排序算法的实现思路

2.手写冒泡、快排算法代码

3.了解各个排序算法的特性,比如时间复杂度、是否稳定


冒泡排序

文字描述

1.依次比较数组中相邻的两个元素,若是a[i]>a[i+1],那么将a[i]和a[i+1]两个元素进行交换,两两都比较一次成为一轮冒泡,结果是将数组中最大的元素交换至最后。

2.重复以上步骤,直至整个数组有序排列。

动图演示

特点: 

1.冒泡排序的时间复杂度:O(n²)

2.冒泡排序是稳定排序算法

3.集合元素有序度较高时,可选择冒泡排序

代码实现

public class BubbleSort {

    public static void main(String[] args) {
        int[] a = {2, 5, 4, 8, 6, 7, 1};
        bubble(a);
    }

    public static void bubble(int[] a) {
        int n = a.length - 1;
        while (true) {
            int last = 0;//表示最后一次索引交换位置
            //一轮冒泡
            for (int i = 0; i < a.length - 1; i++) {
                if (a[i] > a[i + 1]) {
                    //  交换数据
                    int tmp = a[i + 1];
                    a[i + 1] = a[i];
                    a[i] = tmp;
                    last = i;

                }

            }
            System.out.println(Arrays.toString(a));
            n = last;
            if (n == 0 ) {
                break;
            }
        }
    }
}

执行结果: 


选择排序

文字描述

1.将数组分为两个区域,排序区域和未排序区域,每轮从未排序区域找出最小的值放入有序区域。

2.重复上述步骤,直至整个数组有序排列。

优化思路:

1.待排序元素遇到比自己小的元素,意味着已经找到插入位置,无需继续比较下去。

动图演示

特点: 

1.选择排序的时间复杂度:O(n²)

2.选择排序是不稳定排序算法(元素值相同的情况下顺序可能会发生改变)

3.选择排序一般快于冒泡排序,但是集合有序度高的情况下不如冒泡排序

代码实现

public class SelectionSort {

    public static void main(String[] args) {
        int[] a = {2, 5, 4, 8, 6, 7, 1};
        selection(a);
    }

    private static void selection(int[] a) {
        for (int i = 0; i < a.length-1; i++) {
            //i为每轮最小值要交换的索引位置
            int s = i;//每轮最小值的索引位置
            for (int j = s+1; j < a.length; j++) {
                if (a[s] > a[j]){
                    s = j ;
                }
            }
            if (s != i) {
                //  交换数据
                int tmp = a[i];
                a[i] = a[s];
                a[s] = tmp;
                System.out.println(Arrays.toString(a));
            }

        }
    }
}

执行结果


插入排序

文字描述

1.将数组分为两个区域,排序区域和未排序区域,每轮从未排序区域选出第一个元素,按序插入到排序区域中。

2.重复上述步骤,直至整个数组有序排列。

优化思路

1.待排序元素遇到比自己小的元素,意味着已经找到插入位置,无需继续比较下去。

2.插入时可直接移动元素,而不是交换元素

动图演示

特点: 

1.插入排序的平均时间复杂度:O(n²),有序集合插入时间复杂度:O(n)

2.插入排序是稳定排序算法

3.大部分情况下,插入排序略优于选择排序。

代码实现

public class InsertSort {

    public static void main(String[] args) {
        int[] a = {2, 5, 4, 8, 6, 7, 1};
        insert(a);
    }

    private static void insert(int[] a) {
        for (int i = 1; i < a.length; i++) {
            int t = a[i];
            int j = i -1;
            while (j >= 0){
                if (t < a[j]){
                    //前面的元素往后移动一位
                    a[j+1] = a[j];
                }else {
                    break;
                }
                j--;
            }
            //j的值为元素比较结束后的前一位,所以需要加1
            a[j+1] = t;
            System.out.println(Arrays.toString(a));
        }
    }
}

执行结果


希尔排序(了解)

文字描述

1.每轮使用不同的间隙,比如N/2,对数组元素进行分割,两两对比大小,如果a[i]>a[i+N/2-1],将两个元素进行交换。

2.不断缩小间隙范围,N/2,N/4……,1,重复上述步骤,直至整个数组有序排列。

 上图仅为希尔算法的间隙切分方式之一,也是最原始的切分方式,它的平均时间复杂度并不是最优的,不同的分割方式的平均时间复杂度不同。


快速排序

文字描述

1.每轮排序选择一个基准点进行分区。

        1. 让小于基点的元素进入一个分区,大于基点的元素进入另一个分区。

        2.分区完成后,基准点就是其最终位置 

2. 在子分区内重复上面的过程,直到子分区内元素个数小于等于1,这是一种分而治之的思想(divide-and-conquer)。

特点

1.平均复杂度是O(n㏒₂n) ,最坏时间复杂度O(n²)

2.数据量比较大时,优势非常明显

3.属于不稳定排序。

实现方式

快排的实现方式很多,这里主要罗列三种:1.单边快排 ,2.双边快排,3.霍尔分区

单边快排

1.选择最右元素作为基准点元素。

2.j指针负责找出比基准点小的元素,一旦找到则与i交换。

3.i指针维护小于基准点元素的边界,也是每次交换的目标索引。

动图演示

 代码实现

public class QuickSort {

    public static void main(String[] args) {
        int[] a = {5, 3, 7, 2, 9, 8, 1, 4};
        quick(a,0,a.length - 1);
    }

    public static  void quick(int[] a, int l, int r){
        if (l >= r) {
            return;
        }
            int p = partition(a, l, r);
            quick(a, l, p - 1);
            quick(a, p + 1, r);

    }

    private static int partition(int[] a, int l, int r) {
        int pv = a[r];
        int i = l;
        for (int j = i; j < r; j++) {
            if (a[j] < pv){
                if (i != j ) {
                    swap(a, i, j);
                }
                i++;
            }
        }
        if ( i != r ) {
            swap(a, i, r);
            System.out.println(Arrays.toString(a)+" i = "+i);
        }
        return i;
    }

    static void swap(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
}

 执行结果


双边快排

1.选择最左元素作为基准点元素

2. j 指针负责从右往左找出比基准点小的元素,i 指针负责从左向右找出比基准点大的元素,一旦找到二者交换,直至 i,j 相交

3.最后基准点与 i(此时 i 和 j 相等)交换,i即为分区位置

动图演示

代码实现:

public class QuickSort2 {

    public static void main(String[] args) {
        int[] a = {5, 3, 7, 2, 9, 8, 1, 4};
        quick(a,0,a.length - 1);
    }

    public static  void quick(int[] a, int l, int r){
        if (l >= r) {
            return;
        }
            int p = partition(a, l, r);
            quick(a, l, p - 1);
            quick(a, p + 1, r);

    }

    private static int partition(int[] a, int l, int r) {
        int pv = a[l];
        int i = l;
        int j = r;
        while (i < j){

            while (i < j && a[j] > pv){
                j--;
            }

            while (i < j && a[i] <= pv){
                i++;
            }
            swap(a, i, j);
        }
        swap(a, l, j);
        System.out.println(Arrays.toString(a));
        return j;
    }

    static void swap(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
}

执行结果: 

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值