一、树

1.1 树的定义

  • 树是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
  • 树结构是一种非线性存储结构,存储的是具有“一对多”关系的数据元素的集合。

1.2 树的特点

  • 每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树;
  • 树是一种特殊的图

1.3 树与图的区别

image
image

  • 树是没有环的图(在图里面,环的路线是开始和结束都是一样的点)一个子节点只有一个父节点,所以树不是一个递归的数据结构。
Trees Graphs
1. A tree is a special kind of graph that there are never multiple paths exist. There is always one way to get from A to B. 1. A graph is a system that has multiple ways to get from any point A to any other point B.
2. Tree must be connected. 2. Graph may not be connected.
3. Since it connected we can reach from one particular node to all other nodes. This kind of searching is called traversal. 3. Traversal always not applicable on graphs. Because graphs may not be connected.
4. Tree contains no loops, no circuits. 4. Graph may contain self-loops, loops.
5. There must be a root node in tree. 5. There is no such kind of root node in graphs
6. We do traversal on trees. That mean from a point we go to each and every node of tree. 6. We do searching on graphs. That means starting from any node try to find a particular node which we need.
7. pre-order, in-order, post-order are some kind of traversals in trees. 7. Breath first search, Depth first search, are some kind of searching algorithms in graphs.
8. Trees are directed acyclic graphs. 8. Graphs are cyclic or acyclic.
9. Tree is a hierarchical model structure. 9. Graph is network model.
10. All trees are graphs. 10. But all graphs are not trees.
11. Based on different properties trees can be classified as Binary tree, Binary search tree, AVL trees, Heaps. 11. We differ the graphs like directed graphs and undirected graphs.
12. If tree have “n” vertices then it must have exactly “n-1” edges only. 12. In graphs number of edges doesn’t depend on the number of vertices.
13. Main use of trees is for sorting and traversing. 13. Main use of graphs is coloring and job scheduling.
14. Less in complexity compared to graphs. 14. High complexity than trees due to loops.

二、二叉树

2.1 什么是二叉树

2.1.1 定义

  • 树的任意节点至多包含两棵子树。
  • 是n(n>=0)个结点的有限集合,它或者是空树(n=0),或者是由一个根结点及两颗互不相交的、分别称为左子树和右子树的二叉树所组成

2.1.2 特点

  • 每个结点最多有两颗子树,所以二叉树中不存在度(拥有的子树数目称为结点的度)大于2的结点
  • 左子树和右子树是有顺序的,次序不能任意颠倒
  • 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树

2.2 二叉树的分类

2.2.1 满二叉树

image

  • 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树

  • 满二叉树特点:

    • 叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
    • 非叶子结点的度(结点拥有的子树数目称为结点的度)一定是2
    • 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多

2.2.2 完全二叉树

image

  • 除最后一层外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点

  • 完全二叉树特点:

    • 叶子结点只能出现在最下层和次下层。
    • 最下层的叶子结点集中在树的左部。
    • 倒数第二层若存在叶子结点,一定在右部连续位置。
    • 如果结点度为1,则该结点只有左孩子,即没有右子树。
    • 同样结点数目的二叉树,完全二叉树深度最小
  • 堆的实现一般基于完全二叉树

2.2.3 二叉搜索树

image

  • 可以为空树,或者是具备如下性质:若它的左子树不空,则左子树上的所有结点的值均小于根节点的值;若它的右子树不空,则右子树上的所有结点的值均大于根节点的值,左右子树分别为二叉排序树。
  • 二叉查找树是一颗二叉树,它每个结点的值都大于其左子树的任意结点而小于右子树的任意结点,它结合了链表插入的灵活性有序数组查找的高效性(二分查找)
  • 对于使用二叉查找树的算法,它的运行时间取决于树的形状,而树的形状又取决于结点插入的先后顺序。如上图所示,最好情况下,N 个结点的树是完全平衡的,每条空链接到根结点的距离都为 ~lgN;而在最坏的情况下,搜索路径上可能有 N 个结点,退化成了链表。
  • 所以,为了保证运行时间始终在对数级别,在动态构建二叉查找树时,希望保持其平衡性,也就是降低树的高度,使其尽可能为 ~lgN,这样就能保证所有的查找都能在 ~lgN 次比较内结束,就像二分查找那样,这样的树被称为平衡二叉查找树

2.2.4 平衡二叉查找树(AVL树)

