键树的实现(大二大作业)

记录一下大二上写的大作业

写篇博客记录一下刚刚检查完的大作业。

说实话这个作业是真的啥b,都叫经典算法的设计与实现了,很多地方都有限制,能自己发挥写的地方真的很少。就当是练习一下数据结构中的链表和树了。

不过也因为有这次大作业我才会有动力去学Qt,之前暑假就想学了,但是一直没有动力,这次我直接B站刷了一个Qt的视频,可以算是有点入门了。写了这么快一年半的代码了,终于可以写点不是黑框的东西了,而且做了个有点像模像样的程序也是挺有成就感的。

抄点百度,怕我忘了这玩意是干啥的

键树又称为数字查找树,它是一颗度大于等于2的树,树中的每个结点中不是包含一个或几个关键字,而是只含有组成关键字的符号。例如,若关键字为数值,则结点中只包含一个数位;若关键字为单词,则结点中只包含一个字母字符。这种树会给某种类型关键字的表的查找带来方便。

不多扯了,上代码。

使用的是Qt5环境。

Headers

dlt.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#ifndef DLT_H
#define DLT_H

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <stack>
using namespace std;

typedef enum {LEAF, BRANCH} NodeKind; //用枚举区别结点类型
typedef string KeyType; //关键字类型为string
typedef char Record; //叶结点的指针定义

struct DLTNode //二链树的结点
{
NodeKind kind;
char symbol;
DLTNode *next;
union
{
Record *infoptr; //如果该结点为最后一个,可以令该指针指向相关信息。
DLTNode *first; //指向该结点的孩子结点
}Ptr;

int childMaxNum = 1; //结点的孩子数
int pathStatus = 0; //表示当前该结点是否被查询
};
typedef DLTNode *DLTree; //键树结点指针


class DLT //键树类
{

public:
DLTree T; //键树的头结点
DLT(); //构造函数
//void CreateTree(DLTree T);
Record* SearchDLTree(DLTree T, KeyType key); //键树的搜索
void InsertDLTree(DLTree T, KeyType key); //键树的插入
//void PrintKey(DLTree T);
void DestroyTree(DLTree T); //键树销毁
void countSingleChildMaxNum(DLTree T); //计算某个结点的孩子数
void countTotalChildMaxNum(DLTree T); //通过递归遍历计算所以结点的孩子数
};

#endif // DLT_H


//DLTree T = (DLTree)malloc(sizeof(struct DLTNode));
// T->kind = BRANCH;
// T->symbol = '\0';
// T->next = NULL;
// T->Ptr.first = NULL;

inputdialog.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef INPUTDIALOG_H
#define INPUTDIALOG_H

#include <QDialog>

namespace Ui {
class inputDialog;
}

class inputDialog : public QDialog //输入对话框类
{
Q_OBJECT

public:
explicit inputDialog(QWidget *parent = nullptr);
~inputDialog();
// QString getedit();
QString edit = ""; //类中定义一个保存输入内容的变量

private:
Ui::inputDialog *ui;

signals:
void express(); //信号函数,用来传递对话框中的内容

};

#endif // INPUTDIALOG_H

mainwindow.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <dlt.h>
#include <QScrollArea>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow //主窗口类
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

//void paintEvent(QPaintEvent *);

DLT tree;

void paintTree(QPainter *painter, DLTree T, int breadth, int depth); //树的绘制
void arrowRight(QPainter *painter, int breadth, int depth, int space); //右箭头的绘制
void arrowDown(QPainter *painter, int breadth, int depth); //左箭头的绘制
//图形结点的属性
int radius = 15; //半径
int startX = 50; //开始位置
int startY = 50;
int interval = 55; //间隔大小


bool eventFilter(QObject *obj, QEvent *event); //事件过滤器,为了能够自己设定绘制的内容
QScrollArea *s; //滚动条
QWidget *w ; //绘制窗口




private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

Sources

dlt.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "dlt.h"

