Java实现插入排序、冒泡排序、堆排序、希尔排序、选择排序、优先队列排序、快速排序、归并排序(详细注释,原理解析)

14 篇文章 0 订阅

插入排序

package learn;

import java.util.Arrays;

/*
 * 每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左侧数组依然有序。
 * 速度优于选择排序
 */
public class InsertSort {

    public static void insertSort(int[] a) {
        int n = a.length;
        for (int i = 1; i < n; i++) {
            // 为了将当前元素插入到已经排序的数组中需要从后往前遍历
            for (int j = i; j > 0 ; j--) {
                if (a[j] < a[j - 1]) {
                    int temp = a[j];
                    a[j] = a[j - 1];
                    a[j - 1] = temp;
                } else {
                    continue;
                }
            }
        }
    }

    public static void main(String[] args) {
//        int[] s = {4, 6, 1, 2, 3, 0};
        int[] s = {9,7,1,-8,36,-18,100};
        System.out.println(Arrays.toString(s));
        insertSort(s);
        System.out.println(Arrays.toString(s));

    }
}

package sort;

/**
 * 【大顶堆】
 * 堆中某个节点的值总是大于等于其子节点的值,并且堆是一颗完全二叉树。
 *
 * 堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。
 * 位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。
 * @param <T>
 */
public class dui<T extends Comparable<T>> {

    private T[] heap;
    private int N = 0;

    public dui(int maxN) {
        this.heap = (T[]) new Comparable[maxN + 1];
    }

    public boolean isEmpty() {
        return N == 0;
    }

    public int size() {
        return N;
    }

    private boolean less(int i, int j) {
        return heap[i].compareTo(heap[j]) < 0;
    }

    private void swap(int i, int j) {
        T t = heap[i];
        heap[i] = heap[j];
        heap[j] = t;
    }

    // 上浮:把大元素向上排
    private void swim(int k) {
        // k 的父节点 k/2 更小的时候需要对 k 做上浮操作
        while (k > 1 && less(k / 2, k)) {
            swap(k / 2, k);// 交换
            k = k / 2;// 更改当前 k 的位置
        }
    }
    // 下沉:把小元素往下排
    private void sink(int k) {
        // k 的子节点分别是 k*2 与 k*2+1
        while (2 * k <= N) {
            int j = 2 * k;
            if (j < N && less(j, j + 1))// 左子节点要小于右子节点
                j++;
            // 若父节点 >= 左子节点 就表示
            if (!less(k, j))
                break;
            // 父节点 < 左子节点,就需要交换
            swap(k, j);

            k = j;
        }
    }

    // 插入元素:将新元素放到数组末尾,然后上浮到合适的位置。
    public void insert(T v) {
        heap[++N] = v;
        swim(N);
    }

    // 删除最大元素:
    // 从数组顶端删除最大的元素,并将数组的最后一个元素放到顶端,并让这个元素下沉到合适的位置。
    public T delMax() {
        T max = heap[1];
        swap(1, N--);
        heap[N + 1] = null;
        sink(1);
        return max;
    }
}

堆排序

package sort;

import java.util.Arrays;

/**
 * 把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,
 * 那么就可以得到一个从尾到头的递减序列,从正向来看就是一个递增序列,
 * 这就是堆排序。
 */
public class HeapSort {

    public static void heapSort(int[] a) {
        int N = a.length - 1;
        // 从右至左进行下沉操作
        for (int k = N / 2; k >= 1; k--) {
            sink(a, k, N);
        }
        while (N > 1) {
            swap(a, 1, N--);
            sink(a, 1, N);
        }
    }
    // 下沉
    private static void sink(int[] a, int k, int N) {
        while (2 * k <= N) {
            int j = 2 * k;
            if (j < N && less(a[j], a[j + 1]))
                j++;
            if (!less(a[k], a[j]))
                break;
            swap(a, k, j);
            k = j;
        }
    }

    private static boolean less(Comparable v, Comparable w) {
        // 若 v 小于 w 则返回负数
        return v.compareTo(w) < 0;
    }

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

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

}

快速排序

package sort;

import java.util.Arrays;

/**
 * 使用最广泛的排序算法
 * 原理上是一种分治算法,将一个数组分为两部分,对这两部分独立的排序,然后整体有序
 * 与归并排序的区别:
 * 1):归并排序虽然也是把数组分为两部分,但是归并排序需要在部分有序后再做一次排序来让整体有序;
 * 但是快速排序是:部分有序了,整体自然就有序了,无需对整体再一次排序。
 * 2):归并排序的数组切分是平分,但是快速排序的数组切分是根据数组内容由函数产生的
 */
public class QuickSort {

    public static void quickSort(int[] a, int start, int end) {
        if (end <= start) {
            return;
        }
        // j 是中分点,parttion算法是关键
        int j = partition(a, start, end);
        quickSort(a, start, j - 1);// 针对左边排序
        quickSort(a, j + 1, end); // 针对右边排序
    }

