算法-排序-01

简介

'稳定性' : 不改变两个相同元素的相对位置
'In-place' : 原地算法, 不依赖额外的资源或者依赖少数的额外资源,仅依靠输出来覆盖输入

在这里插入图片描述

准备

随机数组

public static Integer[] random(int count, int min, int max) {
    if (count <= 0 || min > max) return null;
    Integer[] array = new Integer[count];
    int delta = max - min + 1;
    for (int i = 0; i < count; i++) {
        array[i] = min + (int)(Math.random() * delta);
    }
    return array;
}

时间测试

import java.text.SimpleDateFormat;
import java.util.Date;

public class Times {
	private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS");
	
	public interface Task {
		void execute();
	}
	
	public static void test(String title, Task task) {
		if (task == null) return;
		title = (title == null) ? "" : ("【" + title + "】");
		System.out.println(title);
		System.out.println("开始:" + fmt.format(new Date()));
		long begin = System.currentTimeMillis();
		task.execute();
		long end = System.currentTimeMillis();
		System.out.println("结束:" + fmt.format(new Date()));
		double delta = (end - begin) / 1000.0;
		System.out.println("耗时:" + delta + "秒");
		System.out.println("-------------------------------------");
	}
}

冒泡排序

/**
 * 冒泡排序 : 通过前后两个元素比较, 将大的元素网后移动
 */

public static int[] bubbleSort(int[] array) {
    int[] sortArray = array;
    // 第一次迭代, 找出最大值, 一共需要 length - 1 次
    for (int i = 0; i < sortArray.length - 1; i++) {
        // 迭代到最后
        for (int j = 0; j < sortArray.length - 1; j++) {
            if (sortArray[j] > sortArray[j + 1]) {
                Util.swap(j + 1, j, sortArray);
            }
        }
    }
    return sortArray;
}

'优化一'
public static int[] bubbleSort1(int[] array) {
    int[] sortArray = array;
    // 第一次迭代, 一共需要 length - 1 次
    for (int i = 0; i < sortArray.length - 1; i++) {
        // 第 i 次只需迭代到 (length - i - 1) 位, 之后是迭代好的
        for (int j = 0; j < sortArray.length - i - 1; j++) {
            if (sortArray[j] > sortArray[j + 1]) {
                Util.swap(j + 1, j, sortArray);
            }
        }
    }
    return sortArray;
}

'优化二, 一般不用'
public static int[] bubbleSort1(int[] array) {
    int[] sortArray = array;
    // 第一次迭代, 一共需要 length - 1 次
    for (int i = 0; i < sortArray.length - 1; i++) {
        // 标记是否进行交换
        boolean flag = true;
        // 第 i 次只需迭代到 (length - i - 1) 位, 之后是迭代好的
        for (int j = 0; j < sortArray.length - i - 1; j++) {
            if (sortArray[j] > sortArray[j + 1]) {
                Util.swap(j + 1, j, sortArray);
                flag = false;
            }
        }
        // true 说明没有进行交换, 直接退出交换
        if (flag){ break; }
    }
    return sortArray;
}

选择排序

/**
 * 选择排序 : 每次跳出最大的元素, 并与数组最后的元素进行交换
 */
public static Integer[] selectionSort(Integer[] array) {
    Integer[] sortArray = array;
	// 第一次迭代, 一共需要 length - 1 次
    for (int i = 0; i < sortArray.length - 1; i++) {
        // 记录最大值下标, 默认为首个元素
        int bigIndex = 0;
        // 开始寻找 第 (i+1) 大的值
        for (int j = 1; j < sortArray.length - i; j++) {
            // 等于使得找到最后面的相同大的值, 使得排序算法稳定
            if (sortArray[j] >= sortArray[bigIndex]) {
                bigIndex = j;
            }
        }
        // 第 (i+1) 大的值, 放入数组倒数 (i + 1) 的末尾
        Util.swap(bigIndex, sortArray.length - i - 1, sortArray);
    }
    return sortArray;
}

堆排序

public static void heapSort(Integer[] array) {
    // 原地建堆, 自下而上的下滤
    int heapSize = array.length;
    for (int i = (heapSize / 2) - 1; i >= 0; i--) {
        siftDown(i,array,heapSize);
    }

    while (heapSize > 1) {
        // 交换堆顶元素和尾部元素
        Util.swap(0, --heapSize, array);
        // 对 0 位置进行 siftDown(恢复堆的性质)
        siftDown(0,array,heapSize);
    }
}