DLT::DLT() //类的构造函数
{
T = (DLTree)malloc(sizeof(struct DLTNode)); //为头结点分配空间
T->kind = BRANCH; //对头结点的各个属性初始化
T->symbol = '\0';
T->next = NULL;
T->Ptr.first = NULL;
}


Record* DLT::SearchDLTree(DLTree T, KeyType key) //键树的搜索,返回最后一个结点的指针。
{
DLTree p = T->Ptr.first;
int i = 0;
stack<DLTree> s; //建立一个栈来存储符合条件的结点指针
while(i < (int)key.length()) //对应搜索关键字的每一个字符。
{
while(p && p->symbol < key[i])
p = p->next; //遍历树的兄弟结点。
if(!p || p->symbol > key[i])
return NULL; //如果在遍历的过程中发现结点的值大于关键字的值,
else //则说明不存在该关键字的结点,可以直接返回查找失败。
{
s.push(p);
p = p->Ptr.first; //查找关键词的下一个关键字。
i++;
}
}
if(p && p->kind == LEAF) //只有最后一个结点为叶结点才能说明存在该关键词
{
s.push(p);
while(!s.empty()) //将栈中的所有结点取出并将标记其查询状态
{
DLTree tmp;
tmp = s.top();
tmp->pathStatus = 1;
s.pop();
}
return p->Ptr.infoptr;
}
else //防止查找到其他关键词的子串时也误判为关键词存在。
return NULL;
}


void DLT::InsertDLTree(DLTree T, KeyType key) //关键词的插入
{
DLTree p = T, q = T->Ptr.first, tmp = NULL;
int i = 0;
bool exist = true;
while(i < (int)key.length()) //找到插入位置
{
while(q && q->symbol < key[i]) //由于键树的结点有序,必须按照规定的顺序找插入位置
{
p = q;
q = q->next;
}
if(q && q->symbol == key[i]) //找到该层上与关键词的某一关键字相等的结点后继续搜索该结点的孩子结点
{
p = q;
q = q->Ptr.first;
i++;
}
else //说明该层缺少该关键字的结点,即找到插入位置
{
exist = false;
tmp = (DLTree)malloc(sizeof(struct DLTNode));
tmp->kind = BRANCH;
tmp->symbol = key[i];
tmp->Ptr.first = NULL;
tmp->next = NULL; //新建结点并进行相关初始化。
if(p->Ptr.first == q) //如果插入位置刚好是某一层的开头
{
p->Ptr.first = tmp;
tmp->next = q;
}
else //如果插入结点是某一层的中间或结尾
{
p->next = tmp;
tmp->next = q;
}
p = tmp;
i++;
while(i < (int)key.length()) //将关键词的后续关键字插入到上一结点的孩子结点
{
tmp = (DLTree)malloc(sizeof(struct DLTNode));
tmp->kind = BRANCH;
tmp->symbol = key[i];
tmp->Ptr.first = NULL;
tmp->next = NULL;
p->Ptr.first = tmp;
p = p->Ptr.first;
i++;
}
tmp = (DLTree)malloc(sizeof(struct DLTNode)); //加上尾结点
tmp->kind = LEAF;
tmp->symbol = '$';
char *t_c = (char *)malloc(sizeof(char) * (key.length() + 1)); //新建一个指针并分配空间
tmp->Ptr.infoptr = strcpy(t_c, key.c_str()); //将该指针赋给尾结点
tmp->next = NULL;
p->Ptr.first = tmp;
}

}
if(exist == true && q && q->kind != LEAF) //如果插入的关键字是某个已存在关键字的子串
{
tmp = (DLTree)malloc(sizeof(struct DLTNode));
tmp->kind = LEAF;
tmp->symbol = '$';
char *t = (char *)malloc(sizeof(char) * (key.length() + 1)); //新建一个指针并分配空间
tmp->Ptr.infoptr = strcpy(t, key.c_str()); //将该指针赋给尾结点
tmp->next = q;
p->Ptr.first = tmp;
}


}