    private static boolean less(Comparable v, Comparable w) {
        // 若 v 小于 w 则返回负数
        return v.compareTo(w) < 0;
    }

    private static void exch(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    /*
    这段代码按照a[start]的值v进行切分。
    当指针i和j相遇时主循环退出。
    在循环中,a[i]小于v时我们增大i,a[j]大于v时我们减小j,
    然后交换a[i]和a[j]来保证i左侧的元素都不大于v, j右侧的元素都不小于V。
    当指针相遇时交换a[start]和a[j],切分结束(这样切分值就留在a[j]中了)。
     */
    private static int partition(int[] a, int start, int end) {
        // i、j 两根指针分别从左右开始寻找第一个大于 v 的与第一个小于 v 的值
        int i = start;
        int j = end + 1;
        int v = a[start];
        while (true) {
            // 找到第一个大于 v 的值与 i
            while (less(a[++i], v)) {
                if (i == end) {
                    break;
                }
            }
            // 找到第一个小于 v 的值与 j
            while (less(v, a[--j])) {
                if (j == start) {
                    break;
                }
            }
            // i、j 指针相交后
            if (i >= j) {
                break;
            }
            // 交换 i、j 处的值,确保 i 左边的都不大于 v,j 右边的都不小于 v
            exch(a, i, j);
        }
        exch(a, start, j);// 将 V = a[j]放入正确的位置
        return j;
    }

    public static void main(String[] args) {
//        int[] a = {12, 6, 9, 3, 78, 3, 3, 4};
        int[] a = {9, 7, 1, -8, -1, -18, 100};
        System.out.println(Arrays.toString(a));
        quickSort(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));

    }
}

冒泡排序

package sort;

public class BubbleSort {


    public static void bubbleSort(int[] a) {
        // 冒泡排序的最大的特点就是顺序是从右往前逐渐有序的
        // 所以,随着排序的逐渐执行,右边的越来越有序,但是每一次比较缺还在比较已经有序的部分
        // 因此需要排除遍历这部分有序的数据,也就是内循环只遍历到右边有序数据的前面就行了,不能再往后遍历了
        int sortedBorder = a.length - 1;
        // 这个 lastSwapIndex 肯定是从大数开始减少的,代表着右边的数据在排序
        int lastSwapIndex = 0;
        for (int i = 0; i < a.length; i++) {
            boolean isSorted = true;
            for (int j = 0; j < sortedBorder; j++) {
                if (a[j] > a[j + 1]) {
                    a[j] ^= a[j + 1];
                    a[j + 1] ^= a[j];
                    a[j] ^= a[j + 1];
                    isSorted = false;
                    lastSwapIndex = j;
                }
            }
            // 内循环遍历结束,代表着,右边已经部分有序了,最后一次排序的 j 就是有序无序的边界
            sortedBorder = lastSwapIndex;
            if (isSorted) {
                break;
            }
        }
    }


