见鬼了,晚上睡不着,所以就写写博客算了。
十字链表的逻辑结构如图:
两个线性表,线性表类型为十字链表节点类型(这里为了便于编写代码没有用联合体,联合体会更省空间),其中一个线性表是每一行的头结点,另一个线性表是每一列的头结点。每个节点通过指针的连接形成十字链表,图中画的不是很多,应该是如下的样子:
本次用第一张图的结构进行讲解。结构体声明如下:
//十字链表节点结构体
struct Node
{
int i, j, v;
Node* down, *right;
Node() {
i = j = v = -1;
down = right = nullptr;
}
Node(int i,int j,int v) {
this->i = i;
this->j = j;
this->v = v;
down = right = nullptr;
}
};
//十字链表结构体
struct OrthogonalList
{
int r, c, size;
Node**rhead, **chead;
OrthogonalList() {
throw logic_error("The number of row and column must be specified for the initialization of the cross linked list!");
}
OrthogonalList(int r, int c) {
rhead = new Node*[r];
chead = new Node*[c];
for (int i = 0; i < r; i++)
rhead[i] = new Node();
for (int i = 0; i < c; i++)
chead[i] = new Node();
this->r = r;
this->c = c;
size = 0;
}
~OrthogonalList() {
for (int i = 0; i < r; i++)
delete rhead[i];
for (int i = 0; i < c; i++)
delete chead[i];
delete rhead;
delete chead;
}
};
这里十字链表节点结构体包含5个分量,分别代表行,列,值,同一行的下一个节点(*right),同一列的下一个节点(*down)。
十字链表结构体包含5个分量,rhead则代表行头结点数组,chead代表列头结点数组。r表示行数,c表示列数,size表示矩阵的内非零元素的个数。默认构造函数不允许使用,因为需要提前对rhead和chead分配空间,故需要提前传入参数行数和列数。
二维数组矩阵转化为十字链表函数:
OrthogonalList * to_orthogonalList(int ** matrix, int m, int n)
{
OrthogonalList* r = new OrthogonalList(m, n);
Node *p = nullptr, *q = nullptr, *s = nullptr;
for (int i = 0; i < m; i++)
{
q = nullptr;
s = nullptr;
p = r->rhead[i];
for (int j = 0; j < n; j++)
{
if (matrix[i][j] != 0)
{
//如果矩阵当前位置的值不为0
r->size++;
s = new Node(i + 1, j + 1, matrix[i][j]);
//第一次循环和第i行头结点相连,之后的循环依次和当前最后一个节点相连
p->right = s;
p = p->right;
if (r->chead[j]->down == nullptr)
{
//如果第j列的down指针为空,则直接连接
r->chead[j]->down = s;
}
else
{
//反之则用一个循环找到要连接的位置
for (q = r->chead[j]; q->down != nullptr; q = q->down);
q->down = s;
}
}
}
}
return r;
}
转化过程如下:
1.通过二重循环遍历矩阵,从第1行开始找,接着是第2行,寻找非零元素。
2.用一个指针p指向对应的行的头结点。
3.申请一个新的节点吧非零元素的信息储存进去,节点指针域全部置空,讲p指针所指的头结点相连。p指向新的节点。若节点所在列的头结点没有连接节点,则直接让头结点和新的节点相连,反之则用一个循环寻找要连接的位置。
4.重复步骤3,直到一行找完,遍历下一行矩阵,回到步骤2继续执行。
5.直到矩阵全部建立完毕,返回十字链表指针。
其中第三个步骤需要分情况讨论:
1.若节点所在列的头结点没有元素,则直接相连,如图所示第一列
2.若节点所在的列的头结点已经连接了一个及以上的元素,则用循环找到列的头结点所在链表的最后一个节点连接,如图所示第五列
根据三元组表的输入方式直接建立十字链表函数:
OrthogonalList * get_orthogonalList()
{
int m, n;
cout << "请输入行数和列数:";
cin >> m >> n;
OrthogonalList* r = new OrthogonalList(m, n);
int i, j, v;
Node *p = nullptr, *q = nullptr;
cin >> i >> j >> v;
while (i != -1)
{
r->size++;
q = new Node(i, j, v);
if (r->rhead[i - 1]->right == nullptr)
//如果第i行的头结点的right指针为空,则直接连接
r->rhead[i - 1]->right = q;
else
{
//如果第i行的头节点的right指针不为空
//用循环要插入节点的位置
for (p = r->rhead[i - 1]->right; p->right != nullptr&&p->right->j > j; p = p->right);
//节点插入
q->right = p->right;
p->right = q;
}
if (r->chead[j - 1]->down == nullptr)
//如果第j列的头结点的down指针为空,则直接连接
r->chead[j - 1]->right = q;
else
{
//如果第j列的头结点的down指针不为空
//用循环要插入节点的位置
for (p = r->chead[j - 1]->down; p->down != nullptr&&p->down->i > i; p = p->down);
//节点插入
q->down = p->down;
p->down = q;
}
cin >> i >> j >> v;
}
return r;
}
比如我输入 1 1 2 代表第一行第一列的值为2,如此输入。由于输入可能是无序的,连接节点需要分情况:
1.当前节点所在行,列的头结点并没有连接节点,则直接相连。
2.当前节点所在行,列的头结点已经连接了一个及以上元素,则用一个循环找到要连接的位置(相应指针域为空,或者行号\列好恰好小于当前节点的节点)并连接。
完整代码如下:
#include <iostream>
using namespace std;
//十字链表节点结构体
struct Node
{
int i, j, v;
Node* down, *right;
Node() {
i = j = v = -1;
down = right = nullptr;
}
Node(int i,int j,int v) {
this->i = i;
this->j = j;
this->v = v;
down = right = nullptr;
}
};
//十字链表结构体
struct OrthogonalList
{
int r, c, size;
Node**rhead, **chead;
OrthogonalList() {
throw logic_error("The number of row and column must be specified for the initialization of the cross linked list!");
}
OrthogonalList(int r, int c) {
rhead = new Node*[r];
chead = new Node*[c];
for (int i = 0; i < r; i++)
rhead[i] = new Node();
for (int i = 0; i < c; i++)
chead[i] = new Node();
this->r = r;
this->c = c;
size = 0;
}
~OrthogonalList() {
for (int i = 0; i < r; i++)
delete rhead[i];
for (int i = 0; i < c; i++)
delete chead[i];
delete rhead;
delete chead;
}
};
//得到矩阵
int** get_matrix();
//二维数组矩阵转化为十字链表
OrthogonalList* to_orthogonalList(int** matrix, int m, int n);
//直接按照三元组的格式建立十字链表 例如 1 1 2代表第一行,第一列的值为2
OrthogonalList* get_orthogonalList();
//打印十字链表
void print(OrthogonalList* r);
int main()
{
print(get_orthogonalList());
system("pause");
return 0;
}
int ** get_matrix()
{
int m, n;
int** matrix = nullptr;
cout << "请输入行数和列数:";
cin >> m >> n;
matrix = new int*[m];
for (int i = 0; i < m; i++)
matrix[i] = new int[n];
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
cin >> matrix[i][j];
}
return matrix;
}
OrthogonalList * to_orthogonalList(int ** matrix, int m, int n)
{
OrthogonalList* r = new OrthogonalList(m, n);
Node *p = nullptr, *q = nullptr, *s = nullptr;
for (int i = 0; i < m; i++)
{
q = nullptr;
s = nullptr;
p = r->rhead[i];
for (int j = 0; j < n; j++)
{
if (matrix[i][j] != 0)
{
//如果矩阵当前位置的值不为0
r->size++;
s = new Node(i + 1, j + 1, matrix[i][j]);
//第一次循环和第i行头结点相连,之后的循环依次和当前最后一个节点相连
p->right = s;
p = p->right;
if (r->chead[j]->down == nullptr)
{
//如果第j列的down指针为空,则直接连接
r->chead[j]->down = s;
}
else
{
//反之则用一个循环找到要连接的位置
for (q = r->chead[j]; q->down != nullptr; q = q->down);
q->down = s;
}
}
}
}
return r;
}
OrthogonalList * get_orthogonalList()
{
int m, n;
cout << "请输入行数和列数:";
cin >> m >> n;
OrthogonalList* r = new OrthogonalList(m, n);
int i, j, v;
Node *p = nullptr, *q = nullptr;
cin >> i >> j >> v;
while (i != -1)
{
r->size++;
q = new Node(i, j, v);
if (r->rhead[i - 1]->right == nullptr)
//如果第i行的头结点的right指针为空,则直接连接
r->rhead[i - 1]->right = q;
else
{
//如果第i行的头节点的right指针不为空
//用循环要插入节点的位置
for (p = r->rhead[i - 1]->right; p->right != nullptr&&p->right->j > j; p = p->right);
//节点插入
q->right = p->right;
p->right = q;
}
if (r->chead[j - 1]->down == nullptr)
//如果第j列的头结点的down指针为空,则直接连接
r->chead[j - 1]->right = q;
else
{
//如果第j列的头结点的down指针不为空
//用循环要插入节点的位置
for (p = r->chead[j - 1]->down; p->down != nullptr&&p->down->i > i; p = p->down);
//节点插入
q->down = p->down;
p->down = q;
}
cin >> i >> j >> v;
}
return r;
}
void print(OrthogonalList * r)
{
Node *p = nullptr, *q = nullptr;
for (int i = 0; i < r->r; i++)
{
p = r->rhead[i]->right;
for (q = p; q != nullptr; q = q->right)
cout << q->i << "\t" << q->j << "\t" << q->v << endl;
}
cout << endl;
}