001_容器的基本使用_vector

  1. // 001_容器的基本使用_vector.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"
  4. #include <iostream>
  5. #include <vector>
  6. using namespace std;
  7. int main()
  8. { // vecNum是一个类对象
  9. vector<int> vecNum;
  10. // 增
  11. // 将一个元素存入到动态数组中. 此时数组的元素个数等于1
  12. vecNum.push_back(100);
  13. // 插入的时候, 只能传递一个迭代器
  14. // 要插入到哪个位置就需要先得到该位置的迭代器
  15. // 迭代器就类似于一个指针
  16. // 1. 定义迭代器
  17. // 1.1 iterator是一个类名, 这个类定义在了vector类中.
  18. // iterator是一个类中类
  19. // 1.2 vector<int>::iterator是说明iterator是在vector<int>类中的
  20. // vector<int>::就相当于选择指定作用域
  21. vector<int>::iterator itr = vecNum.begin(); // 得到动态数组的第1个元素的迭代器对象
  22. // 2. 迭代器的三种用法:
  23. // 2.1 自增运算符: ++itr或者itr++,
  24. // 作用: 找到容器中的下一个元素
  25. // 2.2 自减运算符: --itr或者itr--,
  26. // 作用: 找到容器中的上一个元素
  27. // 2.3 取内容运算符: *itr
  28. // 作用: 获取迭代器对应的元素的内容
  29. cout << "itr=" << *itr << endl;// 输出了100,因为100就是第一个元素,itr刚好指向了第一个元素
  30. // 2.3 如果容器的元素个数发生了变化(增加/删除),迭代器对象有可能
  31. // 就失效了 , 因此,最后重新获取一个迭代器.
  32. vecNum.insert(itr, 200);
  33. // 删
  34. // erase只能接收一个迭代器. 删除哪个,就需要传递哪个
  35. // 元素对应的迭代器
  36. // vecNum.begin() + 1 : 指的是第二个元素
  37. vecNum.erase(vecNum.begin() + 1);
  38. vecNum.push_back(300);
  39. vecNum.push_back(400);
  40. // 改(访问)
  41. cout << "当前元素个数:" << vecNum.size() << endl;
  42. cout << "第1个元素的值是: " << vecNum[0]<<endl;
  43. itr = vecNum.begin();
  44. cout << "第2个元素的值是: " << *(itr + 1) << endl;
  45. vecNum[0] = 10000;// 通过[]运算符来修改
  46. cout << "第1个元素的值是: " << vecNum[0] << endl;
  47. itr = vecNum.begin();
  48. *(itr + 1) = 20000; // 通过迭代器来修改
  49. cout << "第2个元素的值是: " << *(itr + 1) << endl;
  50. // 查(遍历)
  51. // 下标遍历法
  52. for (int i = 0; i < vecNum.size(); ++i) {
  53. cout << vecNum[i]<<',';
  54. }
  55. cout << endl;
  56. // 迭代器遍历法(通用)
  57. // auto : 自动根据begin函数返回值类型来推测
  58. // 出itr的变量类型,此处推测出来的是:vector<int>::iterator
  59. for (auto itr = vecNum.begin();
  60. itr != vecNum.end();/*循环结束条件*/
  61. ++itr)
  62. {
  63. // 通过迭代器来取得元素的值
  64. cout << *itr << ',';
  65. }
  66. cout << endl;
  67. // c++的新式循环
  68. // 格式: for( 元素类型 变量名 : 可迭代对象){}
  69. // 元素类型 : 就是从可迭代对象中取出的元素的类型
  70. // 变量名 : 随便起
  71. // 可迭代对象: 所有的容器, 可以是一个数组.
  72. // auto& d : vecNum
  73. // auto 自动类型, 能够自动推测出从vecNum中取出的元素的数据类型是什么
  74. // & : 表示引用类型, 表示从vecNum中取出的元素将会被赋值给变量d
  75. // 如果使用引用, 那么d就没有内存,直接引用vecNum中的元素, 修改
  76. // 了d也就等于修改了保存在vecNum中的元素,顺便还能提升循环的效率
  77. // (没有引用的话,从vecNum获取一个元素时,就需要拷贝一次0
  78. // 循环会自动遍历完可迭代对象的所有元素,然后自动退出.
  79. for (auto& d : vecNum) {
  80. cout << d << ',';
  81. }
  82. }

