Skip to the content.

首页

数据结构


散列表

两种解决碰撞的方法:拉链法和线性探测法。


二叉查找树(BST)

一棵二叉查找树是一棵二叉树,且每个结点都大于其左子树的任一结点而小于其右子树的任一结点。

删除任意结点的流程:

  1. 为叶子结点直接删除;任一子结点为空,删除该结点,并将其父链接改为指向其非空的子结点。
  2. 若子结点均不为空,则找到该结点的后续结点,即其右子树的最小结点。
  3. 从右子树删除其后续结点,将后续结点的父链接改为指向后续结点的右子树(因为后续结点为树的最小结点,故其左结点必然为空)。
  4. 用后续结点替换掉该结点的位置。

平衡查找树

2-3查找树

一棵2-3查找树或为一棵空树或由以下结点组成:

2-3查找树的插入操作

红黑树

使用标准的二叉查找树(全部为2-结点)和一些额外信息表示一个2-3树,将一个3-结点替换为由一条左斜的红链接相连的两个2-结点,用黑链接表示结点之间的普通链接。

因为2-3树是完美平衡的(任意空链接到根结点的距离都相同),所以红黑树是完美黑色平衡的,任意空连接到根结点的路径上黑链接(黑色结点)数量都相同。

最坏情况下操作是2lgN级别(即左边路径全是3-结点),平均情况下所有操作均是lgN级别。

一、红黑树的插入操作

在沿着插入点到根路径向上移动的每个结点中完成以下操作即可实现插入:

  1. 如果左结点为黑色,右子结点为红色,进行左旋操作;

  2. 如果左子结点为红色且其左子结点也为红色,进行右旋操作;

  3. 如果左右子结点均为红色,进行颜色转换。

最后将根结点设为黑色。

二、自顶向下的2-3-4树的插入算法

2-3-4树允许存在4-结点,使用一条左斜的红链接和一条右斜的红链接以及三个2-结点来表示一个4-结点

插入算法要保证在向下查询路径上分解所有4-结点(通过颜色转换将一个4-结点变为3个2-结点),插入结点后向上回溯配平4-结点(通过旋转将不平衡的4-结点变成一条左斜和一条右斜组成的4-结点)。

相对于红黑树的插入算法,只需要将颜色转换的代码提前至空判断之后递归插入之前即可(即在插入结点前将4-结点配平,又因为可以存在4-结点所以插入的最后一步不再需要颜色转换)。

三、红黑树的删除最小值操作

删除最小值,沿着左链接向下过程中,保证不存在2-结点:

最后从3-结点或4-结点中删除最小值,然后向上回溯分解所有4-结点。

四、红黑树的删除操作

使用和删除最小值相同的变换保证向下查找路径中不存在2-结点,如果键在最底层直接删除即可,否则和二叉查找树一样,找到其后继结点,删去该结点并替换被删除结点即可。

五、Java红黑树实现

实现为2-3-4树,且每个结点均包含父结点的引用,同时允许单个右斜红链接存在。


无向图

非稠密无向图使用邻接表来表示,从图中任意一个顶点出发都存在一条路径到达另一个任意顶点,则此图是连通的。

有向图

一个顶点的出度为由该顶点指出的边的总数,一个顶点的入度为指向该顶点的边的总数。有向图中由顶点v能到达顶点w并不意味着由顶点w也能到达顶点v。

拓扑排序

给定一幅有向图,将所有顶点排序,使得所有的有向边均从排在前面的元素指向后面的元素。有向无环图才有拓扑排序,为所有顶点的逆后序排列

计算有向图强连通分量的Kosaraju算法

1. 求得有向图的反向图的逆后序排列;
2. 按照上一步得到的排列顺序在有向图中进行深度优先搜索。

最小生成树

图的生成树是它的一棵含有所有顶点的无环连通子图,而加权无向图的最小生成树是它的一棵权值之和最小的生成树。

贪心算法

根据切分定理,找到一种切分,它的横切边均不为黑色,则将它的权重最小的横切边标记为黑色,重复,直到标记了V-1条边为止。

切分定理

一幅加权图中,给定任意切分,该切分的横切边中权重最小者必然属于图的最小生成树。

Prim算法

每一步都为一棵生长中的树添加一条边,一开始这颗树只有一个顶点,每次都将下一条连接树中的顶点与不在树中的顶点且权重最小的边加入树中,直到树含有V-1条边。

Kruskal算法

按边的权重顺序,将边加入最小生成树中,加入的边不与已加入的边构成环,直到树含有V-1条边。

最短路径树

从一幅加权有向图其中一个顶点出发的最短路径图是该图的一幅子图,它包含了顶点到所有可达顶点的最短路径。

边的松弛

放松边v->w意味着检查从s到w的最短路径是否是先从s到v再从v到w。

Dijkstra算法

权重非负的情况下,依次将当前距离起点最近的非最短路径树顶点放松并加入树,直到所有顶点都在树中。

拓扑顺序算法

无环情况下,按照拓扑顺序放松顶点,能在线性时间(E+V)内解决问题,为最优解法且与权重是否非负无关。

Bellman-Ford算法

在任意含有V个顶点的加权有向图中给定起点s,从s出发无法到达任何负权重环,将distTo[s]初始化为0,其他初始化为无穷大,以任意顺序放松所有边V轮即可求最短路径树

可用于查找负权重环,经过多轮放松后,负权重环必然会存在edgeTo[]构成的子图中。

关键路径

创建一幅无环有向加权图,包含一个起点s和一个终点t且每个任务对应着两个顶点(一个起点一个终点)。每个任务都有一条从它的起点到它的终点的且权重为任务所需时间的边。每条优先级限制v->w,都有一条从v任务终点到w任务起点且权重为0的边。没有前置限制的任务需要添加一条从s到任务起点且权重为0的边,没有后置限制的任务需要添加一条任务终点到t且权重为0的边。这样每个任务的开始时间为从起点到任务起点的最长距离,任务的关键路径为从s到t的最长路径。


字符串

三向字符串快速排序

三向快速排序和高位优先排序的结合,适合于含有较长公共前缀的字符串。相比于直接使用三向快速排序能显著的减少字符比较次数,而相比于普通的高位优先排序能避免产生大量的空子数组。

private static int charAt(String s, int d){
    if (d < s.length()) {
        return s.charAt(d);
    } else {
        return -1;
    }
}

private static void sort(String[] arr, int lo, int hi, int d){
    if (hi <= lo){
        return;
    }
    int v = charAt(arr[lo], d);
    int lt = lo, i = lo + 1, gt = hi;
    while (i <= gt) {
        int cmp = charAt(a[i], d) - v;
        if (cmp < 0) {
            swap(arr, i++, lt++);
        } else if (cmp > 0) {
            swap(arr, i, gt--);
        } else {
            i++;
        }
    }
    sort(arr, lo, lt - 1, d);
    // 等于区间还需要继续比较下一个位置的字符
    if (v > 0) {
        sort(arr, lt, gt, d + 1)
    }
    sort(arr, gt + 1, hi, d);
}

单词查找树

R向单词查找树

R向单词查找树,R为字符表的大小,每个结点都有R条链接,其中大量的链接可能都为空,每条链接对应一个字符。

三向单词查找树

三向单词查找树中,每个结点都含有一个字符、三条链接和一个值,三条链接分别对应当前字母小于、等于和大于结点字母的所有键。