数据结构之内部排序

直接插入排序

/**
 * 直接插入排序:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成
 */
public static void insertSort(int[] nums) {
    for (int i = 1; i < nums.length; i++) {
        int insertVal = nums[i];
        int insertIndex = i - 1;

        while (insertIndex >= 0 && nums[insertIndex ] > insertVal) {
            // 交换
            nums[insertIndex + 1] = nums[insertIndex ];
			// insertIndex 后移一位
            insertIndex --;
        }

        nums[insertIndex + 1] = insertVal;
    }
}

希尔排序

/**
 * 希尔排序:对各个子表分别进行直接插入排序,再缩小增量,重复上述步骤
 */
public static void shellSortI(int[] nums) {
    for (int gap = nums.length / 2; gap > 0; gap /= 2) { // 分组
        for (int i = gap; i < nums.length; i++) { // 每个元素以 gap 为距离进行移动
            int insertIndex = i;
            int insertVal = nums[insertIndex];

            while (insertIndex - gap >= 0 && nums[insertIndex - gap] > insertVal) {
                // 交换
                nums[insertIndex] = nums[insertIndex - gap];

                insertIndex -= gap;
            }

            nums[insertIndex] = insertVal;
        }
    }
}

/**
 * 使用了三个 for 循环
 */
public static void shellSortII(int[] nums) {
    for (int gap = nums.length / 2; gap > 0; gap /= 2) { // 分组
        for (int i = 0; i < gap; i++) { // 第 i 组
            for (int j = i + gap; j < nums.length; j += gap) { // 在第 i 组内进行直接插入排序
                int insertVal = nums[j];
                int insertIndex = j - gap;

                while (insertIndex >= 0 && nums[insertIndex] > insertVal) {
                    // 交换
                    nums[insertIndex + gap] = nums[insertIndex];

                    insertIndex -= gap;
                }

                nums[insertIndex + gap] = insertVal;
            }
        }
    }
}

冒泡排序

/**
 * 冒泡排序:从后往前(从前往后)两两比较相邻元素的值,若为逆序,则交换它们,直到序列比较完
 */
public static void bubbleSort(int[] nums) {
    for (int i = nums.length - 1; i > 0; i--) {
        boolean swap = false;
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) { // 冒泡
                swap = true;

                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
        if (!swap) return;
    }
}

快速排序

/**
 * 快速排序;在待排序列中任取一个 pivot,通过一趟排序划分成两个部分,其中左边的元素都小于等于 pivot,右边的元素都大于或等于 pivot
 *
 * 标准的快速排序算法
 */
public static void quickSortI(int[] nums) {
    quickSortI(nums, 0, nums.length - 1);
}

private static void quickSortI(int[] nums, int left, int right) {
    if (left > right) return;

    int i = left, j = right, base = nums[left];

    while (i < j) {
        while (i < j && nums[j] >= base) j--;
        nums[i] = nums[j];
        while (i < j && nums[i] <= base) i++;
        nums[j] = nums[i];
    }
    // i == j !!!
    nums[i] = base;

    quickSortI(nums, left, i - 1);
    quickSortI(nums, i + 1, right);
}

/**
 * 非标准的 ”快排“ 算法,只是通过算法实现 左小 基准 右大
 */
public static void quickSortII(int[] nums) {
    quickSortII(nums, 0, nums.length - 1);
}

private static void quickSortII(int[] nums, int left, int right) {
    if (left > right) return;

    int i = left, j = right, base = nums[left];

    while (i < j) {
        while (i < j && nums[j] >= base) j--;

        while (i < j && nums[i] <= base) i++;

        if (i < j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    // i == j !!!
    nums[left] = nums[i];
    nums[i] = base;

    quickSortII(nums, left, j - 1);
    quickSortII(nums, j + 1, right);
}
/**
 * 以中间的元素作为基准 ==> 适用于自定义基准 ==> 通用的快排算法
 */
public static void quickSortWithMidPivot(int[] nums) {
    quickSortWithMidPivot(nums, 0, nums.length - 1);
}

private static void quickSortWithMidPivot(int[] nums, int left, int right) {
    if (left > right) return;

    int i = left, j = right,
            pivotIndex = (left + right) / 2, // 修改此处 left 和 right 的表达式,可以实现以任何基准
            pivot = nums[pivotIndex];

    while (i < j) {
        while (i < j && nums[j] >= pivot) j--;
        nums[pivotIndex] = nums[j];
        pivotIndex = j;
        while (i < j && nums[i] <= pivot) i++;
        nums[pivotIndex] = nums[i];
        pivotIndex = i;
    }
    // i == j !!!
    nums[i] = pivot;

    quickSortWithMidPivot(nums, left, i - 1);
    quickSortWithMidPivot(nums, i + 1, right);
}

简单选择排序

/**
 * 简单选择排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列
 */
public static void selectSort(int[] nums) {
    for (int i = 0; i < nums.length; i++) { // 每一趟找到一个最小的元素
        int index = i;
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[j] < nums[index]) {
                index = j;
            }
        }
        // 交换
        if (index != i) {
            int temp = nums[i];
            nums[i] = nums[index];
            nums[index] = temp;
        }
    }
}

堆排序

/**
 * 堆排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列中
 *
 * 可用于链表中
 */
