寒冬

2019 年 10 月 25 日下午六点零几分,离下班还有不到一个小时,突然接到HR的“DING”:来一趟Wework一楼小会议室。

我想了一下就知道大概是什么事情了,说实话还是有一点突然的,但是一切皆有征兆。已经两个月没发工资了,产品部门已经好久没提需求了,我们部门领导也已经不来上班了。大家心里都知道这家公司已经完了,就等什么时候宣布死亡了。

然后我走进小会议室,两个 HR,通知我今天就是最后一天了,也不用交接了,赶紧收拾一下,把电脑交了就可以走人了。

HR 说你也知道我们公司的情况,我们也不容易,其中一个 HR 也是今天最后一天。稍微寒暄了一下,接着就开始谈赔偿。

工资算到 10 月 25 号,社保交到 10 月份,给半个月工资赔偿。我不同意,至少两个月工资赔偿。

再算一下调休和年假。

年假:还剩两天年假。【2】

调休:还剩一天调休。但是国庆前一天,下午 6 点开始封路,我们部门领导让 5 点半提前下班,强行算作用了半天调休。【0.5】

谈了一个小时还有没吃饭,肚子已经饿得呱呱叫了。不想为了 0.5 天争来争去,就签了离职协议,交了电脑,收拾一下东西走人。

加起来一共还欠我三个月工资,两个月的赔偿,两天年假,半天调休。承诺说十月底会先发第一笔,11 月 20 号结清工资和赔偿。我说如果到时间没发我就去劳动仲裁。

那么现在已经是 11 月 25 号了,整整一个月过去了,工资和赔偿发了没有呢?

10 月 31 号发了八月份的基本工资: 4950。至于剩下的部分,没有任何消息。

顺便说一下,上个周五(11 月 22 号)下午的时候路过Weowrk,还碰到老板了,Hubert 看起来好像心情还不错。

接下来我要开始去劳动仲裁了。时值寒冬,所幸的是,暂时还有一点积蓄,交得起房租,吃得起饭,坚持得下去。

330 total views, 3 views today

PHP Interview Questions(13)

面试时间:2019-11-13 星期三,下午两点。


Yii2 实现原理

Yii2 生命周期

Request Lifecycle
https://www.yiiframework.com/doc/guide/2.0/en/start-workflow

快速排序算法。

分治,递归,每一次递归能够确定一个数的位置。

不稳定算法,平均算法时间复杂度 O(nlog2n),平均空间复杂度 O(log2n)。

<?php

/**
 * Quick Sort Implement 1
 * @param array $array
 * @return array
 */
function quickSort(array $array) {
    if (count($array) <= 1) {
        return $array;
    }

    $middle = $array[0];
    $left = [];
    $right = [];

    for ($i = 1; $i < count($array); $i++) {
        if ($array[$i] < $middle) {
            $left[] = $array[$i];
        } else {
            $right[] = $array[$i];
        }
    }

    return array_merge(quickSort($left), [$middle], quickSort($right));
}

/**
 * Quick Sort Implement 2
 * @param array $array
 * @param  int $start
 * @param  int $end
 */
function quickSort2(array &$array, int $start, int $end) {
    if ($start >= $end) {
        return;
    }

    $i = $start;
    $middle = $array[$start];

    for ($j = $start + 1; $j <= $end; $j++) {
        if ($array[$j] < $middle) {
            $i++;
            exchange($array[$i], $array[$j]);
        }
    }

    exchange($array[$i], $array[$start]);

    quickSort2($array, $start, $i - 1);
    quickSort2($array, $i + 1, $end);
}

function exchange(&$a, &$b) {
    [$a, $b] = [$b, $a];
}

function printArray($array) {
    foreach ($array as $value) {
        echo $value, " ";
    }
    echo "\n";
}

$array = [6, 5, 3, 1, 8, 7, 2, 4];
printArray(quickSort($array));
quickSort2($array, 0, count($array) - 1);
printArray($array);

自动加载原理。

https://zyf.im/2019/04/28/php-composer-basic/

工作中遇到的最难的问题是什么?怎么解决的。

81 total views, 2 views today

PHP Interview Questions(12)

面试时间:2019 年 11 月 5 日,星期二,下午三点。


现在使用的 PHP 版本,有哪些新特性。

PHP 7.2

https://tanghengzhi.com/whats-new-in-php-72/

聊一聊PHP SPL,迭代器和生成器。

/**
 * https://www.php.net/manual/en/intro.spl.php
 **/
