排序算法(一)-- 冒泡、插入、选择

在这里插入图片描述

为了简单,所有的例子默认都为正序,这里不考虑正序倒序的问题!!

准备

开始前,我这边做了以下准备:

  • 一台通电的电脑
  • JDK8 OR JDK11
  • JUnit5
  • Idea
  • lombok(非必须,个人习惯用这个打印日志了)

一个抽象接口:

public interface Sort<E> {
    
    
    E[] sort();
}

一个测试用例:

@Slf4j
class SortTest {
    
    
    Integer[] integers;

    Sort<Integer> sort;

    @BeforeEach
    void setUp() {
    
    
        integers = new Integer[]{
    
    3, 7, 2, 1, 4, 8, 9, 8, 5, 6, 0};
        log.info("排序前: {}", Arrays.toString(integers));
    }

    @AfterEach
    void tearDown() {
    
    
        assertSort(sort.sort());
    }

    @Test
    void bubbleSortTest() {
    
    
        sort = new BubbleSort(integers);
    }

    @Test
    void insertSortTest() {
    
    
        sort = new InsertSort(integers);
    }

    @Test
    void selectSortTest() {
    
    
        sort = new SelectSort(integers);
    }

    private void assertSort(Integer[] sort) {
    
    
        log.info("排序后: {}", Arrays.toString(sort));
        assertAll(
                () -> assertEquals(integers.length, sort.length),
                () -> assertEquals(0, sort[0]),
                () -> assertEquals(9, sort[sort.length - 1]),
                () -> assertEquals(1, sort[1]),
                () -> assertEquals(2, sort[2]),
                () -> assertEquals(3, sort[3]),
                () -> assertEquals(4, sort[4]),
                () -> assertEquals(5, sort[5]),
                () -> assertEquals(6, sort[6]),
                () -> assertEquals(7, sort[7]),
                () -> assertEquals(8, sort[8]),
                () -> assertEquals(8, sort[9])
        );
    }
}

冒泡排序

对于冒泡排序,一切描述都是苍白的,看看这个魔性的视频,你就会完全明白

https://www.bilibili.com/video/BV1xW411Y7VL

public class BubbleSort implements Sort<Integer> {
    
    

    private final Integer[] elements;

    public BubbleSort(Integer[] elements) {
    
    
        this.elements = elements;
    }

    @Override
    public Integer[] sort() {
    
    
        int length = elements.length;
        for (int i = 0; i < length - 1; i++) {
    
    
            for (int j = 0; j < length - i - 1; j++) {
    
    
                // 当前大于下一位,则互换位置,继续向后比较
                if (elements[j] > elements[j + 1]) {
    
    
                    Integer temp = elements[j];
                    elements[j] = elements[j + 1];
                    elements[j + 1] = temp;
                }
                // 当前小于或等于下一位,从下一位开始往后比较
            }
        }
        return elements;
    }
}

插入排序

  1. 插入排序将数组划分为两个区域,已排序区,和未排序区
  2. 排序开始时,数组第一个元素默认在已排序区
  3. 每轮排序都是将未排序区的第一个元素,与已排序区的元素依次比较,如果未排序元素小于当前已排序元素,则此处为插入点
    • 插入点之后的元素向后平移
    • 将未排序元素插入此处
  4. 否则,此未排序元素插入到已排序区的最后,即数组不做任何移动,从未排序区的下一位进行新一轮比较
public class InsertSort implements Sort<Integer> {
    
    

    private Integer[] elements;

    public InsertSort(Integer[] elements) {
    
    
        this.elements = elements;
    }

    @Override
    public Integer[] sort() {
    
    
        int sortedLength = 1;
        int unSortedLength = elements.length - 1;

        for (int i = 0; i < unSortedLength; i++) {
    
    
            // 未排序区的第一个元素
            Integer unSort = elements[sortedLength];
            // 与已排序元素比较
            for (int j = 0; j < sortedLength; j++) {
    
    
                // 1. 已排序区:如果unSort比所有的都大,则排在最后一位,直接完成本轮比对即可
                // 2. 已排序区:unSort小于此位数,则排在此位前
                if (unSort < elements[j]) {
    
    
                    rightMoveOnePlace(sortedLength, j, elements);
                    elements[j] = unSort;
                    break;
                }
                // 3. 已排序区:unSort大于此位数,则进行下一位数对比
            }
            sortedLength++;
        }

        return elements;
    }

    /**
     * 将数组从指定位置开始,向右移动1位
     *
     * @param endMoveElementIndex   结束移动的元素数量
     * @param startMoveElementIndex 开始移动的元素位置
     * @param elements                  待操作的数组
     */
    private void rightMoveOnePlace(int endMoveElementIndex, int startMoveElementIndex, Integer[] elements) {
    
    
        for (int k = 0; k < endMoveElementIndex - startMoveElementIndex; k++) {
    
    
            elements[endMoveElementIndex - k] = elements[endMoveElementIndex - k - 1];
        }
    }
}

选择排序

  1. 选择排序也划分为已排序区和未排序区
  2. 排序开始时,未排序区没有任何元素
  3. 从未排序区第一个元素开始,依次与后面所有元素比较,将最小的元素放到已排序区尾部
public class SelectSort implements Sort<Integer> {
    
    

    private Integer[] elements;

    public SelectSort(Integer[] elements) {
    
    
        this.elements = elements;
    }

    @Override
    public Integer[] sort() {
    
    
        for (int unSortIndex = 0; unSortIndex < elements.length - 1; unSortIndex++) {
    
    
            int min = elements[unSortIndex];
            int minIndex = unSortIndex;
            for (int j = unSortIndex + 1; j < elements.length; j++) {
    
    
                if (elements[j] < min) {
    
    
                    min = elements[j];
                    minIndex = j;
                }
            }
            // 如果相等,则说明未排序区首位元素已经是最小值,不需要交换位置
            if (minIndex != unSortIndex) {
    
    
                elements[minIndex] = elements[unSortIndex];
                elements[unSortIndex] = min;
            }
        }
        return elements;
    }
}

最后

这些排序算法的时间复杂度都是 O ( n 2 ) O(n^2) O(n2)

排序过程中没有申请新的数组空间,都是在原始数组的基础上完成的排序,所以这些都属于原地排序算法,它们的空间复杂度为 O ( n ) O(n) O(n)

下一篇我们来看看归并和快排

扫描二维码关注公众号,回复: 12636865 查看本文章

如果大家感觉对自己有帮助,请给个赞,这将是对我最大的鼓励了~~~

猜你喜欢

转载自blog.csdn.net/weixin_38403680/article/details/113998077