001_容器的基本使用_vector
// 001_容器的基本使用_vector.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <vector>
using namespace std;
int main()
{ // vecNum是一个类对象
vector<int> vecNum;
// 增
// 将一个元素存入到动态数组中. 此时数组的元素个数等于1
vecNum.push_back(100);
// 插入的时候, 只能传递一个迭代器
// 要插入到哪个位置就需要先得到该位置的迭代器
// 迭代器就类似于一个指针
// 1. 定义迭代器
// 1.1 iterator是一个类名, 这个类定义在了vector类中.
// iterator是一个类中类
// 1.2 vector<int>::iterator是说明iterator是在vector<int>类中的
// vector<int>::就相当于选择指定作用域
vector<int>::iterator itr = vecNum.begin(); // 得到动态数组的第1个元素的迭代器对象
// 2. 迭代器的三种用法:
// 2.1 自增运算符: ++itr或者itr++,
// 作用: 找到容器中的下一个元素
// 2.2 自减运算符: --itr或者itr--,
// 作用: 找到容器中的上一个元素
// 2.3 取内容运算符: *itr
// 作用: 获取迭代器对应的元素的内容
cout << "itr=" << *itr << endl;// 输出了100,因为100就是第一个元素,itr刚好指向了第一个元素
// 2.3 如果容器的元素个数发生了变化(增加/删除),迭代器对象有可能
// 就失效了 , 因此,最后重新获取一个迭代器.
vecNum.insert(itr, 200);
// 删
// erase只能接收一个迭代器. 删除哪个,就需要传递哪个
// 元素对应的迭代器
// vecNum.begin() + 1 : 指的是第二个元素
vecNum.erase(vecNum.begin() + 1);
vecNum.push_back(300);
vecNum.push_back(400);
// 改(访问)
cout << "当前元素个数:" << vecNum.size() << endl;
cout << "第1个元素的值是: " << vecNum[0]<<endl;
itr = vecNum.begin();
cout << "第2个元素的值是: " << *(itr + 1) << endl;
vecNum[0] = 10000;// 通过[]运算符来修改
cout << "第1个元素的值是: " << vecNum[0] << endl;
itr = vecNum.begin();
*(itr + 1) = 20000; // 通过迭代器来修改
cout << "第2个元素的值是: " << *(itr + 1) << endl;
// 查(遍历)
// 下标遍历法
for (int i = 0; i < vecNum.size(); ++i) {
cout << vecNum[i]<<',';
}
cout << endl;
// 迭代器遍历法(通用)
// auto : 自动根据begin函数返回值类型来推测
// 出itr的变量类型,此处推测出来的是:vector<int>::iterator
for (auto itr = vecNum.begin();
itr != vecNum.end();/*循环结束条件*/
++itr)
{
// 通过迭代器来取得元素的值
cout << *itr << ',';
}
cout << endl;
// c++的新式循环
// 格式: for( 元素类型 变量名 : 可迭代对象){}
// 元素类型 : 就是从可迭代对象中取出的元素的类型
// 变量名 : 随便起
// 可迭代对象: 所有的容器, 可以是一个数组.
// auto& d : vecNum
// auto 自动类型, 能够自动推测出从vecNum中取出的元素的数据类型是什么
// & : 表示引用类型, 表示从vecNum中取出的元素将会被赋值给变量d
// 如果使用引用, 那么d就没有内存,直接引用vecNum中的元素, 修改
// 了d也就等于修改了保存在vecNum中的元素,顺便还能提升循环的效率
// (没有引用的话,从vecNum获取一个元素时,就需要拷贝一次0
// 循环会自动遍历完可迭代对象的所有元素,然后自动退出.
for (auto& d : vecNum) {
cout << d << ',';
}
}
002_容器的使用_list
// 002_容器的使用_list.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> listNum;
// 增
listNum.push_back(100);
listNum.push_back(300);
listNum.insert(listNum.begin(), 200);
// **注意** : list迭代器只能使用++,或--, 不能使用+运算符
// 删
// 只能用迭代器, 但如果要删除第2个元素
list<int>::iterator itr = listNum.begin();
// itr = itr + 3;list迭代器只能使用++,或--, 不能使用+运算符
++++itr; // 自增2次指向第2个元素
// 所有容器的erase都会删除迭代器指向的元素
// 然后删除之后itr指向就无效了, 因此就变成了一个无效迭代器
// erase会通过返回值返回一个有效的迭代器(一般是删除位置的下一个元素的迭代器)
itr = listNum.erase(itr);
// 改
// 只能通过迭代器去修改
// 例如:修改第1个元素
itr = listNum.begin();
cout << "第1个元素: " << *itr << endl;
*itr = 111111;
cout << "第1个元素: " << *itr << endl;
// 例如:修改第2个元素
++itr; // 自增找到第二个元素
cout << "第2个元素: " << *itr << endl;
*itr = 111111;
cout << "第2个元素: " << *itr << endl;
// 查
// 通过迭代器来遍历
for (auto i = listNum.begin(); i != listNum.end(); ++i) {
cout << *i << ',';
}
cout << endl;
// 通过新式循环来遍历
for (auto& d : listNum) {
cout << d << ',';
}
cout << endl;
}
003_容器的基本使用_map
// 003_容器的基本使用_map.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <map>
#include <string>
#include <set>
using namespace std;
int main()
{
// 什么是键值对?
// 键 : 相当于一个索引.相当于目录中的一下.
// 值 : 就是一堆真正要保存的数据. 键相当于值的摘要.用于代表这个值.
// 例如 , 姓名:小明. 姓名:小红, 其中姓名就是键,小明就是值
// 年龄: 18 , 年龄就是键,18就是值.
// 4544545 : 小名
// 4544545就是键,小名值.
// 总是用键来索引值.
// <string, string>
// 第一个string指的是键的类型
// 第二个string指的是值的类型
// 当需要将数据保存到map容器中时,
// 保存的数据的键值对必须都是string类型.
map<string, string> m1;
map<int, string> m2;
// 增
// pair是一个键值对类
// map就是用于保存键值对类的平衡二叉树.
// map是一个平衡二叉树(红黑树)
// 树中节点的数据类型是 std::pair<typename,typename>
// 插入数据到map中的时候, 必须先给出键值对来初始化一个pair对象
// 然后再pair对象插入到map中.
pair<string,string> p1("姓名","小明");
m1.insert(p1);
//p1是一个键值对对象
cout<<"p1.first="<< p1.first<<endl; // 键值对中的键
cout << "p1.second=" << p1.second << endl;// 键值对中的值
// 也可以直接在实参中调用pair的构造函数来构造一个pair对象
m1.insert(pair<string, string>("年龄", "18"));
// 第三种方法,直接使用`[]`运算符来插入
// 实际上[]运算符会在二叉树中以"性别"作为键来搜索pair对象
// 如果没有搜索到, 函数会自动在树中插入一个用"性别"作为键
// 的pair对象, 然后再将新pair对象值赋值成"男"
m1["性别"] = "男";
// m1["姓名"]运算符会在树中以"姓名"作为键找到对应的pair
// 对象, 然后将pair对象的值返回.
string str = m1["姓名"]; // str被赋值成"小明"
str = m1["aaa"]; // 如果获取一个不存在的键的时候,就会插入一个,值有可能会随机(取决于值的类型有无构造函数)
// 删
// 直接传入一个键就行了. 对应的键值对就会被删除.
m1.erase("姓名");
// 改
cout << "性别:" << m1["性别"] << endl;
m1["性别"] = "女";
cout << "性别:" << m1["性别"] << endl;
// 查
// map保存的节点是pair对象
// 所以map的迭代器指向的元素都是pair对象.
//
cout << "遍历map\n";
for (auto itr = m1.begin();
itr != m1.end();
++itr)
{
itr->first; //(*itr).first
cout << itr->first << "="
<<itr->second << endl;
}
m2.insert(pair<int, string>(1, "123456"));
m2.insert(pair<int, string>(2, "456789"));
m2[100] = "1111";
cout << "遍历map\n";
for (auto itr = m2.begin();
itr != m2.end();
++itr)
{
itr->first; //(*itr).first
cout << itr->first << "="
<< itr->second << endl;
}
}
A星算法
A星算法.zip
平衡二叉树
// 二叉树.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <stdio.h>
// 二叉查找树
class AVLTree {
public:
struct Node {
int nData; // 节点的数据域
Node* pLeft; // 左子树指针
Node* pRight; // 右子树指针
Node() :pLeft(), pRight() {}
Node(int nData)
:nData(nData), pLeft(), pRight()
{}
};
public:
int m_nCount = 0; // 树中节点个数
Node* m_pRoot = 0;// 根节点指针:记录树中的根节点地址.
protected:
// 参数1是指针的引用, 为的是能够在
// 函数内部修改函数外部的实参(可以参考交换两个变量的函数)
/*!
* 函数名 : insert
* 功 能 : 将数据nData插入到pTree作为根节点的树居中
* 参 数 : Node * & pTree 树的根节点.
* 参 数 : int nData 插入的数据
* 返回值 : void
*/
bool insert(Node*& pTree, int nData) {
// 1. 判断树的根节点是否为NULL,如果为NULL
// 直接插入到根节点
bool isInsert = false;
if (pTree == nullptr) {
pTree = new Node(nData);
// 递增节点个数
++m_nCount;
return true;
}
// 2. 如果不为空,则判断插入的数据和根节点
// 所保存的数据的大小关系
if (nData < pTree->nData) {
// 3. 如果插入数据小于根节点数据,那么就将
// 根节点的左子树作为新的根节点,让后将
// 数据插入到左子树中.
isInsert = insert(pTree->pLeft, nData);
}
else {
// 否则就插入到右子树中.
isInsert = insert(pTree->pRight, nData);
}
if (isInsert) {
sort(pTree);
}
return isInsert;
}
Node*& find(Node*& pTree, int nData) {
if (pTree == nullptr) {
return pTree;
}
// 1. 判断当前节点保存的数据是否
// 就是要查找的数据
if (pTree->nData == nData) {
// 如果是, 就返回节点本身
return pTree;
}
// 如果不是, 则判断这个数据是在节点的左子树中
// 还是在右子树中.
// 因为二叉树是左小右大的.
// 因此, 比较要删除的数据和当前节点的大小关系
// 就能知道是在左子树还是在右子树中
else if (nData < pTree->nData) {
return find(pTree->pLeft, nData);
}
else {
return find(pTree->pRight, nData);
}
}
bool remove(Node*& pTree, int nData) {
if (pTree == nullptr) {
return false;
}
bool isRemove = false;
printf("-------- 删除:%d----- \n", nData);
printTree();
// 1. 在树中查找到要删除的节点.
Node*& pFindNode = find(pTree, nData);
// 是否能够找到节点.
if (pFindNode == nullptr) {
return false;
}
// 2. 找到节点的就是被删除的节点
// 删除时要判断节点是否是一个叶子节点
// 如果是叶子节点可以直接删除
if (pFindNode->pLeft == nullptr && pFindNode->pRight == nullptr) {
// 直接删除
delete pFindNode;
// 将指针设置为空,由于pFindNode是一个指针
// 的引用, 因此它修改的是哪里????(实际上修改的是
// 父节点的右子树指针或左子树指针)
pFindNode = nullptr;
// 递减节点个数
--m_nCount;
return true;
}
// 3. 如果不是叶子节点. 就需要在被删除节点的
// 子树中找到一个叶子节点, 然后两者的值
// 互换, 再删除掉叶子节点
// 3.1 再找叶子节点的时候, 需要考虑子树高度差
// 因此会在树高比较高的子树中找叶子节点.
int nLeftHeight = getHeight(pFindNode->pLeft);
int nRightHeight = getHeight(pFindNode->pRight);
// 3.2 树中左小右大的关系, 因此, 需要左子树中
// 找最大值, 在右子树中找最小值.
if (nLeftHeight >= nRightHeight) {
// 左子树比较高,需要找最大值
Node*& pNode = getMax(pFindNode->pLeft);
// 将当前被找到的节点和叶子节点的数据域
// 进行调换
int t = pFindNode->nData;
pFindNode->nData = pNode->nData;
pNode->nData = t;
// 删除叶子节点
// 递归删除.
isRemove = remove(pFindNode->pLeft, pNode->nData);
}
else {
// 右子树比较高,需要找最小值
Node*& pNode = getMin(pFindNode->pRight);
int t = pFindNode->nData;
pFindNode->nData = pNode->nData;
pNode->nData = t;
// 删除叶子节点
// 递归删除.
isRemove = remove(pFindNode->pRight, pNode->nData);
}
// 如果删除成功,就检测树是否平衡
if (isRemove) {
sort(pTree);
}
return isRemove;
}
/*!
* 函数名 : getHeight
* 功 能 : 求一个树的高度
* 参 数 : Node * pTree
* 返回值 : int
*/
int getHeight(Node* pTree) {
if (pTree == nullptr) {
return 0;
}
// 求出左子树的高度
int nLeftHeight =
getHeight(pTree->pLeft);
// 求出右子树的高度
int nRightHeight =
getHeight(pTree->pRight);
// 取左右子树高度的最大值作为子树的高度
int nHeight = nLeftHeight > nRightHeight ? nLeftHeight : nRightHeight;
// 本节点的高度 = 自身高度 + 子树的高度
return 1 + nHeight;
}
Node*& getMin(Node*& pTree) {
if (pTree == nullptr) {
return pTree;
}
// 二叉树是左小右大的.
// 一个树的最小值就在树的最左边
if (pTree->pLeft == nullptr) {
// 如果本节点的左子节点已经为空了
// 说明了本节点就是树中最左边的节点
// 就返回节点自身.
return pTree;
}
return getMin(pTree->pLeft);
}
Node*& getMax(Node*& pTree) {
if (pTree == nullptr) {
return pTree;
}
// 二叉树是左小右大的.
// 一个树的最大值就在树的最右边
if (pTree->pRight == nullptr) {
// 如果本节点的右子节点已经为空了
// 说明了本节点就是树中最右边的节点
// 就返回节点自身.
return pTree;
}
return getMax(pTree->pRight);
}
// 清空整颗树(后序遍历
void clear(Node*& pTree) {
clear(pTree->pLeft);
clear(pTree->pRight);
delete pTree;
}
// 前序方式遍历树
void printTreeByPreOrder(Node*& pTree) {
if (pTree == nullptr) {
return;
}
printf("%d,", pTree->nData);
printTreeByPreOrder(pTree->pLeft);
printTreeByPreOrder(pTree->pRight);
}
// 中序方式遍历树
void printTreeByInOrder(Node*& pTree) {
if (pTree == nullptr) {
return;
}
printTreeByInOrder(pTree->pLeft);
printf("%d,", pTree->nData);
printTreeByInOrder(pTree->pRight);
}
// 后序方式遍历树
void printTreeByPostOrder(Node*& pTree) {
if (pTree == nullptr) {
return;
}
printTreeByPostOrder(pTree->pLeft);
printTreeByPostOrder(pTree->pRight);
printf("%d,", pTree->nData);
}
void printTree(Node*& pTree, int nWidth, char ch) {
if (pTree == nullptr) {
return;
}
printTree(pTree->pLeft, nWidth + 2, '/');
printf("%*c%d\n", nWidth, ch, pTree->nData);
printTree(pTree->pRight, nWidth + 2, '\\');
}
/*!
* 函数名 : sortLeft
* 功 能 : 将一颗右单斜的树进行左旋,然后使其平衡
* 参 数 : Node * & pTree
* 返回值 : void
*/
void sortLeft(Node*& K) {
/*
* K(7)
* \ M(9)
* M(9) ( \
* / \ K(7) N(10)
* L(8) N(10) )
* L(8)
*
* K->pRight = L
* M->pLeft = K
* K = M
*/
Node* M = K->pRight;
Node*L = M->pLeft;
Node* N = M->pRight;
K->pRight = L;
M->pLeft = K;
K = M;
}
/*!
* 函数名 : sortRight
* 功 能 : 将一颗左单斜的树进行左旋,然后使其平衡
* 返回值 : void
*/
void sortRight(Node*& K) {
/*
* K(6) M(4)
* / / )
* M(4) N(2) K(6)
* / \ (
* N(2) R(5) R(5)
*
* K->pLeft = R
* M->pRight = K
* K = M
*/
Node* M = K->pLeft;
Node* N = M->pLeft;
Node* R = M->pRight;
K->pLeft = R;
M->pRight = K;
K = M;
}
/*!
* 函数名 : sortLeftRight
* 功 能 : 先左旋再右旋,将右歪斜树变成平衡树
* 参 数 : Node * &
* 返回值 : void
*/
void sortRightLeft(Node*& K ) {
/*
* K(6) K(6) N(7)
* \ \ / \
* M(8) N(7) K(6) M(8)
* / \
* N(7) M(8)
* /
* ??
* 1. 以M作为顶点. 将M形成树视为左单斜树,使用右单旋使其平衡
* 2. 以K作为顶点, 将K形成的是右单斜树, 使用左单旋使其平衡
*/
sortRight(K->pRight);
sortLeft(K);
}
void sortLeftRight(Node*& K) {
sortLeft(K->pLeft);
sortRight(K);
}
void sort(Node*& pTree) {
// 1. 判断树的形状:
int nLeftHeight = getHeight(pTree->pLeft);
int nRightHeight = getHeight(pTree->pRight);
if (nLeftHeight - nRightHeight > 1) {
// 1.1 左斜
nLeftHeight = getHeight(pTree->pLeft->pLeft);
nRightHeight = getHeight(pTree->pLeft->pRight);
if (nLeftHeight > nRightHeight) {
// 1.1.1 左单斜
sortRight(pTree);
}
else {
// 1.1.2 左歪斜
sortLeftRight(pTree);
}
}
else if (nLeftHeight - nRightHeight < -1) {
// 1.2 右斜
nLeftHeight = getHeight(pTree->pRight->pLeft);
nRightHeight = getHeight(pTree->pRight->pRight);
if (nRightHeight > nLeftHeight) {
// 1.2.1 右单斜
sortLeft(pTree);
}
else {
// 1.2.2 右歪斜
sortRightLeft(pTree);
}
}
}
public:
void insert(int nData) {
insert(m_pRoot, nData);
}
void remove(int nData) {
remove(m_pRoot, nData);
}
void printTree() {
printTree(m_pRoot, 2, '*');
printf("\n");
}
};
struct MyStruct {
int nNum = 0;
const char* pStr;
};
const char*& fun(const char*& p) {
return p;
}
int main() {
MyStruct stc = { 0, "hello" };
const char*& p = fun(stc.pStr);
p = "呵呵";
AVLTree tree;
// tree.insert(10);
// tree.insert(5);
// tree.insert(20);
// tree.insert(7);
// tree.insert(6);
// tree.printTree();
//
//
// tree.remove(10);
// tree.printTree();
// 测试平衡性
for (int i = 0; i<10;++i)
{
tree.insert(i);
tree.printTree( );
}
printf("删除\n");
for (int i = 0; i < 10; ++i) {
tree.remove(i);
printf("删除并旋转之后:\n");
tree.printTree();
}
}