2020/7/30前端学习日记

react

昨天面试被问到diff算法,将不出个所以然,所以今天就打算好好看看。

传统diff算法
先说说传统diff算法,一般看到react的diff算法的时候都会提到这个“传统diff算法的时间复杂度是O(n^3)”,但是一直不知道为什么是n ^ 3而不是n^2。
首先用旧的树的每一个结点对新的树的每一个结点进行遍历,这样的复杂度为O(n^2),然后查找对比差异后计算最小的转换方式,最终的时间复杂度是O(n ^ 3)。

react的diff算法
1.什么是调和?
将虚拟DOM树转化成真实DOM树的最少操作过程称为调和。
2.三大策略
(1)tree diff
React在新旧两颗虚拟DOM树之间进行比较,注意是两个虚拟DOM,并不是虚拟与真实DOM之间进行比较。
对树分层比较,只比较同一层次节点,如果该节点不存在,则该节点及其子结点将会被完全删除。
只需遍历一次就能完成整颗DOM树的比较。

(2)component diff
也有三种策略(也可以说是三种情况吧)
1.同一类型的两个组件,按原策略(层级比较)继续比较虚拟DOM即可。
2.同一类型的两个组件,组件A变化为组件B时,可能虚拟DOM没有任何变化,所以用户可以通过shouldComponentUpdate()来判断是否需要判断计算。(看介绍的时候这里我有个疑惑,一二两点难道不是同一个意思吗?我觉得应该是在比较同一类型的两个组件的情况下,可以选择是否调用shouldComponentUpdate()函数来进行判断。)
3.不同类型组件将一个(将被改变的)组件判断为脏组件,从而替换整个组件的所有节点。(就是直接替换嘛说的这么复杂。。)
注意,如果组件D和组件G结构相似,但是react判断是不同类型的组件,则不会比较其结构,而是删除组件D及其子结点,创建组件G及其子结点。

(3)element diff
当节点处于同一层级时(比如ListItem标签),diff提供三种节点操作:删除、插入、移动
插入:组件C不在集合(A,B)中,需要插入
删除:1.组件不在集合中,但是已经被更改,不能复用和更新,需要删除旧组件,创建新组件。
2.集合中不再包含旧组件,旧组件需要被删除。
移动:组件已经在集合中,且组件未发生更新,只是位置改变。
下面再具体介绍一下移动的逻辑。
移动不再像传统diff算法一样,比较对应位置的节点。

在这里插入图片描述
以上面图片这种情况为例。
react先从新中取得B,然后判断旧中是否存在相同节点B,当发现存在节点B后,就判断是否移动B。

先说一个概念:lastIndex,有点像浮标,或者说一个map的索引,一开始是默认值0,他会与mao中的元素进行比较,比较完后,会改变自己的值(取index和lastindex的较大数),看了后面的解释就会明白。

B在旧 中的index=1,它的lastIndex=0,不满足 index < lastIndex 的条件,因此 B 不做移动操作。此时,lastIndex=(index,lastIndex)中的较大数=1.

看着 A,A在旧的index=0,此时的lastIndex=1(因为先前与新的B比较过了),满足index<lastIndex,因此,对A进行移动操作,此时lastIndex=max(index,lastIndex)=1。

看着D,同(1),不移动,由于D在旧的index=3,比较时,lastIndex=1,所以改变lastIndex=max(index,lastIndex)=3。

看着C,同(2),移动,C在旧的index=2,满足index<lastIndex(lastIndex=3),所以移动。

由于C已经是最后一个节点,所以diff操作结束。

diff的不足与有待优化的地方。
在这里插入图片描述
看图的 D,此时D不移动,但它的index是最大的,导致更新lastIndex=3,从而使得其他元素A,B,C的index<lastIndex,导致A,B,C都要去移动。

理想情况是只移动D,不移动A,B,C。因此,在开发过程中,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,会影响React的渲染性能。

(参考:https://www.jianshu.com/p/3ba0822018cf

算法题

栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true 解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4, push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2之前弹出。

提示:

0 <= pushed.length == popped.length <= 1000
0 <= pushed[i], popped[i] < 1000
pushed 是 popped 的排列。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        if(pushed.length == 0) return true;
        Stack<Integer> stack = new Stack<Integer>();
        int i = 0;
        for(int num : pushed){
            stack.push(num);
            while(!stack.isEmpty() && stack.peek() == popped[i]){
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

再来回顾一下常见的几个排序算法和查找算法
冒泡排序

function bubbleSort(arr) {
    varlen = arr.length;
    for(vari = 0; i < len - 1; i++) {
        for(varj = 0; j < len - 1 - i; j++) {
            if(arr[j] > arr[j+1]) {        // 相邻元素两两对比
                vartemp = arr[j+1];        // 元素交换
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    returnarr;
}

选择排序

function selectionSort(arr) {
    varlen = arr.length;
    varminIndex, temp;
    for(vari = 0; i < len - 1; i++) {
        minIndex = i;
        for(varj = i + 1; j < len; j++) {
            if(arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    returnarr;
} 

插入排序

function insertionSort(arr) {
    varlen = arr.length;
    varpreIndex, current;
    for(vari = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while(preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex + 1] = current;
    }
    returnarr;
}

希尔排序

function shellSort(arr) {
    varlen = arr.length;
    for(vargap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
        for(vari = gap; i < len; i++) {
            varj = i;
            varcurrent = arr[i];
            while(j - gap >= 0 && current < arr[j - gap]) {
                 arr[j] = arr[j - gap];
                 j = j - gap;
            }
            arr[j] = current;
        }
    }
    returnarr;
}

快速排序

function quickSort(arr, left, right) {
    varlen = arr.length,
        partitionIndex,
        left = typeofleft != 'number'? 0 : left,
        right = typeofright != 'number'? len - 1 : right;
 
    if(left < right) {
        partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    returnarr;
}
 
function partition(arr, left ,right) {     // 分区操作
    varpivot = left,                      // 设定基准值(pivot)
        index = pivot + 1;
    for(vari = index; i <= right; i++) {
        if(arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }       
    }
    swap(arr, pivot, index - 1);
    returnindex-1;
}
 
function swap(arr, i, j) {
    vartemp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

排序可以参考这篇文章https://www.cnblogs.com/onepixel/p/7674659.html

顺序查找

//顺序查找
int SequenceSearch(int a[], int value, int n)
{
    int i;
    for(i=0; i<n; i++)
        if(a[i]==value)
            return i;
    return -1;
}

二分查找

//二分查找(折半查找),版本1
int BinarySearch1(int a[], int value, int n)
{
    int low, high, mid;
    low = 0;
    high = n-1;
    while(low<=high)
    {
        mid = (low+high)/2;
        if(a[mid]==value)
            return mid;
        if(a[mid]>value)
            high = mid-1;
        if(a[mid]<value)
            low = mid+1;
    }
    return -1;
}

//二分查找,递归版本
int BinarySearch2(int a[], int value, int low, int high)
{
    int mid = low+(high-low)/2;
    if(a[mid]==value)
        return mid;
    if(a[mid]>value)
        return BinarySearch2(a, value, low, mid-1);
    if(a[mid]<value)
        return BinarySearch2(a, value, mid+1, high);
}

更多查找方法https://www.cnblogs.com/maybe2030/p/4715035.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值