DLUT-2022 小学期每日进程
8.8
数据结构意义和应用
数据结构,简单来说就是研究数据的存储方式。在编写代码的过程中,我们进行数据存储的目的就是方便后期对数据的再利用,没有理由的数据存储行为是对存储空间管理的不负责。理解数据结构,可以帮助我们解决具有复杂关系的大量数据的存储问题。
不仅如此,数据结构还是算法的基石,为算法的实现提供坚实的基础。
那么数据结构有哪些组成方式呢?
-
线性结构
线性结构是最简单的数据结构,包括数组、链表,以及它们衍生出的栈、队列、哈希表。
-
树
树是相对复杂的数据结构,其中比较有代表性的是二叉树,由它又衍生出了二叉堆之类的数据结构。
-
图
图是相对复杂的数据结构,因为在图中会呈现出多对多的关联关系。
单链表的基本概念和操作
在开始小学期之前,笔者已经尝试利用 Java 实现单链表、双向链表、循环链表,虽然对使用 c++ 实现没有涉及,但是能够更好地理解链表的运行原理,编程工具不同但是思想相同。
基本概念
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干个节点(node)所组成。
单链表也就是单向链表,它的每一个节点包含两部分,一部分是存储数据的变量,另一部分是指向下一个节点的指针 next。链表的第 1 个节点被称为头节点,最后 1 个节点称为尾节点,尾节点的 next 指针指向 NULL。
与数组按照下标来随机寻找元素不同,对于链表的其中一个节点 myNode1 ,只能根据节点 myNode 的 next 指针来寻找到该节点的下一个节点 myNode2,依次向下查找,一级一级单向传递。
基本操作
以解决约瑟夫问题来举例
-
初始化链表
//定义节点 class node { public: int data; node* next; //下一个节点 }; //定义链表 class list { public: int size; node* head; //头节点地址 }; //初始化 list* InitializeList() { list* List = new list; List->size = 0; List->head = new node; List->head->data = 0; List->head->next = List->head; return List; }
-
插入数据和删除数据
//插入数据 void InsertList(list* List, int position, int num) { node* newNode = new node; newNode->data = num; newNode->next = NULL; //当前节点 node* currentNode = List->head; for (int i = 0; i < position; i++) { currentNode = currentNode->next; } newNode->next = currentNode->next; currentNode->next = newNode; List->size++; } //删除数据,如果成功删除返回 true,空链表返回 false bool DeleteList(list* List, int num) { if (List->head == NULL) { return 0; } else { node* currentNode = List->head->next; int position = 0; for (int i = 0; i < List->size; i++) { if (currentNode->data == num) break; currentNode = currentNode->next; position++; } currentNode = List->head; for (int i = 0; i < position; i++) { currentNode = currentNode->next; } currentNode->next = currentNode->next->next; List->size--; } return 1; }
-
链表输入
//输出 void PrintList(list* List, int num) { node* currentNode = List->head; for (int i = 0; i < num; i++) { if (currentNode == List->head) currentNode = currentNode->next; cout << currentNode->data << " "; currentNode = currentNode->next; } cout << endl; }
-
最终代码实现
#include <iostream> using namespace std; int main() { int m, n; int temp; cout << "输入数字数量:"; cin >> m; cout << "每数到多少去除:"; cin >> n; //初始化链表 list* List = InitializeList(); for (int i = 0; i < m; i++) { InsertList(List, i, i + 1); } //输出初始链表 cout << "链表为:" << endl; PrintList(List, m); cout << endl; node* currentNode = List->head; int icounter = 1; while (List->size != 0) { for (int i = 0; i < 2; i++) { //排除头节点 if (currentNode == List->head) currentNode = currentNode->next; currentNode = currentNode->next; if (currentNode == List->head) currentNode = currentNode->next; } temp = currentNode->data; if (DeleteList(List, currentNode->data)) { cout << "第" << icounter << "次删除的数字为:" << temp << endl; } else cout << "空列表删除失败!" << endl; currentNode = currentNode->next; icounter++; } return 0; }
8.9
OOP
OOP(Object Oriented Programming)既面向对象程序设计,本质上是以建立模型体现出来的抽象思维过程和面向对象的方法,其基本原则是计算机程序由单个能起到子程序作用的单元或对象组合而成。
OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性,核心概念是类和对象。
OOP = 对象 + 类 + 继承 + 多态 + 消息
面向对象的三大特征是:封装、继承和多态。
STL
概述
STL(Standard Template Library)译为标准模板库或者泛型库,包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于高效完成各种功能。
从根本上说,STL 是一些容器、算法和其他一些组件的集合,所有容器和算法都是总结了几十年来算法和数据结构的研究成果,汇集了许多计算机专家学者经验的基础上实现的,STL 基本上达到了各种存储方法和相关算法的高度优化。
STL 六大部件:容器(Containers)、分配器(Allocators)、算法(Algorithm)、迭代器(Iterators)、适配器(Adapters)、仿函数(Functors)。
STL 参考文档
- C++ 标准库参考 | Microsoft Docs (中文文档)
常用STL
在 C++ 标准中,STL 被组织为以下的头文件:algorithm/deque/functional/iterator/list/map/memory/numeric/queue/set/stack/utility/vector
-
pair
表示一个二元组或元素对,并提供按照字典序对元素进行大小比较。在 中定义了 pair 的六个比较运算符:<、>、<=、>=、==、!=,先比较 first ,再比较 second,也可以通过重载改变比较逻辑。
-
vector
向量容器模板类,以连续数组的方式存储元素序列,可以看做是以顺序结构实现的线性表,是动态数组的理想选择,需要两个模板参数,第一个是存储元素的数据类型,第二个是参数存储分配器的类型。
#include<vector> vector<int>s;//定义一个空 vector,存储 int 类型对象 vector<int>s(n);//定义一个含有 n 个 int 元素的 vector 对象 vector<int>s(first,last);//定义一个 vector 对象,并从由迭代器 first 和 last 定义的序列中复制初值 s[i];//直接以下标方式访问容器中的元素。 s.front();//返回首元素。 s.back();//返回尾元素。 s.push_back(x);//向表尾插入元素 x。 s.size();//返回表长。 s.empty();//当表空时,返回真,否则返回假。 s.pop_back();//删除表尾元素。 s.begin();//返回指向首元素的随机存取迭代器。 s.end();//返回指向尾元素的下一个位置的随机存取迭代器。 s.insert(it, x);//向迭代器 it 指向的元素前插入新元素 val。 s.insert(it, n, x);//向迭代器 it 指向的元素前插入 n 个 x。 s.insert(it, first, last);//将由迭代器 first 和 last 所指定的序列 [first, last)插入到迭代器 it 指向的元素前面。 s.erase(it);//删除由迭代器 it 所指向的元素。 s.erase(first, last);//删除由迭代器 first 和 last 所指定的序列[first, last)。 s.reserve(n);//预分配缓冲空间,使存储空间至少可容纳 n 个元素。 s.resize(n);//改变序列的长度,超出的元素将会被删除,如果序列需要扩展,原空间小于 n, 元素默认值将填满扩展出的空间。 s.resize(n, val);//改变序列的长度,超出的元素将会被删除,如果序列需要扩展,原空间小于 n, 将用 val 填满扩展出的空间。 s.clear();//删除容器中的所有的元素。 s.swap(v);//将 s 与另一个 vector 对象 v 进行交换
-
iterator
iterator 迭代器,是用于访问容器中元素的指示器,相当于数据结构中所说的“遍历指针”,也可以看成一种泛化的指针,STL 有一下几类迭代器:
- 输入 iterator,在容器的连续区间内向前移动,读取容器内任意值
- 输出 iterator,把值写进它所指向的容器中
- 前向 iterator,读取队列中的值,并可以向前移动到下一位置(++p,p++)
- 双向 iterator,读取队列中的值,并可以向前向后遍历容器
- 随机访问 iterator,可以直接以下标的方式对容器进行访问
- 流 iterator,可以直接输出、输入流中的值
-
string
string 是 STL 为我们提供的更便捷的字符串的表达方式,可以看做是一个字符的 vector,vector 上的各种操作都适用于 string,还支持字符串的拼合、转换等操作。
-
stack/queue
stack(栈) 和 queue(队列) 是常用的数据容器。
- stack 定义在 中,有两个模板参数,一个是元素类型,一个是容器类型,前者是必要的。可以进行入栈、出栈、访问栈顶、判断栈空等操作。
- queue 定义在 中,也是两个模板参数,第一个元素类型是必要的。可以进行入队、出队、访问队首元素、访问队尾元素、判断队列空、访问队列中的元素个数等操作。
-
map
在
map 的基本操作有:定义 map 对象、向 map 中插入元素对、查找元素对、删除元素对等。
-
algorithm
是 STL 中最大的一个头文件,它是由一大堆模板函数组成的。
8.10
图形用户界面框架
图形用户界面(Graphical User Interface) 即 GUI,C++ 常用的 GUI 开发框架有以下几个:
-
QT
QT 是跨平台 C++ 图形用户界面应用程序开发框架,目前使用广泛。
-
MFC
Windows 下的 GUI 库,使用比较广泛。
-
wxWidgets
wxWidgets 应用于 Windows、Linux、MacOS等平台。
-
gtkmm
gtkmm 是流行的图形界面库 GTK+ 的官方 C++ 接口。
-
其他
- imgui
- duilib
- xcgui
- GuiLite
- soui
QT Hello World
#include "widget.h"
#include <QLabel>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel *hello = new QLabel("Hello world!");
hello->show();
return a.exec();
}
运行结果如下
(丑是丑点,但是两行代码还要什么自行车啊…)