学习日记记录---部分排序+雪花

一、部分排序算法说明与代码实践

1、排序算法-冒泡排序

1.1插画说明:

在这里插入图片描述

1.2排序简单原理简述:

1、冒泡排序,是通过每一次遍历获取最大/最小值
2、将最大值/最小值放在尾部/头部
3、然后除开最大值/最小值,剩下的数据在进行遍历获取最大/最小值

1.3代码实现:

//冒泡排序,升序
    public static int[] maoPao(int[] arry){
        //外层循环是控制需要比较的次数
        for (int i = 0; i < arry.length; i++) {
            //内存循环是控制每次需要比较的趟数
            for (int j = 0; j < arry.length -i-1; j++) {
                //判断数组中的前者是否大于后者,大=>交换,反正进行下一次比较
                if(arry[j] > arry[j+1]){
                    //创建一个交换数组值的中间变量temp
                    int temp = arry[j];
                    //较小值提前,较大值往后
                    arry[j] = arry[j+1];
                    arry[j+1] = temp;
                }
            }
        }
        return arry;
    }

2、排序算法-选择排序

2.1插画说明:

在这里插入图片描述

2.2排序简单原理简述:

1、将第一个值看成最小值
2、然后和后续的比较找出最小值和下标
3、交换本次遍历的起始值和最小值
4、每次遍历的时候,将前面找出的最小值,看成一个有序的列表,后面的看成无序的列表,然后每次遍历无序列表找出最小值

2.3代码实现:

//选择排序,升序
    public static int[] choose(int[] arry){
        for (int i = 0; i < arry.length; i++) {
            //先将当前的数据设置为最小值
            int min = arry[i];
            //记录最小值的下标信息
            int minIndex = i;
            //以上述值作为最小值,往后逐个取对应的值进行比较,值min大=>交换,反正不做处理
            for (int j = i+1; j < arry.length; j++) {
                //将min和后面数据进行比较
                if(min > arry[j]){
                    //更新最小值
                    min = arry[j];
                    //更新最小值的下标信息
                    minIndex = j;
                }
            }
            //将循环完且比较完的数据进行值更新
            int temp = arry[i];
            arry[i] = min;
            arry[minIndex] = temp;
        }
        return arry;
    }

3、排序算法-插入排序

3.1插画说明:

在这里插入图片描述

3.2排序简单原理简述:

1、默认从第二个数据开始比较
2、如果第二个数据比第一个小,则交换。然后在用第三个数据比较,如果比前面小,则插入(狡猾)。否则,退出循环
3、默认将第一数据看成有序列表,后面无序的列表循环每一个数据,如果比前面的数据小则插入(交换)。否则退出

3.3代码实现:

//插入排序,升序
    public static int[] insertSort(int[] arry){
        //第一层循环,从数组的第二个元素开始,逐个往前比较,比前小=>交换,反之不做操作
        for (int i = 1; i < arry.length; i++) {

            //第二层循环就是将当前的值和往前的每个值进行比较,小就做交换,反之不做交换
            for (int j = i; j > 0; j--) {
                //判断后一个数是否小于前一个数,小于的话就需要交换位置,达到升序的效果
                if(arry[j] < arry[j-1]){
                    int temp = arry[j-1];
                    arry[j-1] = arry[j];
                    arry[j] = temp;
                }
                //如果不小于,说明比较已经完毕(因为是数组从下标1开始的,1和0开始比较的,所以,此数之前应该都是有序的数据)
                else{
                    break;
                }
            }
        }
        return arry;
    }

4、排序算法-快速排序

4.1插画说明:

在这里插入图片描述

4.2排序简单原理简述:

1、确认列表第一个数据为中间值,第一个值看成空缺(低指针空缺)
2、然后在剩下的队列中,看成有左右两个指针(高低)。
3、开始高指针向左移动,如果遇到小于中间值的数据,则将这个数据赋值到低指针空缺,并且将高指针的数据看成空缺值(高指针空缺)。然后先向右移动一下低指针,并且切换低指针移动
4、当低指针移动到大于中间值的时候,赋值到高指针空缺的地方。然后先高指针向左移动,并且切换高指针移动。重复c、d操作
5、直到高指针和低指针相等时退出,并且将中间值赋值给对应指针位置
6、然后将中间值的左右两边看成行的列表,进行快速排序操作

4.3代码实现:

//快速排序,升序
    public static int[] quickSort(int[] arry,int low, int high){
        //如果指针在同一位置(只有一个数据时),退出
        if (high - low < 1) {
            return arry;
        }
        //标记,从高指针开始,还是低指针(默认高指针)
        boolean flag = true;
        //记录指针的其实位置
        int start = low;
        int end = high;
        //默认中间值为低指针的第一个值
        int midValue = arry[low];
        while (true) {
            //高指针移动
            if (flag) {
                //如果列表右方的数据大于中间值,则向左移动
                if (arry[high] > midValue) {
                    high--;
                } else if (arry[high] < midValue) {
                    //如果小于,则覆盖最开始的低指针值,并且移动低指针,标志位改成从低指针开始移动
                    arry[low] = arry[high];
                    low++;
                    flag = false;
                }
            } else {
                //如果低指针数据小于中间值,则低指针向右移动
                if (arry[low] < midValue) {
                    low++;
                } else if (arry[low] > midValue) {
                    //如果低指针的值大于中间值,则覆盖高指针停留时的数据,并向左移动高指针。切换为高指针移动
                    arry[high] = arry[low];
                    high--;
                    flag = true;
                }
            }
            //当两个指针的位置相同时,则找到了中间值的位置,并退出循环
            if (low == high) {
                arry[low] = midValue;
                break;
            }
        }
        //然后出现有,中间值左边的小于中间值。右边的大于中间值。
        //然后在对左右两边的列表在进行快速排序
        quickSort(arry, start, low -1);
        quickSort(arry, low + 1, end);
        return arry;
    }

二、雪花算法初识

1、用途与结构简述:

1、雪花算法是使用一个64 bit 的 long 型的数字作为全局唯一 id,其64bit构成结构如下说明:

(1)第一部分:是1个bit,0,是无意义的

(2)第二部分:是41个bit,这里是用来存时间戳的

(3)第三部分:是5个bit,表示机房的id,例如10001

(4)第四部分:是5个bit,表示机器id,例如11001

(5)第五部分:是12个bit,表示序列号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,例如0000 00000000

2、每个部分含义说明:

(1)第一部分:因为二进制里第一个 bit 表示符号位,如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0

(2)41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2 ^ 41 - 1 个毫秒值,换算成年就是表示 69 年的时间

(3)5 个 bit 代表机房 id,5 个 bit 代表机器 id。意思就是最多代表 2 ^ 5 个机房(32 个机房),每个机房里可以代表 2 ^ 5 个机器(32 台机器),也可以根据自己公司的实际情况确定。

(4)12 bit 可以代表的最大正整数是 2 ^ 12 - 1 = 4096,也就是说可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。简单来说,你的某个服务假设要生成一个全局唯一 id,那么就可以发送一个请求给部署了 SnowFlake 算法的系统,由这个 SnowFlake 算法系统来生成唯一 id。这个 SnowFlake 算法系统首先肯定是知道自己所在的机房和机器的,比如机房 id = 17,机器 id = 12。接着 SnowFlake 算法系统接收到这个请求之后,首先就会用二进制位运算的方式生成一个 64 bit 的 long 型 id,64 个 bit 中的第一个 bit 是无意义的。接着 41 个 bit,就可以用当前时间戳(单位到毫秒),然后接着 5 个 bit 设置上这个机房 id,还有 5 个 bit 设置上机器 id。最后再判断一下,当前这台机房的这台机器上这一毫秒内,这是第几个请求,给这次生成 id 的请求累加一个序号,作为最后的 12 个 bit。最终一个 64 个 bit 的 id 就出来了,算法可以保证,一个机房的一台机器上,在同一毫秒内,生成了一个唯一的 id。可能一个毫秒内会生成多个 id,但是有最后 12 个 bit 的序号来区分开来。

2、Java代码实现:

public class SnowFlakeIdWork {

    /** 开始时间截 (这个用自己业务系统上线的时间) */
    private final long twepoch = 1575365018000L;

    /** 机器id所占的位数 */
    private final long workerIdBits = 10L;

    /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 时间截向左移22位(10+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits;

    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作机器ID(0~1024) */
    private long workerId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     * @param workerId 工作ID (0~1024)
     */
    public SnowFlakeIdWork(long workerId) {
        /*2^5,工作机器的ID占64位中的五位*/
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("工作ID已超限或者为小于零的数字,请确认", maxWorkerId));
        }
        this.workerId = workerId;
    }

    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("生成时间有误,请确认", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列,后12位bit进行区分
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift)
                | (workerId << workerIdShift) 
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
}

3、SnowFlake算法的优点:

(1)高性能高可用:生成时不依赖于数据库,完全在内存中生成。

(2)容量大:每秒中能生成数百万的自增ID。

(3)ID自增:存入数据库中,索引效率高。

4、SnowFlake算法的缺点:

依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值