002_容器的使用_list

  1. // 002_容器的使用_list.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"
  4. #include <iostream>
  5. #include <list>
  6. using namespace std;
  7. int main()
  8. {
  9. list<int> listNum;
  10. // 增
  11. listNum.push_back(100);
  12. listNum.push_back(300);
  13. listNum.insert(listNum.begin(), 200);
  14. // **注意** : list迭代器只能使用++,或--, 不能使用+运算符
  15. // 删
  16. // 只能用迭代器, 但如果要删除第2个元素
  17. list<int>::iterator itr = listNum.begin();
  18. // itr = itr + 3;list迭代器只能使用++,或--, 不能使用+运算符
  19. ++++itr; // 自增2次指向第2个元素
  20. // 所有容器的erase都会删除迭代器指向的元素
  21. // 然后删除之后itr指向就无效了, 因此就变成了一个无效迭代器
  22. // erase会通过返回值返回一个有效的迭代器(一般是删除位置的下一个元素的迭代器)
  23. itr = listNum.erase(itr);
  24. // 改
  25. // 只能通过迭代器去修改
  26. // 例如:修改第1个元素
  27. itr = listNum.begin();
  28. cout << "第1个元素: " << *itr << endl;
  29. *itr = 111111;
  30. cout << "第1个元素: " << *itr << endl;
  31. // 例如:修改第2个元素
  32. ++itr; // 自增找到第二个元素
  33. cout << "第2个元素: " << *itr << endl;
  34. *itr = 111111;
  35. cout << "第2个元素: " << *itr << endl;
  36. // 查
  37. // 通过迭代器来遍历
  38. for (auto i = listNum.begin(); i != listNum.end(); ++i) {
  39. cout << *i << ',';
  40. }
  41. cout << endl;
  42. // 通过新式循环来遍历
  43. for (auto& d : listNum) {
  44. cout << d << ',';
  45. }
  46. cout << endl;
  47. }

