码迷,mamicode.com
首页 > 其他好文 > 详细

ARTS习惯(9)

时间:2021-01-20 12:01:02      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:help   意思   查询   ext   泛型类   注意   布隆过滤器   攻击   泛型   

Algorithm

每周至少做一个Leetcode算法题

第1道

【来源】

《剑指Offer》12#

【题目】

设计一个函数,输入整数n,打印1到最大的n位数

例子

输入:3
输出:1,2,...,998,999
解释:最大的3位数是999

【解答】

题目未指定n的大小,需考虑到大数问题,常用的处理大数问题的数据结构是字符串或者数组,本题用字符串来处理。

解法1:字符串模拟数字加法。

掌握2个关键步骤问题迎刃而解,1)模拟加1操作,注意处理溢出;2)打印

解法2:递归实现全排列

本题等价于n个数字0~9的全排列,用递归很容易实现。基于分治算法的思想,先固定高位,向低位递归,当个位已被固定时,打印字符串。

【示例代码】

package com.pengluo.hht_offer.T12_PrintNumbers;

public class Print1ToMaxOfN {


    /**
     * 解法1:字符串模拟数字加法
     * @param n
     */
    public void print1ToMaxOfNBit(int n) {
        if (n <= 0) {
            return;
        }
        // 定义并初始化数组,每个字符的初始值均为‘0‘
        // 约定:number[0]:最高位,number[n-1]:最低位
        char[] number = new char[n];
        for (int i = 0; i < n; i++) {
            number[i] = ‘0‘;
        }
        // 发生溢出时,结束打印
        while (!increment(number)) {
            printNumber1(number);
        }
    }

    /**
     *  辅助方法:模拟字符串数字加1
     * @param number
     * @return true表示发生溢出,flase表示正常
     */
    private boolean increment(char[] number) {
        // 溢出标志位
        boolean isOverFlow = false;
        // 进位
        int TakeOver = 0;
        // 每个字符表示的数字
        int Snum =0;
        int length = number.length;
        for (int i = length-1; i >= 0; i--) {
            Snum = number[i] - ‘0‘ + TakeOver;
            if (i == length-1) {
                Snum++;
            }
            // 处理进位逻辑
            if (Snum >= 10) {
                if (i == 0) {
                    isOverFlow = true;
                }
                TakeOver = 1;
                Snum -= 10;
                number[i] = (char) (‘0‘ + Snum);
            } else {
                number[i] = (char) (‘0‘ + Snum);
                break;
            }
        }
        return isOverFlow;
    }

    /**
     * 辅助方法:打印
     * @param number
     */
    private void printNumber1(char[] number) {
        boolean flag = false;
        int length = number.length;
        for (int i = 0; i <length; i++) {
            // 数字左边的0不输出
            if (number[i] != ‘0‘) {
                flag = true;
            }
            if (flag) {
                System.out.print(number[i]);
            }

        }
        System.out.println();
    }

    /**
     * 解法2:递归法实现全排列
     *
     * @param n
     */
    public void print1ToMaxNBitByRecursively(int n) {
        if (n <= 0) {
            return;
        }
        char[] number =new char[n];
        for (int i = 0; i < 10; i++) {
            number[0] = (char) (i + ‘0‘);
            printByRecursively(number, n, 0);
        }

    }

    private void printByRecursively(char[] number, int length, int index) {
        // 递归结束条件
        if (index == length - 1) {
            printNumber1(number);
            return;
        }
        for (int i = 0; i < 10; i++) {
            number[index + 1] = (char) (i + ‘0‘);
            printByRecursively(number, length, index+1);
        }
    }

    /**
     *  错误范例:未统一number表示的数字格式,此方法,默认number[0]为最低位
     * @param number
     */
    private void printNumber(char[] number) {
        boolean flag = false;
        int length = number.length;
        for (int i = length - 1; i >= 0; i--) {
            // 数字左边的0不输出
            if (number[i] != ‘0‘) {
                flag = true;
            }
            if (flag) {
                System.out.print(number[i]);
            }

        }
        System.out.println();
    }

}

【拓展】

  • 将本题的打印改成返回一个int[],数组存储所有的打印结果

  • 设计一个函数,可以输出两个数相加的结果。注意输入的参数可能是负数

总结

  • Int、long 等类型都有表示的数字范围,不能处理大数。需要用String表示大数

  • 解法1是用字符串模拟数字加法运算

  • 解法2是用分治思想,递归实现

Review

阅读并点评至少1篇英文技术文章

【原文】:Head First Java 2nd Edition CH16&core java CH8

【点评】

泛型机制是从Java 5开始支持的,专家组花了将近5年时间定义和规范。泛型在集合中应用广泛,ArrayList就是应用最多的集合之一。

