文章目录
- [第7章 非线性数据结构](#第7章 非线性数据结构)
-
- 1.C++的内存模型(内存空间、内存布局、内存分配方式)
-
- (1)栈区
- (2)堆区 (自由存储区)
- (3)指针和引用
-
- [①指针(间接访问 值传递)](#①指针(间接访问 值传递))
- ②引用(引用传递)
- 2.二叉树
-
- (1)二叉树的存储
- (2)二叉树的遍历
- (3)例题
-
- 例题1:二叉树遍历(难度:中等)
- [例题2:二叉树遍历/重建二叉树 (难度:中等)](#例题2:二叉树遍历/重建二叉树 (难度:中等))
- [3.二叉排序树 BST](#3.二叉排序树 BST)
-
- (1)BST的建立 (双指针法)
- (2)例题
-
- [例题1:二叉排序树 (难度:简单-中等)](#例题1:二叉排序树 (难度:简单-中等))
- [例题2:二叉搜索树 (难度:中等-困难)](#例题2:二叉搜索树 (难度:中等-困难))
- [4.优先队列 priority_queue](#4.优先队列 priority_queue)
- [5.散列表 map](#5.散列表 map)
-
- [(1)映射 map](#(1)映射 map)
- (2)例题
- [(3)unorder_map 哈希表](#(3)unorder_map 哈希表)
- [第8章 搜索](#第8章 搜索)
第7章 非线性数据结构
1.C++的内存模型(内存空间、内存布局、内存分配方式)
1.bss段
未初始化数据段。不保存在硬盘上,只是记录数据所需空间的大小,程序开始执行之前,由内核进行初始化为0
2.data段/数据段/常量区
①初始化的字段,包含明确的初始化值,保存在硬盘上,由.exec读取
②初始化之后的全局变量,静态变量和常量数据,直到程序结束才会被回收
3.text段
存放文本指令的地方,保存在硬盘上,只读的,不可修改,由.exec程序读取。可共享,若一个程序有多个进程同时在运行,则可共享text段
4.用户栈、运行时堆
上面高地址,下面低地址
①栈:0x7ff
②堆:0x5
栈往下涨,堆往上涨
栈底其实是在天花板上,堆底在下面
这样,栈和堆有一部分空间可以共用,谁需要就谁占用。栈和堆对着长。栈在上,往下长。堆在下,往上涨。
计算机中存放地址都是用16进制
0x38到0x40中间是8B,不是2B:38 39 3a 3b 3c 3d 3e 3f 40
(1)栈区
栈帧。当被调函数执行结束,栈帧上的变量也会被自动回收。
调用,压栈(在栈中压入一个栈帧)
栈帧后进先出,调用函数时压栈,函数返回时弹栈。
(2)堆区 (自由存储区)
new出来的,不会自动销毁。需要手动回收,否则会发生内存泄漏。
(3)指针和引用
①指针(间接访问 值传递)
1.*
取内容(间接访问)
&
取地址
3.->
运算符
若p为指针,则(*p).member
与p->member
等价
②引用(引用传递)
引用,&x,&y就是给变量x,y起了个别名,并没有新开辟内存存储空间。这样,被调函数的参数列表(int &x, int &y)
就可以直接引用传递给主函数中的变量x,y。因为&x &y与x y 享有同一个内存地址,会直接修改。
C++引用,底层是靠指针实现的
C++的两种传递方式:值传递、引用传递
例1:变量名-直接访问-值传递:在被调函数中无法修改主调函数中参数的内容。尽管变量名相同,但不是同一个地址
cpp
#include <cstdio>
void swap1(int x,int y){
int temp = x;
x = y;
y = temp;
}
int main(){
int x = 1 ,y = 2;
swap1(x,y);
}
结果是,在swap1中x与y的值互换了,但main中的x与y的值不变。因为swap1中和main中的x、y的地址不同。
例2:指针-间接访问-值传递
在被调函数里改变主调函数中变量的内容:传指针(间接访问)
在普通函数中想要改变main函数中的值,要传指针。值才能透出去。
cpp
#include <cstdio>
void swap2(int *px,int *py){
int temp = *px;
*px = *py;
*py = temp;
}
int main(){
int x = 1 ,y = 2;
int *px = &x;
int *py = &y;
swap2(px,py);
}
例3:引用-引用传递
对比例1,只修改了被调函数的参数列表,将int x,int y改为 int &x,int &y
cpp
#include <cstdio>
void swap3(int &x,int &y){
int temp = x;
x = y;
y = temp;
}
int main(){
int x = 1 ,y = 2;
swap3(x,y);
}
2.二叉树
1 << i:1左移i位,为2的i次方
cpp
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int i = pow(2,5); //2的5次方
cout << "i = " << i << endl;
int j = 1 << 5; //2的5次方
cout << "j = " << j << endl;
return 0;
}
(1)二叉树的存储
顺序存储
链式存储
(2)二叉树的遍历
①递归遍历:前中后序遍历 (深度优先)
②层次遍历:队列 (广度优先)
③重建二叉树,并遍历
④申请一个新的树结点
(3)例题
例题1:二叉树遍历(难度:中等)
提交网址:https://www.nowcoder.com/share/jump/2891302591709467187427
题目要求:根据有#的先序遍历序列,构建二叉树,并输出中序遍历序列 (清华复试上机真题)
24机试指南炉灰老师
关键思路:利用分治法,递归建树
cpp
#include <iostream>
#include <string>
using namespace std;
struct TreeNode{
char data;
struct TreeNode *left,*right;
};
TreeNode * RecursiveBuildTree(int &i,string str){
char c = str[i];
++i;
if(c == '#'){
return NULL;
}else{
TreeNode *newNode = new TreeNode;
newNode->data = c;
newNode->left = RecursiveBuildTree(i,str);
newNode->right = RecursiveBuildTree(i,str);
return newNode;
}
}
void InOrder(TreeNode *T){
if(T != NULL){
InOrder(T->left);
cout << T->data <<" ";
InOrder(T->right);
}
}
int main() {
string str;
while(cin >> str){
int i = 0;
TreeNode *root = RecursiveBuildTree(i,str);
InOrder(root);
cout << endl;
}
return 0;
}
例题2:二叉树遍历/重建二叉树 (难度:中等)
提交网址:http://t.cn/AiKgDfLU
题目要求:通过先序序列和中序序列建立二叉树,再输出后序遍历序列(华科复试上机真题)
思路:利用二叉树左右子树的特性,分治法递归重新建树
C++版本:
cpp
#include <iostream>
#include <string>
using namespace std;
struct TreeNode{
char data;
TreeNode *left,*right;
};
//递归重建二叉树
TreeNode *ReBuild(string PreOrder,string InOrder){
if(PreOrder.size() == 0) return NULL;
char rootdata = PreOrder[0];
TreeNode *newNode = new TreeNode; //申请一个新的树结点
newNode->data = rootdata; //数据域
int pos = InOrder.find(rootdata); //pos为中序遍历中根结点的位置
//左子树: 左子树先序遍历序列:PreOrder.substr(1,pos) 左子树中序遍历序列:InOrder.substr(0,pos)
//右子树: 右子树先序遍历序列:PreOrder.substr(pos+1) 右子树中序遍历序列:InOrder.substr(pos+1)
newNode->left = ReBuild(PreOrder.substr(1,pos),InOrder.substr(0,pos));
newNode->right = ReBuild(PreOrder.substr(pos+1),InOrder.substr(pos+1));
return newNode;
}
//后序遍历
void PostOrder(TreeNode *T){
if(T != NULL){
PostOrder(T->left);
PostOrder(T->right);
cout << T->data;
}
}
int main() {
string PreOrder,InOrder;
while(cin >> PreOrder >> InOrder){
TreeNode * root = ReBuild(PreOrder,InOrder);
PostOrder(root);
cout << endl;
}
return 0;
}
C语言版本:
cpp
#include <cstdio>
#include <string>
using namespace std;
struct TreeNode{
char data;
TreeNode * leftChild;
TreeNode * rightChild;
};
TreeNode *rebuild(string preOrder,string inOrder){ //返回值为子树根结点的地址
if(preOrder.size() == 0){
return NULL;
}else{
//从先序遍历确定根
char rootdata = preOrder[0];
TreeNode *pNewNode = new TreeNode;//二叉树结点指针
pNewNode->data = rootdata;
//用根位置切割中序
int pos = inOrder.find(rootdata);//pos是根在中序序列中出现的下标
pNewNode->leftChild = rebuild(preOrder.substr(1,pos),inOrder.substr(0,pos));
pNewNode->rightChild = rebuild(preOrder.substr(pos+1),inOrder.substr(pos+1));
return pNewNode;
}
}
void PostOrder(TreeNode * root){
if(root == NULL){
return;
}
PostOrder(root->leftChild);
PostOrder(root->rightChild);
printf("%c",root->data);
return;
}
int main(){
char preOrder[30];
char inOrder[30];
while(scanf("%s%s",preOrder,inOrder) != EOF){
TreeNode * root = rebuild(preOrder,inOrder);
PostOrder(root);
printf("\n");
}
}
3.二叉排序树 BST
(1)BST的建立 (双指针法)
二叉排序树(二叉搜索树,二叉查找树,Binary Search Tree,BST) 的建立。
current、parent 双指针法
cpp
#include <iostream>
using namespace std;
struct BinaryTree{
int data;
BinaryTree *left,*right;
};
//BST建树,插入结点
void InsertBinary(BinaryTree * &root,int data){
BinaryTree *Node = new BinaryTree;
Node->data = data,Node->left = NULL,Node->right = NULL;
if(root == NULL){
root = Node;
return;
}else{
BinaryTree *current = root, *parent = NULL;
while(current != NULL){
parent = current;
if(data < current->data){
current = current->left;
}else{
current = current->right;
}
}
//跳出while,说明current为空,将新结点Node插入
if(data < parent->data){
parent->left = Node;
}else{
parent->right = Node;
}
return;
}
}
int main() {
BinaryTree *root = NULL;
int data;
int n;
cin >> n;
for(int i = 0; i < n; ++i){
cin >> data;
InsertBinary(root,data);
}
return 0;
}
(2)例题
例题1:二叉排序树 (难度:简单-中等)
华科 KY207 二叉排序树
提交网址:http://t.cn/Ai9PAkkv
提交网址:https://www.acwing.com/problem/content/3598/
1.Edward非递归版本:双指针法 (current,parent)
cpp
#include <iostream>
using namespace std;
struct BinaryTree{
int data;
BinaryTree *left,*right;
};
void InsertBST(BinaryTree * &root,int data){
//申请一个新的树结点,并初始化
BinaryTree * Node = new BinaryTree;
Node->data = data;
Node->left = NULL;
Node->right = NULL;
//对传进来的结点进行判断
if(root == NULL){ //第一次传进来,是根节点
cout << -1 << endl;
root = Node; //修改根节点
return;
}else{
//双指针法
BinaryTree * current = root;
BinaryTree * parent = NULL;
while(current != NULL){
parent = current; //下移前,cur将值保存给parent
if(current->data > data){ //当前结点值 大于 要插入的结点值
current = current->left; //向左走
}else{
current = current->right;
}
}
//curent走到空,将新结点插入
if(data < parent->data){
parent->left = Node;
}else{
parent->right = Node;
}
cout << parent->data << endl;
return;
}
}
int main(){
int n;
cin >> n;
int data;
BinaryTree * root = NULL;
for(int i = 0; i < n; ++i){
cin >> data;
InsertBST(root,data);
}
return 0;
}
2.2024泥鳅老师版本(我觉得while、if、else部分太冗长,代码结构不清晰)
cpp
#include <iostream>
using namespace std;
struct TreeNode{
int data;
TreeNode *left,*right;
};
//二叉树结点的插入
void InsertBST(TreeNode * &proot,int data){
TreeNode *pNew = new TreeNode; //申请一个新结点
pNew->data = data;
pNew->left = NULL;
pNew->right = NULL;
if(proot == NULL){
proot = pNew;
cout << -1 << endl;
}else{
TreeNode *pCur = proot; //pCur向下探索
TreeNode *pPre; //pPre指向pCur的父结点
while(1){
if(pCur->data > data){ //当前结点值 大于 要插入的结点值
pPre = pCur;
pCur = pCur->left; //向左走
if(pCur == NULL){
pPre->left = pNew; //左孩子置为新结点
cout << pPre->data << endl;
break;
}
}else{ //当前结点值 小于 要插入的结点值
pPre = pCur;
pCur = pCur->right; //往右走
if(pCur == NULL){
pPre->right = pNew;
cout << pPre->data << endl;
break;
}
}
}
}
}
int main() {
int n;
cin >> n;
TreeNode * proot = NULL;
int data;
for(int i = 0; i < n; ++i){
cin >> data;
InsertBST(proot,data);
}
return 0;
}
例题2:二叉搜索树 (难度:中等-困难)
浙大 判断是否为同一个二叉搜索树序列
提交网址:http://t.cn/Ai9PUJtK
思路:①BST建树 ②比较前序遍历、中序遍历
(思路比较简单,处理数据很麻烦)
cpp
//判断两BST是否相同
#include <iostream>
#include <string>
using namespace std;
struct BinaryTree{
char data;
BinaryTree *left,*right;
};
//BST建树,插入结点
void InsertBinary(BinaryTree * &root,int data){
BinaryTree *Node = new BinaryTree;
Node->data = data,Node->left = NULL,Node->right = NULL;
if(root == NULL){
root = Node;
return;
}else{
BinaryTree *current = root, *parent = NULL;
while(current != NULL){
parent = current;
if(data < current->data){
current = current->left;
}else{
current = current->right;
}
}
//跳出while,说明current为空,将新结点Node插入
if(data < parent->data){
parent->left = Node;
}else{
parent->right = Node;
}
return;
}
}
//前序遍历
void PreOrder(BinaryTree *root,string &preorder){
if(root != NULL){
preorder += root->data;
PreOrder(root->left,preorder);
PreOrder(root->right,preorder);
}
}
//中序遍历
void InOrder(BinaryTree *T,string &inorder){
if(T != NULL){
InOrder(T->left,inorder);
inorder += T->data;
InOrder(T->right,inorder);
}
}
int main() {
char data;
int n;
while(cin >> n){
if(n == 0) break;
//读取字符串,建树
string str;
cin >> str;
BinaryTree *root = NULL;
for(int i = 0; i < str.size();++i){
InsertBinary(root,str[i]);
}
//原树的先序,中序
string preorder1 = "",inorder1 = "";
PreOrder(root,preorder1);
InOrder(root,inorder1);
//n次对比
for(int i = 0; i < n; ++i){
string str2;
cin >> str2;
BinaryTree *root2 = NULL;
for(int i = 0; i < str2.size();++i){ //分别建树
InsertBinary(root2,str2[i]);
}
string preorder2 = "",inorder2 = "";
PreOrder(root2,preorder2);
InOrder(root2,inorder2);
if(preorder1 == preorder2 && inorder1 == inorder2) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
return 0;
}
4.优先队列 priority_queue
(1)概念
1.堆
优先队列不是队列(queue)而是堆(heap),是二叉堆(大根堆、小根堆),结构是完全二叉树,用数组保存。
2.大根堆
优先队列默认为大根堆。
特点:
①队列:先进先出
②优先队列:出最大的
cpp
priority_queue<typename> name;
3.小根堆 (重新定义优先队列)
cpp
priority_queue <typename,vector<typename>, greater<typename> > name
(2)优先队列模板 STL-priority_queue
1.定义
cpp
#include <queue>
using namespace std;
priority_queue<typename>name;
2.增删元素
①入队 :插入元素 push()
②出队 :删除元素,出最大值 pop()
3.元素访问
队首 :只能访问优先级最高的元素 top()
4.状态
①队空 :是否为空 empty()
②队长 :元素个数 size()
5.测试
cpp
#include <iostream>
#include <queue>
using namespace std;
int main(){
int arr[6] = {2,4,6,1,3,5};
priority_queue<int> myPQ;
for(int i = 0; i < 6; ++i){
myPQ.push(arr[i]);
cout << "top = " << myPQ.top() << endl;
}
cout << "-------------------" << endl;
while(!myPQ.empty()){
cout << "pop,top = " << myPQ.top() << endl;
myPQ.pop();
}
return 0;
}
(3)例题
例题1:复数集合 (难度:中等)
提交网址:http://t.cn/Ai98yYlt
注意:1+i2,应该用scanf读取 ,很难用cin读取。scanf("%d+i%d", &re, &im);
cpp
#include <iostream>
#include <queue>
#include <cmath>
#include <string>
using namespace std;
//复数
struct Complex{
//数据成员
int realpart;
int imaginarypart;
//含参构造函数,简化初始化过程
Complex(int re,int im){
realpart = re;
imaginarypart = im;
}
};
//重载运算符
bool operator < (Complex lhs,Complex rhs){
return pow(lhs.realpart,2) + pow(lhs.imaginarypart,2) < pow(rhs.realpart,2)+pow(rhs.imaginarypart,2);
}
int main() {
int n;
cin >> n;
priority_queue<Complex> myPQ;
for(int i = 0; i < n; ++i){
string action;
cin >> action;
if(action == "Pop"){
if(myPQ.empty()){
cout << "empty" << endl;
}else{
cout << myPQ.top().realpart <<"+i" << myPQ.top().imaginarypart << endl;
myPQ.pop();
cout << "SIZE = " << myPQ.size() << endl;
}
}else if(action == "Insert"){
int re,im;
//cin >> re >> im; //注意,读取 1+i2,应该用scanf控制格式
scanf("%d+i%d", &re, &im);
Complex c(re,im);
myPQ.push(c);
cout << "SIZE = "<<myPQ.size() << endl;
}
}
return 0;
}
提交网址:https://www.acwing.com/problem/content/3542/
多加了一个条件,模相等时,输出较小的虚数
cpp
//重载运算符
bool operator < (Complex lhs,Complex rhs){
if(pow(lhs.realpart,2) + pow(lhs.imaginarypart,2) == pow(rhs.realpart,2)+pow(rhs.imaginarypart,2)){
return lhs.imaginarypart > rhs.imaginarypart; //模相等,输出较小的虚数
}else{
return pow(lhs.realpart,2) + pow(lhs.imaginarypart,2) < pow(rhs.realpart,2)+pow(rhs.imaginarypart,2);
}
}
例题2:哈夫曼树(难度:简单)
哈夫曼树
1.求WPL(带权路径和)
①构建哈夫曼树,再求WPL:已掌握
②迭代式求WPL
例题:哈夫曼树 (北邮复试上机真题)
提交网址:http://t.cn/AiCuGMki
提交网址:https://www.acwing.com/problem/content/description/3534/
思路:
①用优先队列(取相反数)凑成小根堆
②哈夫曼树WPL = Σ叶结点权值×边数 = 非叶结点权值之和 = 除根结点外所有结点的权值之和
cpp
#include <iostream>
#include <queue>
using namespace std;
int main() {
priority_queue<int> myPQ; //大根堆变小根堆:存储权值的相反数
int n,weight;
cin >> n;
for(int i = 0; i < n; ++i){
cin >> weight;
myPQ.push(-1 * weight);
}
int WPL = 0;
while(myPQ.size() >= 2){
int leaf1 = myPQ.top();
myPQ.pop();
int leaf2 = myPQ.top();
myPQ.pop();
WPL += leaf1 + leaf2;
myPQ.push(leaf1 + leaf2);
}
cout << -1*WPL << endl;
return 0;
}
5.散列表 map
(1)映射 map
1.map底层:红黑树 (RBT∈BST),
2.特点:所以map会自动排好序 (BST中序遍历是升序序列)。
3.map查找:时间复杂度 O(logn) (和二分查找相同的时间复杂度,但比二分查找需要更多的空间)
4.操作
(1)头文件
cpp
#include <map>
using namespace std;
定义
cpp
map<string,int> myMap;
初始化:
cpp
map<string,int> myMap = {
{"Caixukun",1},
{"Wuyifan",2},
{"Liyifeng",3}
};
(2)插入键值对
①insert()
方法
cpp
myMap.insert(pair<string,int>("Wuyifan",2));
pair头文件
#include <utility>
②[]
下标访问运算符
cpp
myMap["Caixukun"] = 1;
删除键值对:erase()
cpp
myMap.erase("Wuyifan");
读取键对应的值:
(3)判空:empty()
求长度(获取键值对个数):size()
(4)map的遍历:迭代器 iterator (迭代器是指向内部元素的指针)
①定义迭代器:map<string,int>::iterator it;
②第一个成员的位置:.begin()
③尾后位置:.end()
④迭代器指针后移:++it
(++it使得迭代器指针指向下一对键值对,unorder_map不支持此操作)
⑤键:it->first
⑥值:it->second
cpp
map<string,int>::iterator it;
for(it = myMap.begin();it != myMap.end();++it){
printf("%s %d\n",it->first.c_str(),it->second);
}
(5)查找某个键是否存在:find()
函数
(6)map从大到小排序
map默认从小到大排序,若要使得map降序排序,需要加入greator<key数据类型>
cpp
map<int,string,greater<int>> myMap;
(7)map[i]++
①若map中没有key为i的值,则先insert <i,0>,然后value++。最终map中存入<i,1>。
②若map中已有key = i,则value++
cpp
#include <cstdio>
#include <map>
#include <iostream>
using namespace std;
int main() {
map<int, int> myMap;
for (int i = 0; i < 5; i++) {
myMap[i]++;
}
for (auto it = myMap.begin(); it != myMap.end(); it++) {
cout << '<'<< it->first << ',' << it->second <<'>'<< endl;
}
cout<<"第二轮"<<endl;
for (int i = 0; i < 5; i++) {
myMap[i]++;
}
for (auto it = myMap.begin(); it != myMap.end(); it++) {
cout << '<'<< it->first << ',' << it->second <<'>'<< endl;
}
return 0;
}
(8)map举例:
cpp
#include <cstdio>
#include <string>
#include <map>
using namespace std;
int main(){
//键 key --> 值 value
//<键的类型,值的类型>
map<string,string> myMap = {
{"Caixukun","ikun"},
{"Wuyifan","meigeni"}
};
char str[1000];
scanf("%s",&str);
string name = str;
printf("%s的粉丝被称为%s",name.c_str(),myMap[name].c_str());
}
(2)例题
例题1:查找学生信息
例题1:查找学生信息 (清华大学复试上机题)
提交网址:http://t.cn/AiCuVIuY
cpp
#include <cstdio>
#include <string>
#include <map>
using namespace std;
struct Student{
string name;
string gender;
int age;
};
int main(){
int n;
scanf("%d",&n);
map<string,Student> InfoMap;
for(int i = 0; i < n; ++i){
char num[30];
char name[30];
char gender[30];
int age;
scanf("%s %s %s %d",num,name,gender,&age);
//key string
string numstr = num;
//value Student
Student student;
student.name = name;
student.gender = gender;
student.age = age;
InfoMap[numstr] = student;
}
int m;
scanf("%d",&m);
for(int i = 0; i < m; ++i){
char num[30];
scanf("%s",num);
string numstr = num;
if(InfoMap.find(numstr) != InfoMap.end()){//找到了
printf("%s %s %s %d\n",numstr.c_str(),
InfoMap[numstr].name.c_str(),
InfoMap[numstr].gender.c_str(),
InfoMap[numstr].age);
}else{//学号没找到
printf("No Answer!\n");//这里漏加换行符\n,导致格式错误了好几次。
}
}
}
例题2:魔咒词典
例题2:魔咒词典 (浙大上机复试真题)
提交网址:http://t.cn/AiCufczt
cpp
#include <cstdio>
#include <string>
#include <map>
using namespace std;
//魔咒词典 (采用 增量编辑法:即写一部分,测试一部分)
int main(){
map<string,string> dict;
while(true){
char line[200];
fgets(line,200,stdin);//输入一行
string linestr = line;
linestr.pop_back();//干掉回车,string最后一个字符为\n
if("@END@" == linestr){
break;
}
// printf("%s\n",linestr.c_str());
int pos = linestr.find("]");
string word = linestr.substr(0,pos+1);
string info = linestr.substr(pos+2);
// printf("word = %s, info = %s\n",word.c_str(),info.c_str());
dict[word] = info;
dict[info] = word;
}
int n;
scanf("%d",&n);
getchar();//吃掉回车 ※
for(int i = 0 ; i < n; ++i){
char line[200];
fgets(line,200,stdin);
string linestr = line;
linestr.pop_back();
if(dict.find(linestr) != dict.end()){//存在魔咒或功能
if('[' == linestr[0]){//输入魔咒,查找功能
printf("%s\n",dict[linestr].c_str());
}else{//输入功能查找魔咒
string charm = dict[linestr];//[xxxxxx],想办法去掉[]
string charm_sub = charm.substr(1,charm.size()-2);//取子串,去掉了[]
printf("%s\n",charm_sub.c_str());
}
}else{//不存在该魔咒或功能
printf("what?\n");
}
}
}
(3)unorder_map 哈希表
1.底层:HashTable 散列表
2.unorder_map查找 :时间复杂度O(1)
3.特点:①空间特别大 ②无序,不能遍历
第8章 搜索
搜索问题,就是带有限制的枚举问题。
1.BFS 广度优先搜索
(1)概念
1.名词解释:
BFS,Breadth-First-Search,广度优先搜索(宽度优先搜索)
2.方法:
广度优先搜索,类似层序遍历,把距离为1的结点都访问一遍才走下一层。稳扎稳打,瞻前顾后
3.数据结构:队列
4.目的:为了求最优解问题
(2)例题
例题1:树的高度
提交网址:https://www.acwing.com/problem/content/3702/ [南京理工大学复试上机题]
例题2:玛雅人的密码 (BFS)
提交网址:https://www.acwing.com/problem/content/3388/
提交网址:http://t.cn/Ai0lUhJj
答案:
例题3:Catch that Cow
例题4:Find the Multiple
(3)打表
当OJ时限很小,且输入范围有限(1≤n≤200)
还是上文例题2:Find the Multiple,用打表法解决
2.DFS 深度优先搜索
(1)概念
1.概念:
DFS,Depth-First-Search,深度优先搜索。
2.目的:
判断问题是否有解,例如找迷宫出口。不拥有最优解的特性。
3.实现:
①递归 实现,不需要数据结构。因此DFS代码比BFS代码要简单一些。
②DFS也可以用栈实现
4.特点:
一条路走到黑,撞到南墙才回头
(2)例题
例题1:八皇后问题
提交网址:https://www.acwing.com/problem/content/3475/
例题2:数组划分
提交网址:https://www.acwing.com/problem/content/3506/
答案:
剪枝答案:
例题3:A Knight's Journey
例题4:Square
(3)剪枝算法
在DFS搜索过程中,可以通过放弃对某些不可能产生结果的子集的搜索,以提高效率。这样的算法称为剪枝。