The Standard PHP Library (SPL) is a collection of interfaces and classes that are meant to solve common problems.

No external libraries are needed to build this extension and it is available and compiled by default in PHP 5.0.0.

SPL provides a set of standard datastructure, a set of iterators to traverse over objects, a set of interfaces, a set of standard Exceptions, a number of classes to work with files and it provides a set of functions like spl_autoload_register()

/**
 * https://www.php.net/manual/zh/class.iterator.php
 **/

/**
 * https://www.php.net/manual/zh/language.generators.overview.php
 **/

TCP 和 UDP 的区别。

        UDP	TCP
是否连接	无连接	面向连接
是否可靠	不可靠传输,不使用流量控制和拥塞控制	可靠传输,使用流量控制和拥塞控制
连接对象个数	支持一对一,一对多,多对一和多对多交互通信	只能是一对一通信
传输方式	面向报文	面向字节流
首部开销	首部开销小,仅8字节	首部最小20字节,最大60字节
适用场景	适用于实时应用(IP电话、视频会议、直播等)	适用于要求可靠传输的应用,例如文件传输

https://segmentfault.com/a/1190000018582150

什么是 Socket 编程。

Socket 是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

https://www.jianshu.com/p/f671d3895d13

MySQL 索引。

https://dev.mysql.com/doc/refman/8.0/en/optimize-overview.html

MySQL 事务。

https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_transaction

MySQL 读写分离。

'mysql' => [
    'read' => [
        'host' => [
            '192.168.1.1',
            '196.168.1.2',
        ],
    ],
    'write' => [
        'host' => [
            '196.168.1.3',
         ],
    ],
    'sticky'    => true,
    'driver'    => 'mysql',
    'database'  => 'database',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix'    => '',
],

https://laravel.com/docs/6.x/database

常见的 Redis 数据结构。

https://www.runoob.com/redis/redis-data-types.html

Redis 集合(Set) 常用命令。

https://www.runoob.com/redis/redis-sets.html

Redis cluster 原理。

https://redis.io/topics/cluster-tutorial

Git Rebase

https://git-scm.com/book/en/v2/Git-Branching-Rebasing

https://docs.microsoft.com/en-us/azure/devops/repos/git/merging-with-squash?view=azure-devops

Git 工作原理

http://marklodato.github.io/visual-git-guide/index-zh-cn.html

81 total views, 2 views today

PHP Interview Questions(11)

面试时间:2019 年 11 月 4 日,星期一,下午两点。


1. 给定一个二维数组,数组每一行从左到右都是递增的,每一列也是递增的。请完成一个函数,输入为如上二维数组和一个整数,函数功能为判断该整数是否存在于数组中。时间复杂度尽可能的低。(请说明你的算法的复杂度。)

下面是一个例子:

二维数组:

1 2 8 9

2 4 9 12

4 7 10 13

6 8 11 15

数字:9

<?php

$array = [
    [1, 2, 8, 9],
    [2, 4, 9, 12],
    [4, 7, 10, 13],
    [6, 8, 11, 15],
];

$number = 9;

/**
 * 最简单的方法就是遍历整个数组
 * 时间复杂度 O(Row*Column-1)
 */
function findNumberInArray($array, $number) {
    foreach ($array as $rowArray) {
        foreach ($rowArray as $value) {
            echo $value, " ";
            if ($number == $value) {
                return true;
            }
        }
    }

    return false;
}

/**
 * 从二维数组的左下角开始逐行查找
 * 时间复杂度 O(Row+Column-1)
 */
function findNumberInArray2($array, $number) {
    $minRow = 0;
    $minColumn = 0;
    $maxRow = count($array) - 1;
    $maxColumn = count($array[0]) - 1;

    if ($number < $array[0][0] || $number > $array[$maxRow][$maxColumn]) {
        return false;
    }

    for ($row = $maxRow; $row >= $minRow; $row--) {
        for ($column = $minColumn; $column <= $maxColumn; $column++) {
            echo $array[$row][$column], " ";
            if ($array[$row][$column] == $number) {
                return true;
            }
            if ($array[$row][$column] > $number) {
                break;
            }
            if ($array[$row][$column] < $number) {
                $minColumn = $column + 1;
            }
        }
    }

    return false;
}

/**
 * 从二维数组的右上角开始逐行查找
 * 时间复杂度 O(Row+Column-1)
 */
