ARTS-2019十二月第二周

十二月第二周ARTS

Posted by Mayday on December 16, 2019

一、Algorithm

本周算法完成如下:

1.1 题目来源【Leetcode 31 下一个排列】

1.2 题目描述

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 1,2,3 → 1,3,2 3,2,1 → 1,2,3 1,1,5 → 1,5,1

1.3 解题如下:

package cn.mayday.algorithms.day20191212;

import java.util.Arrays;

/**
 * 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
 * <p>
 * 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
 * <p>
 * 必须原地修改,只允许使用额外常数空间。
 * <p>
 * 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
 * 1,2,3 → 1,3,2
 * 3,2,1 → 1,2,3
 * 1,1,5 → 1,5,1
 */
public class Leetcode31NextPermutation {

    /**
     * 测试函数
     *
     * @param args
     */
    public static void main(String[] args) {
        Leetcode31NextPermutation permutation = new Leetcode31NextPermutation();
        int[] nums1 = new int[]{1, 2, 3};
        permutation.nextPermutation(nums1);
        System.out.println("result1" + Arrays.toString(nums1)); // 1、3、2

        int[] nums2 = {3, 2, 1};
        permutation.nextPermutation(nums2);
        System.out.println("result2" + Arrays.toString(nums2)); // 1、2、3

        int[] nums3 = {1, 1, 5};
        permutation.nextPermutation(nums3);
        System.out.println("result3" + Arrays.toString(nums3)); // 1、5、1

        int[] nums4 = {1, 3, 2};
        permutation.nextPermutation(nums4);
        System.out.println("result3" + Arrays.toString(nums4)); // 2、1、3
    }

    /**
     * 实现方法
     *
     * @param nums
     */
    public void nextPermutation(int[] nums) {
        /**
         * 1 找到逆序区边界
         * 2 逆序区的前一个数值和逆序区最小值替换位置
         * 3 逆序区重排序
         */
        int length = nums.length;
        int reverseIndex = findReverseIndex(nums);
        // System.out.println("逆序索引:" + reverseIndex);

        if (reverseIndex == 0) {
            // 如果等于0,说明全部逆序,直接输出最小值即可
        } else {
            // 如果不等于0,将逆序区的前一位和逆序区中刚刚大于它的数值交换位置即可
            switchHeadNum(nums, reverseIndex);
        }

        // 冒泡排序--逆序区
        boolean isChanged = true;
        for (int i = reverseIndex; i < length; i++) {
            for (int j = i + 1; j < length; j++) {
                if (nums[i] > nums[j]) {
                    int temp = nums[j];
                    nums[j] = nums[i];
                    nums[i] = temp;
                    isChanged = false;
                }
            }
            if (isChanged) {
                break;
            }
        }
    }

    /**
     * 查询出现逆序时的前一个索引。也就是该索引后数据都是顺序
     *
     * @param nums
     * @return
     */
    private int findReverseIndex(int[] nums) {
        for (int i = nums.length - 1; i > 0; i--) {
            if (nums[i - 1] < nums[i]) {
                return i;
            }
        }
        return 0;
    }

    /**
     * 交换第一个逆序和第一个比它大的数字位置
     *
     * @param nums
     * @param reverseIndex
     */
    private void switchHeadNum(int nums[], int reverseIndex) {
        int changedNum = nums[reverseIndex - 1];
        // 从逆序最后一个开始找,肯定能找到刚刚大于他的数字
        for (int i = nums.length - 1; i >= reverseIndex; i--) {
            if (nums[i] > changedNum) {
                nums[reverseIndex - 1] = nums[i];
                nums[i] = changedNum;
                break;
            }
        }
    }
}

1.4 总结

这道题考察对问题的理解,如何找到下一组排列,归根到底为字典序算法

字典序,就是按照字典中出现的先后顺序进行排序。

字典序算法用来解决这样一个问题:给定其中一种排列,求基于字典序的下一种排列。

详细说明见字典序算法详解

二、Review

2.1 文章内容

Facebook的论文《Scaling Memcache at Facebook

本篇文章介绍了FaceBook在缓存方面的设计结构和优化,以及数据对比。(还没看完,下周继续)

三、Tip

学习使用了阿里巴巴 开源的Arthas 工具,强烈推荐。

3.1 该工具具有以下功能

当你遇到以下类似问题而束手无策时,Arthas 可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?

3.2 对于个人目前最经常使用的功能:

Watch功能

通过watch命令可以查看函数的参数/返回值/异常信息。

watch demo.MathGame primeFactors returnObj

输入 Q 或者 Ctrl+C 退出watch命令。

1576506154346

3.3 高阶特性

阿里开源诊断工具Arthas进阶命令

3.4 官方学习文档

https://alibaba.github.io/arthas/

四、Share

本周分享文章为浩子叔的缓存更新的文章

4.1 文章内容

文章内容见下面链接

缓存更新的套路—左耳朵耗子

4.2 总结思考

(1)更新缓存的四种Design Pattern

  • Cache aside—-旁路缓存
  • Read through—-读缓存穿透
  • Write through—写缓存穿透
  • Write behind caching—先更新缓存在更新持久层【Linux文件系统的Page Cache的算法

这些Design Pattern,其实并不是软件架构里的mysql数据库和memcache/redis的更新策略,这些东西都是计算机体系结构里的设计,比如CPU的缓存,硬盘文件系统中的缓存,硬盘上的缓存,数据库中的缓存。基本上来说,这些缓存更新的设计模式都是非常老古董的,而且历经长时间考验的策略,所以这也就是,工程学上所谓的Best Practice,遵从就好了

(2)FaceBook的做法

  • 先更新数据库,再删除缓存

  • 设置缓存实现时间

是不是Cache Aside这个就不会有并发问题了?不是的,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

但,这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

4.3 扩展内容

通过2PC或是Paxos协议保证一致性如何实现 下次补充