什么是平衡二叉查找树

  • 由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:

  • 可以是空树。

    • 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过 1。
  • 平衡之意,如天平,即两边的分量大约相同。

  • 最早的自平衡二叉搜索树结构

第一个自平衡二叉查找树就是AVL 树,它规定,每个结点的左右子树的高度之差不超过 1。在插入或删除结点,打破平衡后,就会通过一次或多次树旋转来重新平衡。

为什么有平衡二叉查找树

  • 二叉搜索树已经在一定程度上提高了搜索效率,但是由于二叉搜索树自身的特性,会存在一种极端情况:

平衡二叉查找树的缺点

AVL 树是严格平衡的,适用于查找密集型应用程序,因为在频繁插入或删除结点的场景下,它花费在树旋转的代价太高。

红黑树就是一种折中方案,它不追求完美平衡,只求部分达到平衡,从而降低在调整时树旋转次数。
image

2.3 二叉树的性质

  1. 在二叉树的第i层上最多有2 i-1 个节点 。(i>=1)

  2. 二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)

  3. n0=n2+1 n0表示度数为0的节点 n2表示度数为2的节点

  4. 在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]+1是向下取整。

  5. 若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:

    (1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;

    (2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;

    (3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点

三、红黑树

3.1 什么是红黑树

红黑树的英文是“Red-Black Tree",简称R-B Tree。

红黑树是一种二叉查找树,通过设置一些规则来保证二叉搜索树的平衡性,使二叉搜索树不会在极端情况下变成链表。

红黑树也是一种平衡二叉查找树的变体,它的左右子树高差有可能大于1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对之进行平衡的代价较低, 其平均统计性能要强于 AVL。

3.2 红黑树的特性

  • 每个节点或者是黑色,或者是红色
  • 根节点是黑色
  • 每个叶结点(最后的空节点)是黑色
  • 如果一个节点是红色的,则它的子节点必须是黑色的,红色节点的孩子和父亲都不能是红色
  • 从每个叶子到根的所有路径上不能有两个连续的红色节点,任意一结点到每个叶子结点的路径都包含数量相同的黑结点。确保没有一条路径会比其他路径长出俩倍。
  • 因而,红黑树是相对接近平衡的二叉树,并不是一个完美平衡二叉查找树

image

为了更好地理解什么是红黑树以及这种看起来特殊的数据结构是如何被提出的,我们首先需要了解一下2-3树,这种数据结构与红黑树是等价的。

说到红黑树,就不得不提 2-3 树,因为,红黑树可以说就是它的一种特殊实现,对它有所了解,非常有助于理解红黑树。

3.3 左倾红黑树与2-3树

3.3.1 什么是2-3树

  • 保持平衡,无非是为了降低树的高度,如果把二叉查找树一般化,允许一个结点保存多个值,变成多叉树,也可认为是降低了高度。
  • 结点可以存放一个元素或者两个元素。相应地,一个结点可能有两个子树或者三个子树。这种结点可以被称为2结点或者3结点。
  • 满足二分搜索树的基本性质,但是它不是一种二叉树。
  • 2-3树是一颗绝对平衡的树:从根节点到任意一个叶子结点所经过的结点数量一定是相同的。

image

3.3.2 2-3树的绝对平衡性

  • 插入新元素时,并不会创建一个新结点,而是和叶子结点做融合
  • 结点的向下分裂和向上融合来保证绝对平衡性
  • 插入元素的过程:

image

image

image

img

3.3.3 2-3树与红黑树的等价性

  • 任意的一颗红黑树能够转换为一颗2-3树
  • 红黑树的根节点一定是黑色的:因为不管2-3树的根节点是2结点还是3结点,红黑树的根节点一定是黑色结点。
  • 每个叶子结点(最后的空节点)一定是黑色的:与上一个性质是吻合的。空树:空节点既是根节点也是叶子结点。
  • 如果一个结点是红色的,它的两个子节点一定是黑色的。
  • 核心性质:从任意一个结点出发,到叶子结点经过的黑色结点数目一定是相同的:红黑树中转换为2-3树后,不管转换为2-3树的2结点还是3结点,一定会有一个结点时黑色的。红黑树中每经过一个黑色的结点,意味着对应着经过了原来的2-3树中的一个结点。
  • 红黑树是保持“黑平衡”的二叉树。严格意义上来讲不是平衡二叉树。最大高度:2logn,时间复杂度O(logn)

image

image

image

3.4 左倾红黑树添加新元素

与2-3树添加元素是等价的

2-3 树插入的都是叶子结点红黑树插入的结点都是红色的,因为在 2-3 树中,待插入结点都认为可以插入到一个多值结点中。

3.4.1 树旋转

在分析插入和删除之前,先了解下什么是树旋转。树旋转是二叉树中调整子树的一种操作,常用于调整树的局部平衡性,它包含两种方式,左旋转右旋转

image

其实旋转操作很容易理解:左旋转就是将用两个结点中的较小者作为根结点变为将较大者作为根结点,右旋转刚好于此相反,如上图所示:

  • 右旋转,就是将较小者 L 作为根结点,然后调整 L 和 P 的子树
  • 左旋转,就是将较大者 P 作为根结点,然后调整 P 和 L 的子树

红黑树的旋转其实就是为了确保和其结构相同的 2-3树的一一对应关系,同时保证红黑树的有序性和平衡性。

3.4.2 保持根节点为黑色和左旋转

增加一个元素右倾:进行一次左旋转

image
image

3.4.3 颜色翻转和右旋转

颜色翻转:

image

image

image

右旋转:

image

image

3.4.4 添加新元素完整过程

image

image

image

image

image

3.4.5 维护红黑树平衡的时机

  • 左旋转的时机:当前node的右节点为红色,且当前结点的左节点不为红色
  • 右旋转的时机:当前node的左节点为红色,且左节点的左节点也是红色
  • 颜色翻转的时机:当前node的左右结点都是红色

3.5 左倾红黑树的实现

左倾红黑树(Left-leaning Red-Black Tree)是红黑树的一种变种,它的主要思想是将红色节点向左倾斜,从而简化插入和删除操作的实现。在左倾红黑树中,所有节点都被标记为红色或黑色,而且满足以下性质:

  1. 根节点是黑色的。
  2. 所有叶子节点都是黑色的。
  3. 如果一个节点是红色的,则它的两个子节点都是黑色的。
  4. 任意一条从根节点到叶子节点的路径上,不能出现连续的两个红色节点。
  5. 从任意一个节点出发到其每个叶子节点的路径中,黑色节点的个数相同。

下面是左倾红黑树的Java代码实现:

public class LeftLeaningRedBlackTree<Key extends Comparable<Key>, Value> {
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private Node root;

    private class Node {
        private Key key;
        private Value val;
        private Node left, right;
        private boolean color;

        public Node(Key key, Value val, boolean color) {
            this.key = key;
            this.val = val;
            this.color = color;
        }
    }

    private boolean isRed(Node x) {
        if (x == null) return false;
        return x.color == RED;
    }
	// 坐旋转,坐旋转的同时变色
    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = h.color;
        h.color = RED;
        return x;
    }
	// 右旋转,右旋转的同时变色
    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        return x;
    }

    private void flipColors(Node h) {
        h.color = RED;
        h.left.color = BLACK;
        h.right.color = BLACK;
    }

    public void put(Key key, Value val) {
        root = put(root, key, val);
        root.color = BLACK;
    }

    private Node put(Node h, Key key, Value val) {
        if (h == null) return new Node(key, val, RED);

        int cmp = key.compareTo(h.key);
        if (cmp < 0) h.left = put(h.left, key, val);
        else if (cmp > 0) h.right = put(h.right, key, val);
        else h.val = val;

        // 左旋
        if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);
        // 右旋
        if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
        // 颜色翻转
        if (isRed(h.left) && isRed(h.right)) flipColors(h);

        return h;
    }
}

