Day_37十字链表

目录

一. 关于十字链表

二. 如何实现十字链表

三. 十字链表的代码实现

        1. 节点类

        2. 出边链接

        3. 入边链接

        4. 输出函数

四. 代码展示

五. 数据测试

六. 总结与反思


一. 关于十字链表

        回顾邻接表,我们不难发现邻接表其实是一种出邻接表,意思是我构造节点数(和邻接矩阵维数一样),每个节点指向这个节点的一个邻接节点,而第一个邻接节点指向这个节点的下一个邻接节点,如此往复,直到这个首节点的所有邻接节点都被指向完成。可以发现这是关于首节点的“指出去”的方法,那么我们现在的问题是我知道其中一个首节点,请问有哪些节点“指进来”这个节点呢?或者说哪些节点指向这个首节点呢?

        倘若我们以邻接表的方式来寻找指向这个首节点的节点,那么只有遍历整个邻接表才可以知道答案(搜索到某个节点,而这个节点正好是这个首节点,则记录这个节点的头节点),这样我们才能得到结果。可是这样的时间复杂度很高,而且实现起来非常麻烦。所有我们在想有一种方案既可以指向首节点的出度,又可以指向首节点的入度,于是十字链表诞生了。

二. 如何实现十字链表

        十字链表其实是出度邻接表+入度邻接表的结合体。首先我们构造首节点(首节点的数量和邻接矩阵的大小一样),但是由于既有出度的指针又有入度的指针,在这里我们需要对首节点“改造”一下,如图G1所示,这和邻接表的区别在于多了一个入度指针。对于其中的节点也需要多一个指针域详见图G2,当然如果节点有权值也可以在其中再加一个权值域。

图G1
图G2

        以闵老师程序里面的矩阵M为例,它对应图如图G3所示

矩阵M
图G3

         现在我们开始编写这个图对应的十字链表,首先构造四个首节点,接着先写这个图的出度,0号节点指向1号节点;接着看1号节点指向3号节点;2号节点指向0号节点;3号节点指向2号节点;这样我们完成了所有的节点的出度。入度也是相同的道理,指向0号节点的有2号节点;指向1号节点的有0号,3号节点;指向2号节点的有3号节点;指向3号节点的有1号节点。如此所有节点的出度和入度都完成。按照十字链表的规则画出图像如图G4所示。

图G4

