# 定义顶点节点
class Vertex(object):
    def __init__(self, data):
        """
        根据顶点值构造顶点节点
        :param data: 顶点的值
        """
        self.data = data  # 数据域
        self.firstIn = None  # 以该顶点为弧头的边组成的链表的第一个节点
        self.firstOut = None  # 以该顶点为弧尾的边组成的链表的第一个节点


# 定义边(弧)节点
class Edge(object):
    def __init__(self, headvex, tailvex):
        """
        根据弧头和弧尾构造一个边(弧)节点
        :param headvex: 弧头节点在顶点列表中的下标值
        :param tailvex: 弧尾节点在顶点列表中的下标值
        """
        self.headvex = headvex  # 弧头顶点在顶点列表中的下标值
        self.tailvex = tailvex  # 弧尾顶点在顶点列表中的下标值
        self.tlink = None  # 和当前弧具有相同弧尾的下一个弧的链接
        self.hlink = None  # 和当前弧具有相同弧头的下一个弧的链接


# 定义十字链表
class OrthogonalList(object):
    def __init__(self, vertexs, edges):
        """
        根据顶点集合和边集合构造十字链表
        :param vertexs: 顶点集合
        :param edges: 边集合
        """
        self.vlen = len(vertexs)  # 顶点的数量
        self.elen = len(edges)  # 边的数量
        # 构造顶点列表
        self.vertexsList = [Vertex for i in range(self.vlen)]  # 生成一个长度和顶点数据数量一样的空的顶点节点类型的列表
        for i in range(self.vlen):
            self.vertexsList[i] = Vertex(vertexs[i])  # 将顶点集合中的顶点一次添加到顶点数组中
        # 添加每一条边到十字链表
        for i in range(self.elen):
            self.__addEdge(edges[i][0], edges[i][1])

    # 根据指定的弧头弧尾的顶点值,将弧添加到十字链表中
    def __addEdge(self, tail, head):
        """
        根据指定的弧头弧尾的顶点值,将弧添加到十字链表中
        :param tail: 弧尾顶点值
        :param head: 弧头顶点值
        """
        # 分别求出弧尾、弧头顶点在顶点列表中的下标值
        tailvexIndex = self.__getPosition(tail)
        headvexIndex = self.__getPosition(head)
        # 构造新的边
        newEdge = Edge(headvexIndex, tailvexIndex)
        # 弧头、弧尾节点对应的顶点
        tailvex = self.vertexsList[tailvexIndex]
        headvex = self.vertexsList[headvexIndex]
        # 将新的边添加到弧尾顶点的以该顶点为弧尾的边组成的链表中
        if tailvex.firstOut is None:
            tailvex.firstOut = newEdge
        else:
            e = tailvex.firstOut
            while e.tlink is not None:
                e = e.tlink
            e.tlink = newEdge
        # 将新的边添加到弧头顶点的以该顶点为弧头的边组成的链表中
        if headvex.firstIn is None:
            headvex.firstIn = newEdge
        else:
            e = headvex.firstIn
            while e.hlink is not None:
                e = e.hlink
            e.hlink = newEdge

    # 获取顶点在顶点列表中的下标值
    def __getPosition(self, v):
        """
        获取顶点在顶点列表中的下标值
        :param v: 顶点
        :return: 对应的下标
        """
        for i in range(len(self.vertexsList)):
            if self.vertexsList[i].data is v:
                return i
        return -1

    # 求顶点入度(以顶点为弧头的边数)
    def inDegree(self, v):
        """
        求顶点的入度
        :param v: 顶点
        :return: 入度
        """
        vex = self.vertexsList[self.__getPosition(v)]  # 获取顶点在十字链表中的顶点列表中对应的元素
        inD = 0  # 保存入度
        h = vex.firstIn
        while h is not None:
            inD += 1
            h = h.hlink
        return inD

    # 求顶点出度(以顶点为为弧尾的边数)
    def outDegree(self, v):
        """
        求顶点的出度
        :param v: 顶点
        :return: 出度
        """
        vex = self.vertexsList[self.__getPosition(v)]  # 获取顶点在十字链表中的顶点列表中对应的元素
        outD = 0  # 保存出度
        t = vex.firstOut
        while t:
            outD += 1
            t = t.tlink
        return outD


# 测试十字链表功能
if __name__ == '__main__':
    vertex = [1, 2, 3, 4, 5]
    edges = [[1, 2], [2, 3], [3, 1], [3, 4], [4, 1], [4, 5], [1, 5]]
    o = OrthogonalList(vertex, edges)
    for i in vertex:
        print('顶点%d的入度和出度分别是:%d,%d' % (i, o.inDegree(i), o.outDegree(i)))