icql

非线性排序

平均时间复杂度:不是O(n)

1)冒泡排序

package work.icql.java.algorithm.sort.isnotN;

/**
 * 冒泡排序:
 * 1)稳定排序
 * 2)算法思想
 * 外层最大遍历元素个数
 * 每一遍都会将当前未排序中最大的数排在最后面
 * 里层的遍历是冒泡比较操作
 * 3)平均时间复杂度:O(n^2),平均空间复杂度:O(1)
 */
public class BubbleSort {

    private BubbleSort() {
    }

    /**
     * 第一版
     *
     * @param data
     */
    public static void sort1(int[] data) {
        //循环次数
        //第一层循环,最大趟数 = length - 1
        //第二层循环,最大交换次数 = length - 1 - 外层循环计数器
        //因为数组末尾的有序区长度就是外层循环计数器的值
        int count = data.length - 1;
        for (int i = 0; i < count; i++) {
            for (int j = 0; j < count - i; j++) {
                //相邻位置 比较 交换
                if (data[j] > data[j + 1]) {
                    int temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                }
            }
        }
    }

    /**
     * 第二版:内层循环没有交换元素,则说明已经全部有序,所以直接返回
     *
     * @param data
     */
    public static void sort2(int[] data) {

        int count = data.length - 1;
        for (int i = 0; i < count; i++) {
            boolean isSorted = true;
            for (int j = 0; j < count - i; j++) {
                if (data[j] > data[j + 1]) {
                    int temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                    isSorted = false;
                }
            }

            if (isSorted) {
                return;
            }
        }
    }

    /**
     * 第三版:优化数组末尾的有序边界,有序边界应该为内层循环最后一次改变位置的地方
     *
     * @param data
     */
    public static void sort3(int[] data) {

        int count = data.length - 1;
        int sortBorder = count;
        int lastExchangeIndex = 0;

        for (int i = 0; i < count; i++) {
            boolean isSorted = true;
            for (int j = 0; j < sortBorder; j++) {
                if (data[j] > data[j + 1]) {
                    int temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;

                    isSorted = false;
                    lastExchangeIndex = j;
                }
            }

            sortBorder = lastExchangeIndex;
            if (isSorted) {
                return;
            }
        }
    }

}

2)插入排序

package work.icql.java.algorithm.sort.isnotN;

/**
 * 插入排序:
 * 1)稳定排序
 * 2)算法思想
 * 数据分为两个区间,已排序区间和未排序区间
 * 插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序
 * 重复这个过程,直到未排序区间中元素为空
 * 3)平均时间复杂度:O(n^2),平均空间复杂度:O(1)
 */
public class InsertionSort {

    private InsertionSort() {
    }

    public static void sort1(int[] data) {
        int count = data.length;
        for (int i = 1; i < count; i++) {
            //已排序区间的最后一个元素
            int orderedIndex = i - 1;
            int current = data[i];
            while (orderedIndex >= 0 && current < data[orderedIndex]) {
                data[orderedIndex + 1] = data[orderedIndex];
                orderedIndex--;
            }
            data[orderedIndex + 1] = current;
        }
    }

    public static void sort2(int[] data) {
        int count = data.length;
        for (int i = 1; i < count; i++) {
            //已排序区间的最后一个元素
            int orderedIndex = i - 1;
            int current = data[i];
            for (; orderedIndex >= 0; orderedIndex--) {
                int prev = data[orderedIndex];
                if (current < prev) {
                    data[orderedIndex + 1] = prev;
                } else {
                    break;
                }
            }
            data[orderedIndex + 1] = current;
        }
    }
}

3)选择排序

package work.icql.java.algorithm.sort.isnotN;

/**
 * 选择排序:
 * 1)不稳定排序
 * 2)算法思想
 * 实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序
 * 每次会从未排序区间中找到最小的元素,将其与已排序区间的末尾元素进行交换
 * 3)平均时间复杂度:O(n^2),平均空间复杂度:O(1)
 */
public class SelectionSort {

    private SelectionSort() {
    }

    public static void sort(int[] data) {
        int count = data.length;
        for (int i = 0; i < count - 1; i++) {
            // 寻找[i, 数组末尾)区间里的最小值的索引
            int minIndex = i;
            for (int j = i + 1; j < count; j++) {
                if (data[j] < data[minIndex]) {
                    minIndex = j;
                }
            }
            if (i != minIndex) {
                int temp = data[i];
                data[i] = data[minIndex];
                data[minIndex] = temp;
            }
        }
    }
}

4)归并排序

package work.icql.java.algorithm.sort.isnotN;

import java.util.Arrays;