function findNumberInArray3($array, $number) {
    $minRow = 0;
    $minColumn = 0;
    $maxRow = count($array) - 1;
    $maxColumn = count($array[0]) - 1;

    if ($number < $array[0][0] || $number > $array[$maxRow][$maxColumn]) {
        return false;
    }
    
    for ($row = $minRow; $row <= $maxRow; $row++) {
        for ($column = $maxColumn; $column >= $minColumn; $column--) {
            echo $array[$row][$column], " ";
            if ($array[$row][$column] == $number) {
                return true;
            }
            if ($array[$row][$column] > $number) {
                $maxColumn = $column - 1;
            }
            if ($array[$row][$column] < $number) {
                break;
            }
        }
    }

    return false;
}

echo "/**
* 最简单的方法就是遍历整个数组
* 时间复杂度 O(Row*Column-1)
*/\n";
for ($number = 0; $number <= 15; $number++) {
    echo "find ", $number, ": ";
    findNumberInArray($array, $number);
    echo "\n";
}

echo "/**
* 从二维数组的左下角开始逐行查找
* 时间复杂度 O(Row+Column-1)
*/\n";
for ($number = 0; $number <= 15; $number++) {
    echo "find ", $number, ": ";
    findNumberInArray2($array, $number);
    echo "\n";
}

echo "/**
* 从二维数组的右上角开始逐行查找
* 时间复杂度 O(Row+Column-1)
*/\n";
for ($number = 0; $number <= 15; $number++) {
    echo "find ", $number, ": ";
    findNumberInArray3($array, $number);
    echo "\n";
}

2.把数组最开始的若干个元素搬到数组末尾,称为数组的旋转。给定一个递增数组的旋转数组,请完成一个函数,时间复杂度尽可能的低,输出该旋转数组的最小元素。并给出复杂度。

例如输入数组(4,5,6,7,8,10,1,2,3),输出 1。

<?php

$array = [4, 5, 6, 7, 8, 10, 1, 2, 3];

/**
 * 使用 min() 函数
 * 时间复杂度:O(N)
 * 源码:https://github.com/php/php-src/blob/master/ext/standard/array.c
 */
echo min($array), "\n";

/**
 * 使用二分查找法
 * 时间复杂度:O(log2N)
 */
function findMinimumNumberInArray($array, $start = 0, $end = null) {
    if ($end == null) {
        $end = count($array) - 1;
    }

    if ($start == $end) {
        return $array[$start];
    }

    $middle = floor(($start + $end) / 2);

    if ($array[$start] < $array[$end]) {
        return findMinimumNumberInArray($array, $start, $middle);
    } else {
        return findMinimumNumberInArray($array, $middle, $end);
    }
}

echo findMinimumNumberInArray($array), "\n";

3.输入一个字符串,输出该字符串中字符的所有组合。(不限编程语言,请注明你选择的语言)

下面是一个例子:

输入参数:字符串:“abc”

输出:“a”, “b”, “c”, “ab”, “ac”, “bc”, “abc”

<?php

$string = "abc";
$length = strlen($string);

/**
 * 使用二进制表示不同的排列组合
 * 
 * 0 0 1 a
 * 0 1 0 b
 * 0 1 1 ab
 * 1 0 0 c
 * 1 0 1 ac
 * 1 1 0 bc
 * 1 1 1 abc
 */
 for ($i = 1; $i < 1 << $length; $i++) {
     for ($j = 0; $j < $length; $j++) {
         if ($i & (1 << $j)) {
             echo $string[$j];
         }
     }
     echo "\n";
 }

参考:

剑指Offer面试题:2.二维数组中的查找

剑指Offer面试题:7.旋转数组的最小数字

剑指Offer面试题:26.字符串的排列

210 total views, 3 views today

Java 8 函数式编程(4)

本篇对应《Java 8 函数式编程》第 5 章。


要点回顾

  1. 方法 引用 是一 种 引用 方法 的 轻量级 语法, 形如: ClassName:: methodName。
  2. 收集 器 可用 来 计算 流的 最 终值, 是 reduce 方法 的 模拟。
  3. Java 8 提供 了 收集 多种 容器 类型 的 方式, 同时 允许 用户 自定义 收集器。

练习

Question 1:

https://leetcode.com/playground/KrzQMGgR