在这个实现中,我们使用了三种基本操作来维护左倾红黑树的性质:

  1. 左旋(rotateLeft):将一个节点的右子节点向左旋转,使得它成为新的根节点
  2. 右旋(rotateRight):将一个节点的左子节点向右旋转,使得它成为新的根节点
  3. 颜色翻转(flipColors):将一个节点的颜色和它的两个子节点的颜色互换,从而保证每个红色节点都有一个黑色节点作为父节点

在插入节点时,我们首先按照二叉搜索树的方式找到要插入的位置,并将节点标记为红色。然后,我们检查当前节点的父节点、父节点的兄弟节点和祖父节点的颜色,根据需要进行旋转和颜色翻转,以保证左倾红黑树的性质不被破坏。

在这个实现中,我们将所有空节点都视为黑色节点,并将根节点标记为黑色。这些约定使得插入操作更加简单,同时保证了左倾红黑树的性质。

总的来说,左倾红黑树是一种比较优秀的数据结构,它能够在保持红黑树性质的前提下,简化插入和删除操作的实现,同时也能够保持良好的平衡性能。

3.6 红黑树和AVL树的区别

RB-Tree和AVL树作为BBST,其实现的算法时间复杂度相同,AVL作为最先提出的BBST,貌似RB-tree实现的功能都可以用AVL树是代替,那么为什么还需要引入RB-Tree呢?

  • 红黑树不追求"完全平衡",即不像AVL那样要求节点的 |balFact| <= 1,它只要求部分达到平衡,但是提出了为节点增加颜色,红黑是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。
  • 就插入节点导致树失衡的情况,AVL和RB-Tree都是最多两次树旋转来实现复衡rebalance,旋转的量级是O(1)。删除节点导致失衡,AVL需要维护从被删除节点到根节点root这条路径上所有节点的平衡,旋转的量级为O(logN),而RB-Tree最多只需要旋转3次实现复衡,只需O(1),所以说RB-Tree删除节点的rebalance的效率更高,开销更小!
  • AVL的结构相较于RB-Tree更为平衡,插入和删除引起失衡,如2所述,RB-Tree复衡效率更高;当然,由于AVL高度平衡,因此AVL的Search效率更高啦。
  • 针对插入和删除节点导致失衡后的rebalance操作,红黑树能够提供一个比较"便宜"的解决方案,降低开销,是对search,insert ,以及delete效率的折衷,总体来说,RB-Tree的统计性能高于AVL.
  • 故引入RB-Tree是功能、性能、空间开销的折中结果

