一. 十字链表的本质
- 首先十字链表有一行头指针(RHead), 和一列头指针(CHead), 它们都可以保存一个结点的地址(PointPtr)
- 每个结点保存以下信息: 行号(i), 列号(j), 值(v), 下方结点(down), 右方结点(right)
- 初始化的时候, 各头指针的值都是 NULL
- 可以把每一行, 每一列都看成是一个链表
- 当插入元素的时候, 假设元素的位置是(i, j), 先确定它在第 i 行链表的位置, 再确定它在第 j 列链表的位置
- 这样就构造出了一个十字链表
二. 结点和整个矩阵的设计
1. 结点的设计
根据每个结点需要保存的信息, 可以知道结点应该如下:
1 | //设计结点 |
2. 矩阵的设计
矩阵肯定包括行数(mu), 列数(nu), 非零元素个数(tu), 而且还需要一行(RHead)和一列头指针(CHead), 每个头指针所包含的元素类型都是一样的, 所以一行和一列头指针可以分别用数组保存起来.
由于事先不知道数组的长度, 所以这两个数组应该是动态分配的.
动态分配之后肯定是赋值给指针, 那么是什么类型的指针呢?
答案是: 因为元素类型是 PointPtr , 所以是 PointPtr * 类型的.
那么问题又来了, 为什么元素类型是 PointPtr 的呢?
因为结点是 Point 类型的, 而动态分配结点的返回值是 PointPtr 类型的.
(头指针保存一个结点的地址, 结点又保存下一个结点的地址)
1 | typedef struct |
三. 矩阵的初始化
1. 输入矩阵的信息
1 | do |
2. 初始化各头指针
1 | //分配两个指针数组 |
3. 插入元素
根据元素个数循环:
1 | for(k=1; k<=tu; k++) |
然后输入元素, 确保元素的行号, 列号无误之后, 动态分配一个新结点
1 | do |
至此, 得到了一个新的结点, 然后就到了重点: 在十字链表中插入这个结点.
插入一个结点(i, j), 首先在第 i 行链表中插入, 然后在第 j 列链表中插入. 对我而言还是有点难理解, 但就是这样做的.
在某一行链表中插入, 有两种情况, 一种是链表还没有结点, 直接插入. 还有一种是链表已经有了结点, 在合适的地方插入新结点.
1 | //先确定新结点在某一行中的位置 |
在某一列中插入结点的方法跟上面很相似:
1 | if(T->Chead[j]==NULL || T->Chead[j]->i>i) |
四. 完整代码
1 |
|