三. 十字链表的代码实现

        1. 节点类

        节点类首先有两个int型变量Row,Column用于记录节点的行和列的位置信息,接着有两个OrthogonalNode类的指针域nextIn,nextOut分别指向这个首节点的入度和出度。最后节点的构造函数:传入行paraRow,列paraColumn构造一个出度为NULL入度为NULL的节点。这里的构造和邻接表节点的构造很相似,不过是多了一个nextIn指针域,其他的性质几乎没有改变。

        /**
         * An inner class for adjacent node.
         */
        class OrthogonalNode {
            /**
             * The row index.
             */
            int row;

            /**
             * The column index.
             */
            int column;

            /**
             * The next out node.
             */
            OrthogonalNode nextOut;

            /**
             * The next in node.
             */
            OrthogonalNode nextIn;

            /**
             * ********************
             * The first constructor.
             *
             * @param paraRow    The row.
             * @param paraColumn The column.
             *                   ********************
             */
            public OrthogonalNode(int paraRow, int paraColumn) {
                row = paraRow;
                column = paraColumn;
                nextOut = null;
                nextIn = null;
            }// Of OrthogonalNode
        }// Of class OrthogonalNode

        2. 出边链接

        构造头结点i,根据邻接矩阵判断第i行第j列的值是否为0,若不为0,则这个节点是头结点i的出度,将头结点的nextOut链接到节点j;接着判断邻接矩阵第i行第k列是否为0;若不为0,则这个节点也是节点i的出度,将节点k链接到节点j的nextOut域;周而复始直到这一行所有节点都被遍历一次,即完成节点i的出边链接,只要根据邻接矩阵大小将上述过程完成几次,则我们完成十字链表的出边链接。

            numNodes = paraMatrix.length;

            // Step 1. Initialize. The data in the headers are not meaningful.
            OrthogonalNode tempPreviousNode, tempNode;

            headers = new OrthogonalNode[numNodes];

            // Step 2. Link to its out nodes.
            for (int i = 0; i < numNodes; i++) {
                headers[i] = new OrthogonalNode(i, -1);
                tempPreviousNode = headers[i];
                for (int j = 0; j < numNodes; j++) {
                    if (paraMatrix[i][j] == 0) {
                        continue;
                    } // Of if

                    // Create a new node.
                    tempNode = new OrthogonalNode(i, j);

                    // Link.
                    tempPreviousNode.nextOut = tempNode;
                    tempPreviousNode = tempNode;
                } // Of for j
            } // Of for i

        3. 入边链接

        这里我们引入一系列临时节点tempColumnNodes用于记录入边到哪一个节点;从0号节点出发,遍历0号节点的出边节点,当第j出边节点不为NULL时,第j个出边节点的列Column是i,则tempColumnNodes[i]的入边节点指向这个节点j,最关键的来了,接下来我们更新这个临时节点tempColumnNodes[i]的值为现在0号节点的第j出边节点(更新之后相当于现在节点i的入度域变成了第j号节点),一直循环直到0号节点的所有出边节点完成。接着遍历1,2,3号节点,按照上述相同的方法。

            // Step 3. Link to its in nodes. This step is harder.
            OrthogonalNode[] tempColumnNodes = new OrthogonalNode[numNodes];
            for (int i = 0; i < numNodes; i++) {
                tempColumnNodes[i] = headers[i];
            } // Of for i

            for (int i = 0; i < numNodes; i++) {
                tempNode = headers[i].nextOut;
                while (tempNode != null) {
                    tempColumnNodes[tempNode.column].nextIn = tempNode;
                    tempColumnNodes[tempNode.column] = tempNode;

                    tempNode = tempNode.nextOut;
                } // Of while
            } // Of for i
        }// Of the constructor

        4. 输出函数

        输出函数没什么特别的地方,无非就是遍历每一个首节点,第一遍输出出边节点,第二遍输出如边节点。

        /**
         * ********************
         * Overrides the method claimed in Object, the superclass of any class.
         * ********************
         */
        public String toString() {
            String resultString = "Out arcs: ";

            OrthogonalNode tempNode;
            for (int i = 0; i < numNodes; i++) {
                tempNode = headers[i].nextOut;

                while (tempNode != null) {
                    resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
                    tempNode = tempNode.nextOut;
                } // Of while
                resultString += "\r\n";
            } // Of for i

            resultString += "\r\nIn arcs: ";

            for (int i = 0; i < numNodes; i++) {
                tempNode = headers[i].nextIn;

                while (tempNode != null) {
                    resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
                    tempNode = tempNode.nextIn;
                } // Of while
                resultString += "\r\n";
            } // Of for i

四. 代码展示

        主函数类:

package Day_37;

public class demo1 {
    /**
     *********************
     * The entrance of the program.
     *
     * @param args Not used now.
     *********************
     */
    public static void main(String args[]) {
        int[][] tempMatrix = { { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 0 }, { 0, 1, 1, 0 } };
        OrthogonalList tempList = new OrthogonalList(tempMatrix);
        System.out.println("The data are:\r\n" + tempList);
    }// Of main
}

        调用类:

package Day_37;


    /**
     * Orthogonal List for directed graph.
     *
     * @author Jian An 2569222191@qq.com.
     */
    public class OrthogonalList {

        /**
         * An inner class for adjacent node.
         */
        class OrthogonalNode {
            /**
             * The row index.
             */
            int row;

            /**
             * The column index.
             */
            int column;

            /**
             * The next out node.
             */
            OrthogonalNode nextOut;

            /**
             * The next in node.
             */
            OrthogonalNode nextIn;

            /**
             * ********************
             * The first constructor.
             *
             * @param paraRow    The row.
             * @param paraColumn The column.
             *                   ********************
             */
            public OrthogonalNode(int paraRow, int paraColumn) {
                row = paraRow;
                column = paraColumn;
                nextOut = null;
                nextIn = null;
            }// Of OrthogonalNode
        }// Of class OrthogonalNode

        /**
         * The number of nodes. This member variable may be redundant since it is always
         * equal to headers.length.
         */
        int numNodes;

        /**
         * The headers for each row.
         */
        OrthogonalNode[] headers;

        /**
         * ********************
         * The first constructor.
         *
         * @param paraMatrix The matrix indicating the graph.
         *                   ********************
         */
        public OrthogonalList(int[][] paraMatrix) {
            numNodes = paraMatrix.length;

            // Step 1. Initialize. The data in the headers are not meaningful.
            OrthogonalNode tempPreviousNode, tempNode;

            headers = new OrthogonalNode[numNodes];

            // Step 2. Link to its out nodes.
            for (int i = 0; i < numNodes; i++) {
                headers[i] = new OrthogonalNode(i, -1);
                tempPreviousNode = headers[i];
                for (int j = 0; j < numNodes; j++) {
                    if (paraMatrix[i][j] == 0) {
                        continue;
                    } // Of if

                    // Create a new node.
                    tempNode = new OrthogonalNode(i, j);

                    // Link.
                    tempPreviousNode.nextOut = tempNode;
                    tempPreviousNode = tempNode;
                } // Of for j
            } // Of for i

            // Step 3. Link to its in nodes. This step is harder.
            OrthogonalNode[] tempColumnNodes = new OrthogonalNode[numNodes];
            for (int i = 0; i < numNodes; i++) {
                tempColumnNodes[i] = headers[i];
            } // Of for i

            for (int i = 0; i < numNodes; i++) {
                tempNode = headers[i].nextOut;
                while (tempNode != null) {
                    tempColumnNodes[tempNode.column].nextIn = tempNode;
                    tempColumnNodes[tempNode.column] = tempNode;

                    tempNode = tempNode.nextOut;
                } // Of while
            } // Of for i
        }// Of the constructor

        /**
         * ********************
         * Overrides the method claimed in Object, the superclass of any class.
         * ********************
         */
        public String toString() {
            String resultString = "Out arcs: ";

            OrthogonalNode tempNode;
            for (int i = 0; i < numNodes; i++) {
                tempNode = headers[i].nextOut;

                while (tempNode != null) {
                    resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
                    tempNode = tempNode.nextOut;
                } // Of while
                resultString += "\r\n";
            } // Of for i

            resultString += "\r\nIn arcs: ";

            for (int i = 0; i < numNodes; i++) {
                tempNode = headers[i].nextIn;

                while (tempNode != null) {
                    resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
                    tempNode = tempNode.nextIn;
                } // Of while
                resultString += "\r\n";
            } // Of for i

            return resultString;
        }// Of toString


    }// Of class OrthogonalList

五. 数据测试

        运行结果如下图所示

         对应的有向图如图G3所示,对应的十字链表如图G4所示。

六. 总结与反思

        十字链表方法其实是邻接表的一种拓展,在邻接表的基础之上增加了一个指针域(入边域),本质上很好理解,但是用代码实现起来其实并没有像邻接表那样简单,邻接表是正向的构建,而十字链表的入边域确是反向的构建,由正在遍历的节点来寻找首节点,这里面的计算机思维我觉得是十分重要的,总之这一节学到的东西很多:反向构建链接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值