/**
 * 归并排序:
 * 1)稳定排序
 * 2)算法思想
 * 分治思想,要排序一个数组,我们先把数组从中间分成前后两部分
 * 然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了
 * 3)平均时间复杂度:O(nlogn),平均空间复杂度:O(n)
 */
public class MergeSort {

    private MergeSort() {
    }

    public static void sort(int[] data) {
        sort(data, 0, data.length - 1);
    }

    private static void sort(int[] data, int left, int right) {
        //不再继续分解
        if (left >= right) {
            return;
        }
        int mid = (left + right) / 2;
        //左
        sort(data, left, mid);
        //右
        sort(data, mid + 1, right);
        //合并
        merge(data, left, right, mid);
    }

    private static void merge(int[] data, int left, int right, int mid) {
        //临时数组,用来保存当前用以归并的数据
        int[] aux = Arrays.copyOfRange(data, left, right + 1);
        int leftIndex = 0;
        int leftIndexEnd = mid - left;
        int rightIndex = leftIndexEnd + 1;
        int rightIndexEnd = aux.length - 1;
        //遍历重新设置 原数组 [left,right] 的值使之有序
        for (int k = left; k <= right; k++) {
            //左边元素已经处理完
            if (leftIndex > leftIndexEnd) {
                data[k] = aux[rightIndex];
                rightIndex++;
                continue;
            }
            //右边元素已经处理完
            if (rightIndex > rightIndexEnd) {
                data[k] = aux[leftIndex];
                leftIndex++;
                continue;
            }
            //如果左边所指元素 <= 右边所指元素
            if (aux[leftIndex] <= aux[rightIndex]) {
                data[k] = aux[leftIndex];
                leftIndex++;
                continue;
            }
            //如果左边所指元素 > 右边所指元素
            data[k] = aux[rightIndex];
            rightIndex++;
        }
    }
}

5)快速排序

package work.icql.java.algorithm.sort.isnotN;

/**
 * 快速排序:
 * 1)不稳定排序
 * 2)算法思想
 * 分治思想,取任意点作为分区点,将小于分区点的数据放在分区点左边,将大于分区点的数据放在分区点右边
 * 再分别对分区点两侧的数据做同样的操作,依次类推
 * 3)平均时间复杂度:O(nlogn),平均空间复杂度:O(1)
 */
public class QuickSort {

    private QuickSort() {
    }

    public static void sort(int[] data) {
        sort(data, 0, data.length - 1);
    }

    private static void sort(int[] data, int left, int right) {
        if (left >= right) {
            return;
        }
        //分区点,分区方法里 小于分区点-分区点-大于分区点
        int pivot = partition(data, left, right);
        sort(data, left, pivot - 1);
        sort(data, pivot + 1, right);
    }

    /**
     * 原地分区方法
     *
     * @param data
     * @param left
     * @param right
     * @return
     */
    private static int partition(int[] data, int left, int right) {
        //选取分区点,右边界
        int pivotIndex = right;
        int pivotValue = data[pivotIndex];
        int boarderIndex = left;
        int borderValue = data[boarderIndex];
        //遍历一遍
        for (int i = left; i <= right; i++) {
            int current = data[i];
            //当游标值小于分区点值时,交换边界点和游标值
            if (current < pivotValue) {
                data[i] = borderValue;
                data[boarderIndex] = current;
                //更新边界点和边界索引
                boarderIndex++;
                borderValue = data[boarderIndex];
            }
        }
        //交换边界点和分区点,更新分区点索引
        data[right] = borderValue;
        data[boarderIndex] = pivotValue;
        pivotIndex = boarderIndex;
        return pivotIndex;
    }
}

6)堆排序

TODO



线性排序

平均时间复杂度:O(n)

1)桶排序

package work.icql.java.algorithm.sort.isN;

/**
 * 桶排序:
 * 1)算法思想
 * 将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。
 * 桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了
 * 2)平均时间复杂度:O(n)
 */
public class BucketSort {

    private BucketSort() {
    }

    //比较适合用于外部排序
}

2)计数排序

package work.icql.java.algorithm.sort.isN;

/**
 * 计数排序:
 * 1)算法思想
 * 当所需排序的数据范围不大且有限,可以在桶排序的基础上,相等的数据放在一个桶
 * 2)平均时间复杂度:O(n)
 */
public class CountingSort {

    private CountingSort() {
    }
}

3)基数排序

package work.icql.java.algorithm.sort.isN;

/**
 * 基数排序:
 * 1)算法思想
 * 比较数据的每一位,比如对手机号进行排序,可以先比较第一位,再比较第二位,以此类推
 * 2)平均时间复杂度:O(n)
 */
public class RadixSort {

    private RadixSort() {
    }
}