一、背景
为了方便以后的复习,这里记录一下树和链表的过程。
二、基础概念
树:左子树,右子树,根节点(逻辑结构)
链表:表头,数据(物理结构)
两种结构的区别
从大的方面来说,数据结构是反映数据的一种形式,它具体分为逻辑结构和物理结构,
1,逻辑结构:它是表现数据之间的一种关系的结构,分为线性结构和非线性结构;
2,物理结构:它是表现数据的是如何存储的结构,计算机内部是如何安排该数据的存储,通常分为顺序结构,链式结构,索引结构,哈希结构;
实现过程
空间结构
三、实现
3.1 链表
定义头文件
#ifndef FIRST_NODEOP_H
#define FIRST_NODEOP_H
class Node1 {
public:
int data{}; // 数据
Node1 *next = nullptr; // 移动和寻找的结构体指针
static void outAllList(Node1 *node1); // 遍历所有结果
static void addTail(Node1 *node1, int value); // 尾插法
static void addHead(Node1 *node1, int value); // 头插法
static char searchOne(Node1 *node1, int value); // 查找
static void deleteOne(Node1 *node1, int value); // 删除
};
#endif //FIRST_NODEOP_H
使用c++的资源文件
#include "nodeop.h"
#include <iostream>
using namespace std;
int main() {
// 使用auto避免重复生成对象名字 和使用Node1 *head = new Node1等效
auto *head = new Node1; // 头节点的声明
head->data = 1;
auto *first = new Node1; // 创建第一个节点
first->data = 2;
head->next = first; // 头结点和第一个节点相连接
auto *second = new Node1; //创建第二个节点
second->data = 10;
first->next = second; // 第一个节点和第二个相连
Node1::addTail(head, 11); // 插入节点
Node1::addTail(head, 12); // 插入节点
Node1::addTail(head, 17); // 插入节点
Node1::addHead(head, 13); // 插入节点
Node1::addHead(head, 14); // 插入节点
Node1::addHead(head, 15); // 插入节点
Node1::outAllList(head);
cout << Node1::searchOne(head, 30) << endl;
cout << Node1::searchOne(head, 2) << endl;
Node1::deleteOne(head, 17); // 删除节点
Node1::outAllList(head); // 遍历结果
}
void Node1::outAllList(Node1 *head) {
while (head != nullptr) { // 遍历结果
cout << head->data << " ";
head = head->next;
}
}
void Node1::addTail(Node1 *node1, int value) {
while (node1->next != nullptr) { // 找到指针的最后节点,插入数据
node1 = node1->next;
}
auto *newNode = new Node1;
newNode->data = value;
node1->next = newNode;
}
void Node1::addHead(Node1 *node1, int value) {
if (node1->next == nullptr) {
auto *newNode = new Node1;
newNode->data = value;
node1->next = newNode;
} else {
auto *newNode = new Node1;
newNode->data = value;
newNode->next = node1->next;
node1->next = newNode;
}
}
char Node1::searchOne(Node1 *node1, int value) {
while (node1 != nullptr) {
if (node1->data == value) {
return 'y';
}
node1 = node1->next;
}
return 'n';
}
void Node1::deleteOne(Node1 *node1, int val) {
Node1 *pre = node1;
Node1 *cur = node1;
while (cur != nullptr && cur->data != val){
pre = cur;
cur = cur->next;
}
if (cur != nullptr){
pre->next = cur->next;
}
}
3.2 树
定义的树的头文件
#ifndef FIRST_TREE_H
#define FIRST_TREE_H
struct Tree {
int data{};
Tree *rightTree = nullptr;
Tree *leftTree = nullptr;
};
#endif //FIRST_TREE_H
资源文件
同时实现了树的深度优先遍历和广度优先遍历(和层次遍历一样)
#include "Tree.h"
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
void mid(Tree *tree);
vector<vector<int> > levelOrder(Tree *pRoot); // 层次、广度搜索
vector<int> dfs(Tree *pBoot); // 深度
int main() {
/**
* 5
* 1 2
* 3 4
* 6 10
*/
Tree *r7 = new Tree();
r7->data = 10;
Tree *r6 = new Tree();
r6->data = 6;
Tree *r2 = new Tree();
r2->data = 2;
Tree *r3 = new Tree();
r3->data = 3;
Tree *r4 = new Tree();
r4->data = 4;
r4->leftTree = r6;
r4->rightTree = r7;
Tree *r1 = new Tree();
r1->data = 1;
r1->rightTree = r4;
r1->leftTree = r3;
Tree *root = new Tree();
root->data = 5;
root->rightTree = r2;
root->leftTree = r1;
cout << "中序遍历:" << endl;
mid(root); // 中序遍历
cout << endl;
vector<vector<int >> nums = levelOrder(root);
cout << "层次遍历: " << endl;
for (auto &num : nums) {
for (int j : num) {
cout << j << " ";
}
}
cout << endl;
cout << "深度遍历: " << endl;
vector<int> nums1 = dfs(root);
for (int num:nums1) {
cout << num << " ";
}
}
void mid(Tree *tree) {
// 中序遍历
if (tree != nullptr) {
// 后续遍历和先序遍历改变输出的位置就可了
mid(tree->leftTree);
cout << tree->data << " ";
mid(tree->rightTree);
}
}
vector<vector<int> > levelOrder(Tree *pRoot) {
// 队列实现
vector<vector<int> > res;
if (pRoot == nullptr)
return res;
queue<Tree *> q;
q.push(pRoot);
while (!q.empty()) {
int cnt = q.size(); // 关键:这个size可以保证逐层遍历二叉树
vector<int> temp;
while (cnt--) { // 层次遍历需要查询完每一个节点
Tree *pNode = q.front(); // 先访问到元素在进行操作
q.pop();
temp.push_back(pNode->data);
if (pNode->leftTree)
q.push(pNode->leftTree);
if (pNode->rightTree)
q.push(pNode->rightTree);
}
res.push_back(temp);
}
return res;
}
vector<int> dfs(Tree *pBoot) {
// 栈实现
vector<int> res;
if (pBoot == nullptr)
return res;
stack<Tree *> s;
s.push(pBoot);
while (!s.empty()) {
Tree *pNode = s.top(); // 先访问到元素 在执行操作
s.pop();
res.push_back(pNode->data);
if (pNode->rightTree) // 由于压栈所以出左边的先右压栈
s.push(pNode->rightTree);
if (pNode->leftTree)
s.push(pNode->leftTree);
}
return res;
}
三、总结
在实现的过程,回想起头指针和头结点的问题,头指针是作为链表和树的第一个位置,所以在请求内存的时候就会生成,以这个来进行操作链表、树等数据结构。
指针移动如果没有设置另一个指针头,那么数据就会遗失,或者使用双指针来进行操作。