void DLT::DestroyTree(DLTree T) //键树的销毁
{
if(T) //递归的方式遍历所有结点
{
if(T->kind == BRANCH)
DestroyTree(T->Ptr.first); //递归孩子结点
DestroyTree(T->next); //递归兄弟结点
if(T->kind == LEAF)
free(T->Ptr.infoptr); //释放空间
free(T); //释放空间
T = NULL; //赋值防止野指针
}
}


void DLT::countSingleChildMaxNum(DLTree T) //计算某个结点的孩子数
{
if(T->kind == BRANCH) //如果该结点是枝结点
{
int cnt = 0;
DLTree p = T->Ptr.first; //通过指针遍历该结点的孩子结点的所有兄弟结点
while(p)
{
cnt += p->childMaxNum; //计数
p = p->next;
}
T->childMaxNum = cnt; //将计数的值赋给该结点
}
else
{
T->childMaxNum = 1; //如果该结点是叶结点,则孩子数默认为1
}
}

void DLT::countTotalChildMaxNum(DLTree T) //通过递归遍历并使用计算单个结点的函数计算所有结点的孩子数
{
if(T)
{
if(T->kind == BRANCH)
countTotalChildMaxNum(T->Ptr.first); //递归孩子结点
countTotalChildMaxNum(T->next); //递归兄弟结点
countSingleChildMaxNum(T); //调用计算单个结点的函数
}
}

inputdialog.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "inputdialog.h"
#include "ui_inputdialog.h"
#include <QDebug>
inputDialog::inputDialog(QWidget *parent) : //输入对话框
QDialog(parent),
ui(new Ui::inputDialog)
{
ui->setupUi(this);
connect(ui->cancel, &QPushButton::clicked, [=](){ //对话框取消按钮
this->edit = "";
this->close();
});

connect(ui->ok, &QPushButton::clicked, [=](){ //对话框确定按钮
this->edit = ui->lineEdit->text(); //获取输入框里的内容
emit express(); //发送传递内容信号
this->close();
});

}

inputDialog::~inputDialog()
{
delete ui;
}

mainwindow.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <dlt.h>
#include <QPainter>
#include <QPen>
#include <inputdialog.h>
#include <QDebug>
#include <QMessageBox>
#include <QScrollArea>
MainWindow::MainWindow(QWidget *parent) //主窗口
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);

tree = DLT(); //通过构造函数初始化树
//qDebug() << tree.T->symbol;

s = new QScrollArea(this); //创建滚动条
s->setGeometry(0, 0, 600, 600);
w = new QWidget(s); //创建用于绘制的区域
s->setWidget(w);
w->setGeometry(0, 0, 3000, 3000);
w->installEventFilter(this); //加载事件过滤器

connect(ui->btn1, &QPushButton::clicked, [=](){ //Add按钮的功能
inputDialog *dlg = new inputDialog; //新建一个输入框

connect(dlg, &inputDialog::express, [=](){
string str = dlg->edit.toStdString(); //获取输入内容
//string str = "Hello";
tree.InsertDLTree(tree.T, str); //键树的插入
tree.countTotalChildMaxNum(tree.T->Ptr.first); //重新计算所有结点的孩子数量
//qDebug() << QString::fromStdString(str);
//this->update();
w->update(); //重新绘制
});

dlg->setModal(true); //设置对话框的属性
dlg->setAttribute(Qt::WA_DeleteOnClose); //由于是在堆上分配的空间,结束时需要删除,防止泄漏
dlg->exec();
});

connect(ui->btn2, &QPushButton::clicked, [=](){ //Search按钮
inputDialog *dlg = new inputDialog;
connect(dlg, &inputDialog::express, [=](){
string str = dlg->edit.toStdString(); //获取输入内容并进行相应转换
Record *res = NULL;
res = tree.SearchDLTree(tree.T, str); //获取搜索结果
if(res == NULL)
QMessageBox::information(this, "Result", "Not exist");
// else
// QMessageBox::information(this, "Result", "Exist");

});
dlg->setModal(true); //设置对话框相关属性
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->exec();
});