AVL更平衡,结构上更加直观,时间效能针对读取而言更高;维护稍慢,空间开销较大。

红黑树,读取略逊于AVL,维护强于AVL,空间开销与AVL类似,内容极多时略优于AVL,维护优于AVL。

基本上主要的几种平衡树看来,红黑树有着良好的稳定性和完整的功能,性能表现也很不错,综合实力强,在诸如STL的场景中需要稳定表现。

红黑树的查询性能略微逊色于AVL树,因为其比AVL树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的AVL树最多多一次比较,但是,红黑树在插入和删除上优于AVL树,AVL树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于AVL树为了维持平衡的开销要小得多

  • 总结:实际应用中,若搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。

3.7 红黑树的性能

  1. 对于完全随机的数据,普通的二叉搜索树就很好用。
  2. 对于查询较多的情况,AVL树很好用。
  3. 红黑树牺牲了平衡性(2logn的高度),但是红黑树的统计性能更优(综合增删改查所有操作)。

3.8 红黑树的应用

3.8.1 红黑树在hashmap中的应用

  • 在jdk1.8版本后,Java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。
  • 红黑树是”近似平衡“的。相比avl树,在检索的时候效率其实差不多,都是通过平衡来二分查找。但对于插入删除等操作效率提高很多。红黑树不像avl树一样追求绝对的平衡,他允许局部很少的不完全平衡,这样对于效率影响不大,但省去了很多没有必要的调平衡操作,avl树调平衡有时候代价较大,所以效率不如红黑树,在现在很多地方都是底层都是红黑树。
  • 红黑树的高度只比高度平衡的AVL树的高度(log2n)仅仅大了一倍,在性能上却好很多。
  • HashMap在里面就是链表加上红黑树的一种结构,这样利用了链表对内存的使用率以及红黑树的高效检索,是一种很happy的数据结构。
  • AVL树是一种高度平衡的二叉树,所以查找的非常高,但是,有利就有弊,AVL树为了维持这种高度的平衡,就要付出更多代价。每次插入、删除都要做调整,就比较复杂、耗时。所以,对于有频繁的插入、删除操作的数据集合,使用AVL树的代价就有点高了。
  • 红黑树只是做到了近似平衡,并不严格的平衡,所以在维护的成本上,要比AVL树要低。
  • 所以,红黑树的插入、删除、查找各种操作性能都比较稳定。对于工程应用来说,要面对各种异常情况,为了支撑这种工业级的应用,我们更倾向于这种性能稳定的平衡二叉查找树。
  • java8不是用红黑树来管理hashmap,而是在hash值相同的情况下(且重复数量大于8),用红黑树来管理数据。 红黑树相当于排序数据,可以自动的使用二分法进行定位,性能较高。一般情况下,hash值做的比较好的话基本上用不到红黑树。
  • 红黑树牺牲了一些查找性能 但其本身并不是完全平衡的二叉树。因此插入删除操作效率略高于AVL树。
  • AVL树用于自平衡的计算牺牲了插入删除性能,但是因为最多只有一层的高度差,查询效率会高一些。