// 下滤, 使得前 heapSize 个元素符合二叉堆
private static void siftDown(int index, Integer[] array,int heapSize) {
    Integer element = array[index];

    // index必须是非叶子节点
    int half = heapSize / 2;
    while (index < half) {
        // 默认是左边跟父节点比  左节点 = index * 2 + 1
        int childIndex = index * 2 + 1;
        Integer child = array[childIndex];

        int rightIndex = childIndex + 1;
        // 右子节点存在且比左子节点大
        if (rightIndex < heapSize && array[rightIndex] > child) {
            child = array[childIndex = rightIndex];
        }

        // 大于等于子节点, 已符合二叉堆, 无需迭代
        if (element >= child) break;

        // 更新, 继续迭代
        array[index] = child;
        index = childIndex;
    }
    array[index] = element;
}

插入排序

/**
 * 插入排序 : 每当扫描到一个元素,就将它插入到头部合适的位置,使得头部数据依然保持有序
 * 	逆序对 : 
 *		数组 <2,3,8,6,1> 的逆序对为 : <2,1> <3,1> <8,1> <8,6> <6,1>,共5个
 * 插入排序的时间复杂度与逆序对的数量成正比关系
 */
public static void insertSort(Integer[] array) {
    Integer[] sortArray = array;
    // 第一次迭代, 一共需要 length - 1 次
    for (int i = 1; i < array.length; i++) {
        int flag = i;
        // 开启循环, 直至在排好序部分排序完毕
        while (flag > 0 && sortArray[flag] < sortArray[flag - 1]) {
            Util.swap(flag, flag - 1, sortArray);
            flag--;
        }
    }
}

'优化一 : 将多次交换抽取出, 最后将要插入的元素插入'
public static void insertSort1(Integer[] array) {
    Integer[] sortArray = array;

    for (int i = 1; i < array.length; i++) {
        int flag = i;
        // 获取要查入对象的值
        int value = sortArray[flag];
        // 迭代, 凡是不符合要求的, 往后移动一位
        while (flag > 0 && value < sortArray[flag - 1]) {
            sortArray[flag] = sortArray[flag - 1];
            flag--;
        }
        // 正式插入
        sortArray[flag] = value;
    }
}

'优化二 : 用二分搜索替代搜索, 查找第一个比插入元素大的值, 然后插入此位置'
/**
 * 查找 : 第一个比插入元素大的值
 */
private static int search(Integer[] array, int index) {
    int begin = 0;
    int end = index;
	// 开始查找
    while (begin < end) {
        int mid = (begin + end) / 2;
        // 小, 更新分段
        if (array[index] < array[mid]) {
            end = mid;
        } else { // 大于等于, 所有可能都要舍去, 更新分段
            begin = mid + 1;
        }
    }
    // 返回下标
    return begin;
}
/**
 * 插入元素到指定位置
 */ 
private static void insert(Integer[] array, int insertIndex, int valueIndex) {
    Integer value = array[valueIndex];
    for (int i = valueIndex; i > insertIndex; i--) {
        array[i] = array[i - 1];
    }
    array[insertIndex] = value;
}
/**
 * 排序
 */
public static Integer[] insertSort2(Integer[] array) {
    Integer[] sortArray = array;
    // 循环插入
    for (int i = 1; i < array.length; i++){
        insert(sortArray,search(sortArray,i),i);
    }
    return sortArray;
}

归并排序

/**
 * 归并排序 : 分裂成小数组, 然后继续组合
 * 门面方法
 */
public static Integer[] mergeSort(Integer[] array) {
    Integer[] sortArray = array;
    mergeSort(sortArray, 0, sortArray.length);
    return sortArray;
}

/**
 * 对 [begin, end) 进行排序
 */
private static void mergeSort(Integer[] array, int begin, int end) {
	// 只有一个元素, 无需分裂
    if (end - begin < 2) {
        return;
    }
	// 递归归并
    int mid = (begin + end) / 2;
    mergeSort(array, begin, mid);
    mergeSort(array, mid, end);
    merge(array, begin, mid, end);
}

/**
 * 将 array[begin, mid) 和 array[mid,end) 合并
 */
private static void merge(Integer[] array, int begin, int mid, int end) {
    // 将 array[begin, mid) 复制到 left[0, mid - begin)
    Integer[] left = new Integer[mid - begin];
    for (int i = 0; i < left.length; i++) {
        left[i] = array[begin + i];
    }
	// 迭代下标
    int i = 0;
    int j = mid;
	// 目标将 left 数组归并完成, 因为只要 left 数组归并完成, 后面的 array 数组部分是排好序的, 无需归并
    while (i < left.length) {
        // array[mid,end) 未插入完全, 且 array[j] 小
        if (j < end && left[i] > array[j]) {
            array[begin++] = array[j++];
        } else { //  array[mid,end) 插入完全, 或 left[i] 小
            array[begin++] = left[i++];
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值