public static void heapSort(int[] nums) {
    for (int i = nums.length - 1; i > 0; i--) { // 当 i = 0 时,就不需要比较
        // 化成大根堆
        BigHeapI(nums, i + 1);
        // 交换元素
        int temp = nums[0];
        nums[0] = nums[i];
        nums[i] = temp;
    }
}
// BigHeapI 和 BigHeapII 逻辑上类似,但是 BigHeapI 代码更加简洁,BigHeapII 比较容易理解
private static void BigHeapI(int[] nums, int length) {
    for (int i = length / 2 - 1; i >= 0; i--) {
        int t = i, temp = nums[t];
        // 调节的算法
        for (int j = 2 * t + 1; j < length; j = (2 * j) + 1) {
            if (j + 1 < length && nums[j] < nums[j + 1]) j++;
            if (temp >= nums[j]) break; // 已经是大根堆了 【 >= 是保证确保相同元素 】
            else { // 不是大根堆时:即 j 比 t 大,所以 t 与 j 交换,并且 t 下沉
                nums[t] = nums[j];
                t = j;
            }
        }
        nums[t] = temp;
    }
}

private static void BigHeapII(int[] nums, int length) {
    // 从后往前遍历根(i < length / 2) ==> 化成大根堆
    for (int i = length / 2 - 1; i >= 0; i--) {
        // 根 i => 左孩子: 2i+1 | 右孩子: 2i+2
        int t = i, temp = nums[t];
        for (int j = 2 * t + 1; j < length; j = (2 * j) + 1) { // 沿着子树递归
            // 确定两个左右孩子的大小
            if (j + 1 < length && nums[j] < nums[j + 1]) j++;
            if (temp >= nums[j]) break; // 已经是大根堆了 【 >= 是保证确保相同元素 】
            else { // 不是大根堆时:即 j 比 t 大,所以 t 与 j 交换,并且 t 下沉
                nums[t] = nums[j]; nums[j] = temp;
                // 将 j 作新根继续下沉
                t = j; temp = nums[t];
            }
        }
    }
}

归并排序

/**
 * 归并排序:将两个或多个有序序列合并成一个有序序列
 *
 * 将 nums 划分成多个有序的子序列,每次合并相邻的两个子序列
 */
public static void mergeSort(int[] nums) {
    int[] copy = new int[nums.length]; // 用于保存副本的元素
    mergeSort(nums, 0, nums.length - 1, copy);
}
// 划分子序列
private static void mergeSort(int[] nums, int left, int right, int[] copy) {
    if (left < right) {
        int mid = (left + right) / 2;
        mergeSort(nums, left, mid, copy);
        mergeSort(nums, mid + 1, right, copy);
        merge(nums, left, mid, right, copy);
    }
}
// 合并
private static void merge(int[] nums, int left, int mid, int right, int[] copy) {
    for (int i = left; i <= right; i++) {
        copy[i] = nums[i]; // 将 nums 中元素复制到 copy 中
    }
    // [left, mid] [mid + 1, right]
    int i, j, k;
    for (i = left, j = mid + 1, k=i; i <= mid && j <= right; k++) {
        if (copy[i] <= copy[j]) nums[k] = copy[i++]; // 较小者复制到 nums 中
        else nums[k] = copy[j++];
    }
    while (i <= mid) nums[k++] = copy[i++];
    while (j <= right) nums[k++] = copy[j++];
}

基数排序

// 桶排序,分配 10 个桶,对应 0 ~ 9
// 不能处理负数
public static void radixSort(int[] nums) {
    // 1. 得到数组中最大的数的位数
    int max = nums[0]; // 假设第一个数就是最大数
    for (int i = 1; i < nums.length; i++) {
        if (nums[i] > max) {
            max = nums[i];
        }
    }
    // 2. 得到最大数是几位数
    int maxLength = (max + "").length();

    // 定义一个二维数组,表示10个桶,每个桶就是一个一维数组
    // 说明
    // 1. 二维数组包含10个一维数组
    // 2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为nums.length
    // 3. 明确,基数排序是使用空间换时间的经典算法
    int[][] bucket = new int[10][nums.length];

    // 为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
    // 可以样理解
    // 比如:bucketElementCounts[0],记录的就是 bucket[0] 桶的放入数据个数
    int[] bucketElementsCounts = new int[10];

    // 这里我们使用循环将代码处理一下
    for (int i = 0,  n = 1; i < maxLength; i++, n*=10) {
        for (int j = 0; j < nums.length; j++) {
            // 取出每个元素的个位、十位、百位等的值
            int digitOfElement = nums[j] / n % 10;
            // 放到对应的桶中
            bucket[digitOfElement][bucketElementsCounts[digitOfElement]] = nums[j];
            bucketElementsCounts[digitOfElement]++;
        }
        // 按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
        int index = 0;
        // 遍历每一个桶,并将桶中数据,放入到原数组
        for (int j = 0; j < bucketElementsCounts.length; j++) {
            // 如果桶中有数据,我们才放入到原数组
            if (bucketElementsCounts[j]!=0) {
                // 循环该桶,即第k个桶(即第k个一维数组),放入
                for (int k = 0; k < bucketElementsCounts[j]; k++) {
                    // 取出元素放入nums
                    nums[index++] = bucket[j][k];
                }
                // 第j轮处理后,需要将每个 bucketElementCounts[j] = 0!!! 清空
                bucketElementsCounts[j] = 0;
            }
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值