3.8.2 红黑树在TreeMap和TreeSet的应用

红黑树是一种自平衡的二叉搜索树,它可以在保持二叉搜索树的性质下,保持树的高度平衡,从而保证了树操作的时间复杂度在最坏情况下也是O(log n)。由于其优秀的性能表现,红黑树被广泛应用于Java的TreeMap和TreeSet中。

TreeMap和TreeSet都是基于红黑树实现的数据结构,它们分别用于实现基于键的有序映射和基于元素的有序集合。它们都支持一系列的操作,如插入、删除、查找最大值、查找最小值、查找某个键或元素的前驱或后继等操作。

在TreeMap中,每个键值对被表示为一个节点,节点按照键的大小进行排序,并存储在一棵红黑树中。在TreeSet中,每个元素被表示为一个节点,节点按照元素的大小进行排序,并存储在一棵红黑树中。

由于红黑树的自平衡性质,TreeMap和TreeSet能够在插入、删除、查找等操作中保持较为稳定的性能表现,具有很高的效率和可靠性。在Java中,TreeMap和TreeSet被广泛应用于需要高效实现有序映射和有序集合的场景,如排序、搜索、去重等场景,是非常常用的数据结构之一。

3.9 红黑树的其他实现

算法导论中的红黑树的实现

package rbTree;

import java.util.HashMap;

/**
 * @Author WaleGarrett
 * @Date 2021/7/11 18:48
 */
public class RBTree<Key extends Comparable<Key>, Value> {
    private class Node<Key, value>{
        private Key key;
        private Value value;
        private Node<Key, Value> left;
        private Node<Key, Value> right;
        private int color;
        public Node(Key key, Value value, int color){
            this.key = key;
            this.value = value;
            this.color = color;
        }
    }
    private static final int RED = 0;
    private static final int BLACK = 1;

    /**
     * 判断一个结点的颜色是否是红色
     * @param node
     * @return
     */
    private boolean isRed(Node<Key, Value> node){
        if(node == null)
            return false;
        return node.color == RED;
    }

    /**                 node                 x
     * 左旋转:         /   \               /  \
     * @param node    T1    x    ---->   node  T3
     * @return             / \           /  \
     *                    T2 T3         T1  T2
     */
    private Node<Key, Value> leftRotate(Node<Key, Value> node){
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }
    /**                  node                 x
     * 右旋转:         /   \                /  \
     * @param node    x     T2    ---->    T3  node
     * @return       / \                       /  \
     *              T3 T1                    T1   T2
     */
    private Node<Key, Value> rightRotate(Node<Key, Value> node){
        Node x = node.left;
        node.left = x.right;
        x.right = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }

    /**
     * 颜色翻转:当左右指针均为红色时进行颜色翻转
     * @param node
     */
    private void flipColor(Node<Key, Value> node){
        node.color = RED;
        node.left.color = BLACK;
        node.right.color = BLACK;
    }

    /**
     * 根节点
     */
    private Node<Key, Value> root;

    /**
     * 添加元素
     * @param key
     * @param value
     */
    public void add(Key key, Value value){
        root = add(root, key, value);
        // 保证根节点的颜色始终是黑色
        root.color = BLACK;
    }

    public Node add(Node<Key, Value> node, Key key, Value value){
        /**
         * 新加入的结点始终保持是红色的
         */
        if(node == null)
            return new Node<Key, Value>(key, value, RED);
        int compare = key.compareTo(node.key);
        if(compare < 0){
            // key小于当前node结点,新结点必然插入到node的左子树
            node.left = add(node.left, key, value);
        }else if(compare > 0){
            // key大于当前node结点,新结点必然插入到node的右子树
            node.right = add(node.right, key, value);
        }else{
            node.value = value;
        }
        /**
         * 使红黑树树保持平衡
         */
        // 当node结点的右结点为红色,而左结点为黑色时,左旋
        if(isRed(node.right) && !isRed(node.left))
            node = leftRotate(node);
        // 当node左侧有两个连续的红色结点时,右旋
        if(isRed(node.right) && isRed(node.right.right))
            node = rightRotate(node);
        // 当node的左右结点均为红色时,颜色翻转
        if(isRed(node.left) && isRed(node.right))
            flipColor(node);
        return node;
    }
}