    public static void main(String[] args) {
        int[] arr = {6, 3, 8, 2, 9, 1, 0, 5, 4, 7, -1};
//        int[] arr = {6, 3, 8, 12, 19, 110};
//        int[] arr = {0, 3, 8, 12, 19, 110};
        System.out.println("排序前数组为:");
        for (int num : arr) {
            System.out.print(num + " ");
        }

        bubbleSort(arr);

        System.out.println();
        System.out.println("排序后的数组为:");
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

希尔排序

package sort;

import java.util.Arrays;
/*
希尔排序是基于插入排序的一种快速排序算法
插入排序的缺点:对于大规模的乱序数组排序速度慢
希尔排序简单的改变了插入排序:交换不相邻的元素以对数组的局部进行排序,
并最终用插入排序将局部有序的数组排序
 希尔排序对于大型数组排序效率很高,不需要额外的内存空间
  */
public class ShellSort {

    public static void shellSort(int[] a) {
        int n = a.length;
        // 设置增量,增量的取法有很多,这里是推荐取法
        // 插入排序的增量是1,属于相邻元素比较,现在换成不相邻元素比较
        int h = 1;
        while (h < n / 3) {
            h = 3 * h + 1;
        }
        // 增量最小为 1 ,也就是相邻的两个元素比较
        while (h >= 1) {

            // 对相聚 h 的两个元素使用插入排序
            for (int i = h; i < n; i++) {
                for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
                    exch(a, j, j-h);
                }
            }
            // 排序结束后,缩小增量,继续排序
            h /= 3;
        }
    }
    private static void exch(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    private static boolean less(Comparable v, Comparable w) {
        // 若 v 小于 w 则返回负数
        return v.compareTo(w) < 0;
    }

    public static void main(String[] args) {
//        int[] s = {3, -2, -1};
        int[] s = {9,7,1,-8,-1,-18,100};
        System.out.println(Arrays.toString(s));
        shellSort(s);
        System.out.println(Arrays.toString(s));

    }
}

选择排序

package sort;

import java.util.Arrays;

/**
 * 在数组里面选择最小的,放到第一位,然后在剩下的数组里面找到最小的放到第二位,如此反复直到结束
 */
public class SelectSort {

    public static void selectSort(int[] a) {
        int n = a.length;
        for (int i = 0; i < n-1; i++) {
            for (int j = i + 1; j < n; j++) {
                // 要始终保持最小值为当前的 i
                int minIndex = i;
                boolean exchange = true;
                // 如果 a[j] < a[min] 说明需要把 j 设置为最小的
                if (a[j] < a[minIndex]) {
                    minIndex = j;
                } else {
                    exchange = false;
                }
                // 如果需要交换数据就开始交换
                if (exchange) {
                    int temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {0, 200, -523, 7, 1, -1, -18, 2, 10, -6};
        System.out.println(Arrays.toString(array));
        selectSort2(array);
        System.out.println(Arrays.toString(array));

    }

    public static void selectSort2(int[] array) {
        for (int i = 0; i < array.length; i++) {
            for (int j = i + 1; j < array.length; j++) {
                // 假设 i 所处位置的元素就是最小的
                int min = i;
                if (array[j] < array[min]) {
                    // 后面的更小就要排序到前面
                    array[i] ^= array[j];
                    array[j] ^= array[i];
                    array[i] ^= array[j];
                }
            }
        }
    }
}

优先队列排序

package sort;

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;

public class Test {

    private static class Customer {

        private Integer id;

        private String name;

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Customer(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        public Customer() {
        }

        @Override
        public String toString() {
            return  "weight=" + id +
                    ", name=" + name;
        }

    }

    public static void main(String[] args) {
        // 1.演示优先队列的自然排序
        Queue<Integer> naturePriorityQueue = new PriorityQueue<>(10);
        for (int i = 0; i < 10; i++) {
            int random = (int) (Math.random() * 100000 + 1);
            System.out.println("add i value = " + random);
            naturePriorityQueue.add(random);
            Integer peek = naturePriorityQueue.peek();
            System.out.println("poll i value = " + peek);
            System.out.println();

        }


        // 2.演示自定义排序
        Queue<Customer> customerPriorityQueue = new PriorityQueue<>(10, idComparator);// 初始化队列大小并指定比较器

        for (int i = 0; i < 10; i++) {
            Customer customer = new Customer((int) (Math.random() * 6532 + 1), "customer" + i);
            System.out.println("add customer is " + customer.toString());
            customerPriorityQueue.add(customer);
            Customer customer1 = customerPriorityQueue.peek();
            System.out.println("peek customer is " + customer1.toString());
            System.out.println();
        }

    }

    // 自定义的比较器
    public static Comparator<Customer> idComparator = new Comparator<Customer>() {
        @Override
        public int compare(Customer o1, Customer o2) {
            return o1.getId() - o2.getId();
        }
    };

}

自顶向下的归并实现

package sort;

import java.util.Arrays;

public class MergeSortUp2Down {

    /*
    该方法用来拆解原数组为一个个单一的元素
     */
    public static void mergeSortUp2Down(int[] array, int start, int end) {
        if (start >= end)
            return;
        int mid = (start + end) >> 1; // 把除法换成位运算
        mergeSortUp2Down(array, start, mid);
        mergeSortUp2Down(array, mid + 1, end);
        // 拆完后进入比较排序处理阶段
        merge(array, start, mid, end);
    }

    /*
    每一次进入该方法就代表一个小阶段的归并
    需要一个临时数组存储排序的数据(不是修改原始数据)
    归并完了之后可能还有一部分数据本来有序,无需归并,把他们加入到临时数组后面(到此为止归并才真正的结束)
    (现在知道归并时为什么不能修改原始数组了吧)
    最后把归并的结果写回到原始数组中
     */
    private static void merge(int[] array, int start, int mid, int end) {
        // 归并的特点是两两比对,但不是只比对两个数,是两组!用 i、j 指向这两组元素的第一个
        int i = start;
        int j = mid + 1;
        int index = 0;
        // 存储新排序的数据
        int[] tmp = new int[end - start + 1];
        while (i <= mid && j <= end) { // 前面的一组不能越 mid 界,后面一组不可以越 end 界
            if (array[j] <= array[i]) {
                tmp[index++] = array[j++];
            } else {
                tmp[index++] = array[i++];
            }
        }
        while (i <= mid) {
            tmp[index++] = array[i++];
        }
        while (j <= end) {
            tmp[index++] = array[j++];
        }
        // 把现在的有序数据写回到原始数组里面
        for (index = 0; index < tmp.length; index++) {
            array[start + index] = tmp[index];
        }
    }

    public static void main(String[] args) {
        int[] a = {6, 3, 7, 2, 2, 3, 4, 9, 1, -1};
        mergeSortUp2Down(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
    }
}

  • 31
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

byg_qlh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值