003_容器的基本使用_map

  1. // 003_容器的基本使用_map.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"
  4. #include <iostream>
  5. #include <map>
  6. #include <string>
  7. #include <set>
  8. using namespace std;
  9. int main()
  10. {
  11. // 什么是键值对?
  12. // 键 : 相当于一个索引.相当于目录中的一下.
  13. // 值 : 就是一堆真正要保存的数据. 键相当于值的摘要.用于代表这个值.
  14. // 例如 , 姓名:小明. 姓名:小红, 其中姓名就是键,小明就是值
  15. // 年龄: 18 , 年龄就是键,18就是值.
  16. // 4544545 : 小名
  17. // 4544545就是键,小名值.
  18. // 总是用键来索引值.
  19. // <string, string>
  20. // 第一个string指的是键的类型
  21. // 第二个string指的是值的类型
  22. // 当需要将数据保存到map容器中时,
  23. // 保存的数据的键值对必须都是string类型.
  24. map<string, string> m1;
  25. map<int, string> m2;
  26. // 增
  27. // pair是一个键值对类
  28. // map就是用于保存键值对类的平衡二叉树.
  29. // map是一个平衡二叉树(红黑树)
  30. // 树中节点的数据类型是 std::pair<typename,typename>
  31. // 插入数据到map中的时候, 必须先给出键值对来初始化一个pair对象
  32. // 然后再pair对象插入到map中.
  33. pair<string,string> p1("姓名","小明");
  34. m1.insert(p1);
  35. //p1是一个键值对对象
  36. cout<<"p1.first="<< p1.first<<endl; // 键值对中的键
  37. cout << "p1.second=" << p1.second << endl;// 键值对中的值
  38. // 也可以直接在实参中调用pair的构造函数来构造一个pair对象
  39. m1.insert(pair<string, string>("年龄", "18"));
  40. // 第三种方法,直接使用`[]`运算符来插入
  41. // 实际上[]运算符会在二叉树中以"性别"作为键来搜索pair对象
  42. // 如果没有搜索到, 函数会自动在树中插入一个用"性别"作为键
  43. // 的pair对象, 然后再将新pair对象值赋值成"男"
  44. m1["性别"] = "男";
  45. // m1["姓名"]运算符会在树中以"姓名"作为键找到对应的pair
  46. // 对象, 然后将pair对象的值返回.
  47. string str = m1["姓名"]; // str被赋值成"小明"
  48. str = m1["aaa"]; // 如果获取一个不存在的键的时候,就会插入一个,值有可能会随机(取决于值的类型有无构造函数)
  49. // 删
  50. // 直接传入一个键就行了. 对应的键值对就会被删除.
  51. m1.erase("姓名");
  52. // 改
  53. cout << "性别:" << m1["性别"] << endl;
  54. m1["性别"] = "女";
  55. cout << "性别:" << m1["性别"] << endl;
  56. // 查
  57. // map保存的节点是pair对象
  58. // 所以map的迭代器指向的元素都是pair对象.
  59. //
  60. cout << "遍历map\n";
  61. for (auto itr = m1.begin();
  62. itr != m1.end();
  63. ++itr)
  64. {
  65. itr->first; //(*itr).first
  66. cout << itr->first << "="
  67. <<itr->second << endl;
  68. }
  69. m2.insert(pair<int, string>(1, "123456"));
  70. m2.insert(pair<int, string>(2, "456789"));
  71. m2[100] = "1111";
  72. cout << "遍历map\n";
  73. for (auto itr = m2.begin();
  74. itr != m2.end();
  75. ++itr)
  76. {
  77. itr->first; //(*itr).first
  78. cout << itr->first << "="
  79. << itr->second << endl;
  80. }
  81. }

A星算法

A星算法.zip