connect(ui->btn3, &QPushButton::clicked, [=](){ //Clear按钮
tree.DestroyTree(tree.T->Ptr.first); //键树的销毁并释放空间
tree.T->Ptr.first = NULL;
w->update();
});

}

bool MainWindow::eventFilter(QObject *obj, QEvent *event) //事件过滤器
{
if(obj == w && event->type() == QEvent::Paint) //在指定窗口上绘制
{
QPainter *painter = new QPainter(w); //新建绘图工具
paintTree(painter, MainWindow::tree.T, 0, 0); //调用绘制函数
painter->end();
}
return QWidget::eventFilter(obj,event); //将其余事件返回给系统控制
}

void MainWindow::paintTree(QPainter *painter, DLTree T, int breadth, int depth) //自定义的绘图事件,递归实现
{
if(T->pathStatus == 1) //如果该结点正在被查询,进行特殊绘制
{
T->pathStatus = 0;
QPen pen = painter->pen();
painter->setPen(Qt::blue); //将结点绘制成蓝色
painter->drawEllipse(QPoint(startX + breadth * interval, startY + depth * interval), radius, radius);
painter->setPen(pen);
}
else //普通绘制
painter->drawEllipse(QPoint(startX + breadth * interval, startY + depth * interval), radius, radius);
char *s = new char[2]; //填充内容
s[0] = T->symbol;s[1] = '\0';
painter->drawText(QRect(startX - radius + breadth * interval, startY - radius + depth * interval, radius * 2, radius * 2), Qt::AlignCenter, tr(s));
delete[] s; //释放空间
if(T->symbol != '$' && T->Ptr.first) //如果有孩子结点
{
arrowDown(painter, breadth, depth); //画出向下的箭头
paintTree(painter, T->Ptr.first, breadth, depth + 1); //进入递归,优先处理孩子结点。
}
if(T->next) //如果有兄弟结点
{
arrowRight(painter, breadth, depth, T->childMaxNum); //画出向右的箭头,需要考虑中间的间隔
paintTree(painter, T->next, breadth + T->childMaxNum, depth); //进入递归,处理兄弟结点
}

}

void MainWindow::arrowDown(QPainter *painter, int breadth, int depth) //画向下箭头
{
painter->drawLine(startX + breadth * interval, startY + radius + depth * interval, startX + breadth * interval, startY - radius + (depth + 1) * interval);
painter->drawLine(startX + breadth * interval, startY - radius + (depth + 1) * interval, startX + breadth * interval + 3, startY - radius + (depth + 1) * interval - 8);
painter->drawLine(startX + breadth * interval, startY - radius + (depth + 1) * interval, startX + breadth * interval - 3, startY - radius + (depth + 1) * interval - 8);
}

void MainWindow::arrowRight(QPainter *painter, int breadth, int depth, int space) //画向右箭头,要考虑中间的间隔,防止重叠
{
painter->drawLine(startX + radius + breadth * interval, startY + depth * interval, startX - radius + (breadth + space) * interval, startY + depth * interval);
painter->drawLine(startX - radius + (breadth + space) * interval, startY + depth * interval, startX - radius + (breadth + space) * interval - 8, startY + depth * interval - 3);
painter->drawLine(startX - radius + (breadth + space) * interval, startY + depth * interval, startX - radius + (breadth + space) * interval - 8, startY + depth * interval + 3);
}

MainWindow::~MainWindow() //析构
{
delete ui;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[]) //主函数
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

Forms

inputdialog.ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>inputDialog</class>
<widget class="QDialog" name="inputDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>362</width>
<height>130</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>362</width>
<height>130</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>362</width>
<height>130</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ok">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

mainwindow.ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>800</width>
<height>600</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>800</width>
<height>600</height>
</size>
</property>
<property name="windowTitle">
<string>DST</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>640</x>
<y>400</y>
<width>125</width>
<height>171</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="btn1">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn2">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn3">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

最终效果

作者

Jhuoer Yen

发布于

2021-01-19

更新于

2023-09-18

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×