泛型编程的优点是可读性好安全性高代码可重用

类型参数(type parameters)

// 添加类型参数:<String>
// java 7开始,构造函数中的泛型类型可以省略
ArrayList<String> files = new ArrayList<>();

做一个泛型程序员

泛型程序员的任务是:预测所用类未来所有可能有的用途。这相当难却很有价值。

大多数Java程序员满足于API调用

定义一个简单的泛型类(Generic class)

// 类型变量:<T>,它指定了方法的返回类型,域、局部变量的类型都为T
public class Pair<T> {

}

定义一个简单的泛型方法

public class ArrayAlg{
    
    public static <T> T getMiddle(T[] a) {
        return a[a.length/2];
    }
}

类型变量的限定

 public class ArrayAlg{
    // 在泛型中,`extends`是`extends` 或`implements`的意思
    // 限定一个
    // 表示只有实现Comparable接口的类可以传入,否则编译报错
    public static <T extends Comparable> T min(T[] a) {
        
    }
     
    // 限定类型用&连接
    public static <T extends Comparable & Serializle> T min(T[] a) {
        
    }
     
     
}

泛型代码和虚拟机

JVM里没有泛型代码,全部是普通的类。JVM自动通过类型擦除把泛型类变成原始类型

类型擦除

  • 用第一个限制类代替泛型变量T,没有明确限制则默认用Object代替T
// 擦除类型:去掉class上的<T>,域和成员变量T换成对应的类
// 1) public class Pair<T> {}
		T-->Object
// 2)public class <T extends Comparable & Serializle> implements Serializle {}
        T-->Comparable
// 3)public class <T extends Serializle & Comparable> implements Serializle {}
		T-->Serializle

JVM自动生成桥方法,用来处理多态

通配符类型

如果是一名库程序员,一定要习惯通配符类型

// 使用通配符,也就是说Employee的所有子类都可以做方法的参数.可以读取(getter)
public static void printBuddies(Pair<? extends Employee> p)
   {
      Employee first = p.getFirst();
      Employee second = p.getSecond();
      System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
   }

// 超类限制,Manager及其子类可以做方法的参数,可以写入(setter)
public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
   {
      if (a.length == 0) return;
      Manager min = a[0];
      Manager max = a[0];
      for (int i = 1; i < a.length; i++)
      {
         if (min.getBonus() > a[i].getBonus()) min = a[i];
         if (max.getBonus() < a[i].getBonus()) max = a[i];
      }
      result.setFirst(min);
      result.setSecond(max);
   }

// 无限定通配符
class PairAlg
{
   public static boolean hasNulls(Pair<?> p)
   {
      return p.getFirst() == null || p.getSecond() == null;
   }

   public static void swap(Pair<?> p) { swapHelper(p); }

   public static <T> void swapHelper(Pair<T> p)
   {
      T t = p.getFirst();
      p.setFirst(p.getSecond());
      p.setSecond(t);
   }
}

Tip

学习至少一个技术技巧

缓存穿透、缓存击穿、缓存雪崩的问题由来,解决方法

缓存穿透:缓存和数据库都没有命中,大部分场景时恶意攻击,大量的请求到达数据库,数据库不堪重负崩了

解决方案:

  • 接口层做校验,用户鉴权校验
  • 布隆过滤器:将所有存在的数据哈希存到位图中,那么布隆过滤器判断不存在,那么数据库中一定没有,拦截即可。相比直接存到缓存redis中,它的优点是节省空间,比如10亿条数据存到Map等数据结构中,占用900G的空间。大概了解
  • id做基础校验,id<0(非法参数)直接拦截
  • 数据库未命中时,设置短时间的空值缓存,如1分钟。缓解数据库压力

缓存击穿:缓存中没有,数据库中有。通常是:缓存时间到期,此时有大量并发请求,缓存中取不到数据,数据库瞬间压力很大。

解决方案:

  • 热点数据永不过期
  • 查询DB过程加互斥锁
    • Redis的SETNX
    • Memcache的ADD
    • ReentrantLock

缓存雪崩:缓存中数据批量的到期,需要去数据库查询,数据库压力大增。和缓存击穿不同点在,后者是同一key大量并发请求,

前者这里是很多key的缓存都集体失效

解决方案:

  • 缓存过期时间设置为随机,避免一个时间同时过期
  • 用分布式,将热点数据均匀存在不同redis数据节点
  • 热点数据永不过期

Share

分享一篇有观点和思考的技术文章

一名资深系统工程师放羊去了

ARTS习惯(9)

标签:help   意思   查询   ext   泛型类   注意   布隆过滤器   攻击   泛型   

原文地址:https://www.cnblogs.com/PengLuo22/p/14299900.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!