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(); }}