平衡二叉树

  1. // 二叉树.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"
  4. #include <stdio.h>
  5. // 二叉查找树
  6. class AVLTree {
  7. public:
  8. struct Node {
  9. int nData; // 节点的数据域
  10. Node* pLeft; // 左子树指针
  11. Node* pRight; // 右子树指针
  12. Node() :pLeft(), pRight() {}
  13. Node(int nData)
  14. :nData(nData), pLeft(), pRight()
  15. {}
  16. };
  17. public:
  18. int m_nCount = 0; // 树中节点个数
  19. Node* m_pRoot = 0;// 根节点指针:记录树中的根节点地址.
  20. protected:
  21. // 参数1是指针的引用, 为的是能够在
  22. // 函数内部修改函数外部的实参(可以参考交换两个变量的函数)
  23. /*!
  24. * 函数名 : insert
  25. * 功 能 : 将数据nData插入到pTree作为根节点的树居中
  26. * 参 数 : Node * & pTree 树的根节点.
  27. * 参 数 : int nData 插入的数据
  28. * 返回值 : void
  29. */
  30. bool insert(Node*& pTree, int nData) {
  31. // 1. 判断树的根节点是否为NULL,如果为NULL
  32. // 直接插入到根节点
  33. bool isInsert = false;
  34. if (pTree == nullptr) {
  35. pTree = new Node(nData);
  36. // 递增节点个数
  37. ++m_nCount;
  38. return true;
  39. }
  40. // 2. 如果不为空,则判断插入的数据和根节点
  41. // 所保存的数据的大小关系
  42. if (nData < pTree->nData) {
  43. // 3. 如果插入数据小于根节点数据,那么就将
  44. // 根节点的左子树作为新的根节点,让后将
  45. // 数据插入到左子树中.
  46. isInsert = insert(pTree->pLeft, nData);
  47. }
  48. else {
  49. // 否则就插入到右子树中.
  50. isInsert = insert(pTree->pRight, nData);
  51. }
  52. if (isInsert) {
  53. sort(pTree);
  54. }
  55. return isInsert;
  56. }
  57. Node*& find(Node*& pTree, int nData) {
  58. if (pTree == nullptr) {
  59. return pTree;
  60. }
  61. // 1. 判断当前节点保存的数据是否
  62. // 就是要查找的数据
  63. if (pTree->nData == nData) {
  64. // 如果是, 就返回节点本身
  65. return pTree;
  66. }
  67. // 如果不是, 则判断这个数据是在节点的左子树中
  68. // 还是在右子树中.
  69. // 因为二叉树是左小右大的.
  70. // 因此, 比较要删除的数据和当前节点的大小关系
  71. // 就能知道是在左子树还是在右子树中
  72. else if (nData < pTree->nData) {
  73. return find(pTree->pLeft, nData);
  74. }
  75. else {
  76. return find(pTree->pRight, nData);
  77. }
  78. }
  79. bool remove(Node*& pTree, int nData) {
  80. if (pTree == nullptr) {
  81. return false;
  82. }
  83. bool isRemove = false;
  84. printf("-------- 删除:%d----- \n", nData);
  85. printTree();
  86. // 1. 在树中查找到要删除的节点.
  87. Node*& pFindNode = find(pTree, nData);
  88. // 是否能够找到节点.
  89. if (pFindNode == nullptr) {
  90. return false;
  91. }
  92. // 2. 找到节点的就是被删除的节点
  93. // 删除时要判断节点是否是一个叶子节点
  94. // 如果是叶子节点可以直接删除
  95. if (pFindNode->pLeft == nullptr && pFindNode->pRight == nullptr) {
  96. // 直接删除
  97. delete pFindNode;
  98. // 将指针设置为空,由于pFindNode是一个指针
  99. // 的引用, 因此它修改的是哪里????(实际上修改的是
  100. // 父节点的右子树指针或左子树指针)
  101. pFindNode = nullptr;
  102. // 递减节点个数
  103. --m_nCount;
  104. return true;
  105. }
  106. // 3. 如果不是叶子节点. 就需要在被删除节点的
  107. // 子树中找到一个叶子节点, 然后两者的值
  108. // 互换, 再删除掉叶子节点
  109. // 3.1 再找叶子节点的时候, 需要考虑子树高度差
  110. // 因此会在树高比较高的子树中找叶子节点.
  111. int nLeftHeight = getHeight(pFindNode->pLeft);
  112. int nRightHeight = getHeight(pFindNode->pRight);
  113. // 3.2 树中左小右大的关系, 因此, 需要左子树中
  114. // 找最大值, 在右子树中找最小值.
  115. if (nLeftHeight >= nRightHeight) {
  116. // 左子树比较高,需要找最大值
  117. Node*& pNode = getMax(pFindNode->pLeft);
  118. // 将当前被找到的节点和叶子节点的数据域
  119. // 进行调换
  120. int t = pFindNode->nData;
  121. pFindNode->nData = pNode->nData;
  122. pNode->nData = t;
  123. // 删除叶子节点
  124. // 递归删除.
  125. isRemove = remove(pFindNode->pLeft, pNode->nData);
  126. }
  127. else {
  128. // 右子树比较高,需要找最小值
  129. Node*& pNode = getMin(pFindNode->pRight);
  130. int t = pFindNode->nData;
  131. pFindNode->nData = pNode->nData;
  132. pNode->nData = t;
  133. // 删除叶子节点
  134. // 递归删除.
  135. isRemove = remove(pFindNode->pRight, pNode->nData);
  136. }
  137. // 如果删除成功,就检测树是否平衡
  138. if (isRemove) {
  139. sort(pTree);
  140. }
  141. return isRemove;
  142. }
  143. /*!
  144. * 函数名 : getHeight
  145. * 功 能 : 求一个树的高度
  146. * 参 数 : Node * pTree
  147. * 返回值 : int
  148. */
  149. int getHeight(Node* pTree) {
  150. if (pTree == nullptr) {
  151. return 0;
  152. }
  153. // 求出左子树的高度
  154. int nLeftHeight =
  155. getHeight(pTree->pLeft);
  156. // 求出右子树的高度
  157. int nRightHeight =
  158. getHeight(pTree->pRight);
  159. // 取左右子树高度的最大值作为子树的高度
  160. int nHeight = nLeftHeight > nRightHeight ? nLeftHeight : nRightHeight;
  161. // 本节点的高度 = 自身高度 + 子树的高度
  162. return 1 + nHeight;
  163. }
  164. Node*& getMin(Node*& pTree) {
  165. if (pTree == nullptr) {
  166. return pTree;
  167. }
  168. // 二叉树是左小右大的.
  169. // 一个树的最小值就在树的最左边
  170. if (pTree->pLeft == nullptr) {
  171. // 如果本节点的左子节点已经为空了
  172. // 说明了本节点就是树中最左边的节点
  173. // 就返回节点自身.
  174. return pTree;
  175. }
  176. return getMin(pTree->pLeft);
  177. }
  178. Node*& getMax(Node*& pTree) {
  179. if (pTree == nullptr) {
  180. return pTree;
  181. }
  182. // 二叉树是左小右大的.
  183. // 一个树的最大值就在树的最右边
  184. if (pTree->pRight == nullptr) {
  185. // 如果本节点的右子节点已经为空了
  186. // 说明了本节点就是树中最右边的节点
  187. // 就返回节点自身.
  188. return pTree;
  189. }
  190. return getMax(pTree->pRight);
  191. }
  192. // 清空整颗树(后序遍历
  193. void clear(Node*& pTree) {
  194. clear(pTree->pLeft);
  195. clear(pTree->pRight);
  196. delete pTree;
  197. }
  198. // 前序方式遍历树
  199. void printTreeByPreOrder(Node*& pTree) {
  200. if (pTree == nullptr) {
  201. return;
  202. }
  203. printf("%d,", pTree->nData);
  204. printTreeByPreOrder(pTree->pLeft);
  205. printTreeByPreOrder(pTree->pRight);
  206. }
  207. // 中序方式遍历树
  208. void printTreeByInOrder(Node*& pTree) {
  209. if (pTree == nullptr) {
  210. return;
  211. }
  212. printTreeByInOrder(pTree->pLeft);
  213. printf("%d,", pTree->nData);
  214. printTreeByInOrder(pTree->pRight);
  215. }
  216. // 后序方式遍历树
  217. void printTreeByPostOrder(Node*& pTree) {
  218. if (pTree == nullptr) {
  219. return;
  220. }
  221. printTreeByPostOrder(pTree->pLeft);
  222. printTreeByPostOrder(pTree->pRight);
  223. printf("%d,", pTree->nData);
  224. }
  225. void printTree(Node*& pTree, int nWidth, char ch) {
  226. if (pTree == nullptr) {
  227. return;
  228. }
  229. printTree(pTree->pLeft, nWidth + 2, '/');
  230. printf("%*c%d\n", nWidth, ch, pTree->nData);
  231. printTree(pTree->pRight, nWidth + 2, '\\');
  232. }
  233. /*!
  234. * 函数名 : sortLeft
  235. * 功 能 : 将一颗右单斜的树进行左旋,然后使其平衡
  236. * 参 数 : Node * & pTree
  237. * 返回值 : void
  238. */
  239. void sortLeft(Node*& K) {
  240. /*
  241. * K(7)
  242. * \ M(9)
  243. * M(9) ( \
  244. * / \ K(7) N(10)
  245. * L(8) N(10) )
  246. * L(8)
  247. *
  248. * K->pRight = L
  249. * M->pLeft = K
  250. * K = M
  251. */
  252. Node* M = K->pRight;
  253. Node*L = M->pLeft;
  254. Node* N = M->pRight;
  255. K->pRight = L;
  256. M->pLeft = K;
  257. K = M;
  258. }
  259. /*!
  260. * 函数名 : sortRight
  261. * 功 能 : 将一颗左单斜的树进行左旋,然后使其平衡
  262. * 返回值 : void
  263. */
  264. void sortRight(Node*& K) {
  265. /*
  266. * K(6) M(4)
  267. * / / )
  268. * M(4) N(2) K(6)
  269. * / \ (
  270. * N(2) R(5) R(5)
  271. *
  272. * K->pLeft = R
  273. * M->pRight = K
  274. * K = M
  275. */
  276. Node* M = K->pLeft;
  277. Node* N = M->pLeft;
  278. Node* R = M->pRight;
  279. K->pLeft = R;
  280. M->pRight = K;
  281. K = M;
  282. }
  283. /*!
  284. * 函数名 : sortLeftRight
  285. * 功 能 : 先左旋再右旋,将右歪斜树变成平衡树
  286. * 参 数 : Node * &
  287. * 返回值 : void
  288. */
  289. void sortRightLeft(Node*& K ) {
  290. /*
  291. * K(6) K(6) N(7)
  292. * \ \ / \
  293. * M(8) N(7) K(6) M(8)
  294. * / \
  295. * N(7) M(8)
  296. * /
  297. * ??
  298. * 1. 以M作为顶点. 将M形成树视为左单斜树,使用右单旋使其平衡
  299. * 2. 以K作为顶点, 将K形成的是右单斜树, 使用左单旋使其平衡
  300. */
  301. sortRight(K->pRight);
  302. sortLeft(K);
  303. }
  304. void sortLeftRight(Node*& K) {
  305. sortLeft(K->pLeft);
  306. sortRight(K);
  307. }
  308. void sort(Node*& pTree) {
  309. // 1. 判断树的形状:
  310. int nLeftHeight = getHeight(pTree->pLeft);
  311. int nRightHeight = getHeight(pTree->pRight);
  312. if (nLeftHeight - nRightHeight > 1) {
  313. // 1.1 左斜
  314. nLeftHeight = getHeight(pTree->pLeft->pLeft);
  315. nRightHeight = getHeight(pTree->pLeft->pRight);
  316. if (nLeftHeight > nRightHeight) {
  317. // 1.1.1 左单斜
  318. sortRight(pTree);
  319. }
  320. else {
  321. // 1.1.2 左歪斜
  322. sortLeftRight(pTree);
  323. }
  324. }
  325. else if (nLeftHeight - nRightHeight < -1) {
  326. // 1.2 右斜
  327. nLeftHeight = getHeight(pTree->pRight->pLeft);
  328. nRightHeight = getHeight(pTree->pRight->pRight);
  329. if (nRightHeight > nLeftHeight) {
  330. // 1.2.1 右单斜
  331. sortLeft(pTree);
  332. }
  333. else {
  334. // 1.2.2 右歪斜
  335. sortRightLeft(pTree);
  336. }
  337. }
  338. }
  339. public:
  340. void insert(int nData) {
  341. insert(m_pRoot, nData);
  342. }
  343. void remove(int nData) {
  344. remove(m_pRoot, nData);
  345. }
  346. void printTree() {
  347. printTree(m_pRoot, 2, '*');
  348. printf("\n");
  349. }
  350. };
  351. struct MyStruct {
  352. int nNum = 0;
  353. const char* pStr;
  354. };
  355. const char*& fun(const char*& p) {
  356. return p;
  357. }
  358. int main() {
  359. MyStruct stc = { 0, "hello" };
  360. const char*& p = fun(stc.pStr);
  361. p = "呵呵";
  362. AVLTree tree;
  363. // tree.insert(10);
  364. // tree.insert(5);
  365. // tree.insert(20);
  366. // tree.insert(7);
  367. // tree.insert(6);
  368. // tree.printTree();
  369. //
  370. //
  371. // tree.remove(10);
  372. // tree.printTree();
  373. // 测试平衡性
  374. for (int i = 0; i<10;++i)
  375. {
  376. tree.insert(i);
  377. tree.printTree( );
  378. }
  379. printf("删除\n");
  380. for (int i = 0; i < 10; ++i) {
  381. tree.remove(i);
  382. printf("删除并旋转之后:\n");
  383. tree.printTree();
  384. }
  385. }