public static void subquestionA() {
        List<String> collected = Stream.of("a", "b", "hello")
                                       .map(String::toUpperCase)
                                       .collect(Collectors.toList());
        System.out.println(collected);
    }
    
    public static void subquestionB() {
        int count = Stream.of(1, 2, 3)
                          .reduce(0, Integer::sum);
        System.out.println(count);
    }
    
    public static void subquestionC() {
        List<Integer> together = Stream.of(Arrays.asList(1,2), Arrays.asList(3,4))
                                       .flatMap(List::stream)
                                       .collect(Collectors.toList());
        System.out.println(together);
    }

Question 2:

LongestName.java

public class LongestName {

    private static Comparator<Artist> byNameLength = comparing(artist -> artist.getName().length());

    public static Artist byReduce(List<Artist> artists) {
        return artists.stream()
                      .reduce((acc, artist) -> {
                          return (byNameLength.compare(acc, artist) >= 0) ? acc : artist;
                      })
                      .orElseThrow(RuntimeException::new);
    }

    public static Artist byCollecting(List<Artist> artists) {
        return artists.stream()
                      .collect(Collectors.maxBy(byNameLength))
                      .orElseThrow(RuntimeException::new);
    }

}

WordCount.java

public class WordCount {

    public static Map<String, Long> countWords(Stream<String> names) {
        return names.collect(groupingBy(name -> name, counting()));
    }

}

GroupingBy.java

public class GroupingBy<T, K> implements Collector<T, Map<K, List<T>>, Map<K, List<T>>> {

    private final static Set<Characteristics> characteristics = new HashSet<>();
    static {
        characteristics.add(Characteristics.IDENTITY_FINISH);
    }

    private final Function<? super T, ? extends K> classifier;

    public GroupingBy(Function<? super T, ? extends K> classifier) {
        this.classifier = classifier;
    }

    @Override
    public Supplier<Map<K, List<T>>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<K, List<T>>, T> accumulator() {
        return (map, element) -> {
            K key = classifier.apply(element);
            List<T> elements = map.computeIfAbsent(key, k -> new ArrayList<>());
            elements.add(element);
        };
    }

    @Override
    public BinaryOperator<Map<K, List<T>>> combiner() {
        return (left, right) -> {
            right.forEach((key, value) -> {
                left.merge(key, value, (leftValue, rightValue) -> {
                    leftValue.addAll(rightValue);
                    return leftValue;
                });
            });
            return left;
        };
    }

    @Override
    public Function<Map<K, List<T>>, Map<K, List<T>>> finisher() {
        return map -> map;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return characteristics;
    }

}

Question 3:

Fibonacci.java

public class Fibonacci {

    private final Map<Integer,Long> cache;

    public Fibonacci() {
        cache = new HashMap<>();
        cache.put(0, 0L);
        cache.put(1, 1L);
    }

    public long fibonacci(int x) {
        return cache.computeIfAbsent(x, n -> fibonacci(n-1) + fibonacci(n-2));
    }

}

参考:

123 total views, 1 views today

Java 8 函数式编程(3)

本篇对应《Java 8 函数式编程》的第四章。


要点回顾

  1. 使用为基本类型定制的Lambda表达式和Stream,如IntStream可以显著提升系统性能。
  2. 默认方法是指接口中定义的包含方法体的方法,方法名有default关键字做前缀。
  3. 在一个值可能为空的建模情况下,使用Optional对象能替代使用null值。

练习

Question 1:

/** 该接口表示艺术家的演出——专辑或演唱会 */
public interface Performance {

    public String getName();

    public Stream<Artist> getMusicians();

    public default Stream<Artist> getAllMusicians() {
        return getMusicians()
              .flatMap(artist -> concat(Stream.of(artist), artist.getMembers()));
    }

}

Question2:

不能。

public interface Parent {
    default public boolean equals(Object object) {
        return true;
    }

    default public int hashCode() {
        return 1;
    }
}
% javac Parent.java
Parent.java:2: error: default method equals in interface Parent overrides a member of java.lang.Object
    default public boolean equals(Object object) {
                           ^
Parent.java:6: error: default method hashCode in interface Parent overrides a member of java.lang.Object
    default public int hashCode() {
                       ^
2 errors

Question 3:

public class Artists {

    private List<Artist> artists;

    public Artists(List<Artist> artists) {
        this.artists = artists;
    }

    public Optional<Artist> getArtist(int index) {
        if (index < 0 || index >= artists.size()) {
            return Optional.empty();
        }
        return Optional.of(artists.get(index));
    }

    public String getArtistName(int index) {
        Optional<Artist> artist = getArtist(index);
        return artist.map(Artist::getName)
                     .orElse("unknown");
    }

}

参考:

246 total views, 1 views today