参考

https://www.cnblogs.com/chuonye/p/11236136.html

posted @ 2023-03-22 11:18 Garrett_Wale 阅读(924) 评论(1) 推荐(5) 编辑
摘要: 引言 在学习软件工程的时候接触过一些软件工程开发模型的相关概念,其中,印象比较深刻的就是瀑布模型和敏捷开发模型。这两种模型在日常的软件开发中都是非常常用的,但是它们也有比较大的区别,所以在实际的应用场景也不同。 瀑布模型式是最典型的预见性的方法,严格遵循预先计划的需求、分析、设计、编码、测试的步骤顺 阅读全文
posted @ 2022-05-11 10:24 Garrett_Wale 阅读(7685) 评论(0) 推荐(0) 编辑
摘要: 引言 我们在定时任务中经常能接触到cron表达式,但是在写cron表达式的时候我们会遇到各种各样版本的cron表达式,比如我遇到过5位、6位甚至7位的cron表达式,导致我一度搞混这些表达式。更严重的是,当我们没有准确写出cron表达式时,会出现定时任务一直没有执行,或者定时任务执行太频繁的糟糕情况 阅读全文
posted @ 2022-05-11 09:44 Garrett_Wale 阅读(10364) 评论(2) 推荐(3) 编辑
摘要: 引言 Hyperledger Fabric是当前比较流行的一种联盟链系统,它隶属于Linux基金会在2015年创建的超级账本项目且是这个项目最重要的一个子项目。目前,与Hyperledger的另外几个子项目Hyperledger Iroha,Hyperledger Indy和Hyperledger 阅读全文
posted @ 2022-04-11 20:22 Garrett_Wale 阅读(2187) 评论(2) 推荐(2) 编辑
摘要: Java中的注解及其实现原理 什么是注解? 对于很多初次接触的开发者来说应该都有这个疑问?Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方 阅读全文
posted @ 2021-11-27 10:22 Garrett_Wale 阅读(6217) 评论(0) 推荐(1) 编辑
摘要: 一: 关于能量守恒 出射光线的能量永远不能超过入射光线的能量(发光面除外)。如图示我们可以看到,随着粗糙度的上升镜面反射区域的会增加,但是镜面反射的亮度却会下降。如果不管反射轮廓的大小而让每个像素的镜面反射强度(Specular Intensity)都一样的话,那么粗糙的平面就会放射出过多的能量,而 阅读全文
posted @ 2019-08-15 11:01 Garrett_Wale 阅读(1441) 评论(0) 推荐(0) 编辑
摘要: 题目来源 45. 跳跃游戏 II 题目详情 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处: 0 <= j <= nu 阅读全文
posted @ 2023-02-19 22:53 Garrett_Wale 阅读(419) 评论(0) 推荐(0) 编辑
摘要: 题目来源 343. 整数拆分 题目详情 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n = 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1。 示例 2: 输入: n = 阅读全文
posted @ 2023-01-25 20:36 Garrett_Wale 阅读(184) 评论(0) 推荐(0) 编辑
摘要: 题目来源 670. 最大交换 题目详情 给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能得到的最大值。 示例 1 : 输入: 2736 输出: 7236 解释: 交换数字2和数字7。 示例 2 : 输入: 9973 输出: 9973 解释: 不需要交换。 注意: 给定数字的范围是 [0 阅读全文
posted @ 2023-01-24 21:02 Garrett_Wale 阅读(223) 评论(0) 推荐(0) 编辑
摘要: 题目来源 面试题 02.05. 链表求和 题目详情 给定两个用链表表示的整数,每个节点包含一个数位。 这些数位是反向存放的,也就是个位排在链表首部。 编写函数对这两个整数求和,并用链表形式返回结果。 示例: 输入: (7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295 输 阅读全文
posted @ 2023-01-21 10:34 Garrett_Wale 阅读(63) 评论(0) 推荐(0) 编辑
摘要: 题目来源 887. 鸡蛋掉落 题目详情 给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。 已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。 每次操作,你可以取一枚没有碎的鸡 阅读全文
posted @ 2023-01-08 13:05 Garrett_Wale 阅读(481) 评论(0) 推荐(0) 编辑
摘要: 题目来源 400. 第 N 位数字 题目详情 给你一个整数 n ,请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...] 中找出并返回第 n 位上的数字。 示例 1: 输入: n = 3 输出: 3 示例 2: 输入: n = 11 输出: 0 解释: 阅读全文
posted @ 2022-12-30 22:47 Garrett_Wale 阅读(149) 评论(0) 推荐(0) 编辑
摘要: 题目来源 224. 基本计算器 题目详情 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。 注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。 示例 1: 输入: s = "1 + 1" 输出: 2 示例 2: 输入: s = " 2-1 + 2 阅读全文
posted @ 2022-12-28 23:17 Garrett_Wale 阅读(228) 评论(0) 推荐(0) 编辑
摘要: 引言 在日常工作和生活中,我们经常使用word来撰写文档、论文。为了更好地管理文档中的图片以及在正文中引用图片标题,需要借助题注来实现。通过题注,可以在正文中交叉引用图片,并为引用生成超链接。 尽管题注的功能很强大,但是在使用的过程中还是会遇到一些问题,特别是当在文档正文中间插入一张新图片并为其生成 阅读全文
posted @ 2022-08-02 20:01 Garrett_Wale 阅读(7659) 评论(0) 推荐(1) 编辑
摘要: 引言 在学习mysql时,我们经常会使用explain来查看sql查询的索引等优化手段的使用情况。在使用explain时,我们可以观察到,explain的输出有一个很关键的列,它就是type属性,type表示的是扫描方式,代表 MySQL 使用了哪种索引类型,不同的索引类型的查询效率是不一样的。 在 阅读全文
posted @ 2022-05-14 21:38 Garrett_Wale 阅读(4324) 评论(1) 推荐(3) 编辑
摘要: 题目来源 556. 下一个更大元素 III 题目详情 给你一个正整数 n ,请你找出符合条件的最小整数,其由重新排列 n中存在的每位数字组成,并且其值大于 n 。如果不存在这样的正整数,则返回 -1 。 注意 ,返回的整数应当是一个 32 位整数 ,如果存在满足题意的答案,但不是 32 位整数 ,同 阅读全文
posted @ 2022-04-07 22:12 Garrett_Wale 阅读(54) 评论(0) 推荐(0) 编辑
摘要: 题目来源 496. 下一个更大元素 I 题目详情 nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x大的元素。 给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。 对于每 阅读全文
posted @ 2022-04-07 21:48 Garrett_Wale 阅读(48) 评论(0) 推荐(0) 编辑
摘要: 题目来源 47. 全排列 II 题目详情 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 示例 1: 输入: nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]] 示例 2: 输入: nums = [1,2,3] 输出: [[1 阅读全文
posted @ 2022-04-07 21:09 Garrett_Wale 阅读(126) 评论(0) 推荐(0) 编辑
摘要: 题目来源 329. 矩阵中的最长递增路径 题目详情 给定一个 m x n 整数矩阵 matrix ,找出其中 最长递增路径 的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你 不能 在 对角线 方向上移动或移动到 边界外(即不允许环绕)。 示例 1: 输入: matrix = [[9 阅读全文
posted @ 2022-04-06 22:45 Garrett_Wale 阅读(194) 评论(0) 推荐(0) 编辑
摘要: 题目来源 1047. 删除字符串中的所有相邻重复项 题目详情 给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。 在 S 上反复执行重复项删除操作,直到无法继续删除。 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。 示例: 输入: "abbaca" 输 阅读全文
posted @ 2022-04-06 16:53 Garrett_Wale 阅读(131) 评论(0) 推荐(0) 编辑
摘要: 题目来源 补充题2. 圆环回原点问题 题目详情 圆环上有10个点,编号为0~9。从0点出发,每次可以逆时针和顺时针走一步,问走n步回到0点共有多少种走法。 输入: 2 输出: 2 解释:有2种方案。分别是0->1->0和0->9->0 题解分析 解法一:动态规划 本题考察的是动态规划。 如果你之前做 阅读全文
posted @ 2022-04-06 16:35 Garrett_Wale 阅读(1120) 评论(0) 推荐(0) 编辑
摘要: 题目来源 349. 两个数组的交集 题目详情 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2] 示例 2: 输 阅读全文
posted @ 2022-04-05 20:45 Garrett_Wale 阅读(103) 评论(1) 推荐(0) 编辑
点击右上角即可分享
微信分享提示