第二章
我觉得此章节的数据结构可以实现一下
栈
数组实现栈
#include<iostream>
using namespace std;
struct stack
{
int top;//下一个应该放的元素位置
int mCapacity;
int *p;
stack(int capacity)
{
mCapacity = capacity;
p = new int[mCapacity];
top = 0;
}
int pop()
{
if (top==0)
{
cout << "栈是空的" << endl;
}
else
{
top--;//默认下一个位置就是待插入元素就可以了,并没有实际删除
return p[top];
}
return -1;
}
int push(int x)
{
int ret = -1;
if (top>=mCapacity)
{
cout << "已经满了" << endl;
}
else
{
p[top] = x;
top++;
ret = x;
}
return ret;
}
int gettop()
{
if (top==0)
{
return -1;
}
else
{
return p[top-1];
}
}
int getsize()
{
return top;
}
};
int main()
{
stack s(5);
/*for (int i=0;i<5;i++)
{
s.push(i);
cout << "size()="<< s.getsize() << endl;
}
for (int i = 0;i <10;i++)
{
cout << "pop()=" << s.pop() << endl;
}*/
//cout << "size()=" << s.getsize() << endl;
for (int i =3;i < 5;i++)
{
s.push(i);
cout << "gettop()=" << s.gettop() << endl;
//cout << "size()=" << s.getsize() << endl;
}
}
链表实现栈
#include<iostream>
using namespace std;
//只能实现头插法,才能用链表
struct Node
{
int data;
Node *next;
Node()
{
data = 0;
next = nullptr;
}
Node(int x)
{
data =x;
next = nullptr;
}
};
struct linStack
{
Node * dummpy;
linStack()
{
dummpy = new Node();
}
int pop()
{
if (dummpy->next==NULL)//只有一个空节点,
{
cout << "栈现在是空的" << endl;
return -1;
}
else//注意一定要采用头插法
{
int ret = dummpy->next->data;
dummpy->next = dummpy->next->next;
return ret;
}
}
int push(int x)
{
Node * newnode = new Node(x);
newnode->next = dummpy->next;
dummpy->next = newnode;
return -1;
}
int getTop()
{
if (dummpy->next!=nullptr)
{
return dummpy->next->data;
}
else
{
return -1;
}
}
int getsize()
{
Node * p = dummpy->next;
int count = 0;
while (p != nullptr)
{
count++;
p = p->next;
}
return count;
}
};
int main()
{
linStack s;
for (int i=0;i<5;i++)
{
s.push(i);
//cout << "getsize=" << s.getsize() << endl;
}
for (int i = 0;i < 10;i++)
{
cout << "pop()=" << s.pop() << endl;
}
for (int i = 3;i < 10;i++)
{
s.push(i);
cout << "s.top()=" << s.getTop() << endl;
}
}
队列:
数组实现循环队列
#include<iostream>
#include<algorithm>
using namespace std;
struct quque
{
int mCapacity;
int *p;
int head ;
int tail;
quque(int capacity)
{
mCapacity = capacity;
p = new int[mCapacity];
tail = 0;//top是代表下一个位置需要插入的数
head = 0;
}
int front()
{
int size = abs(tail - head);
if (size == 0) return -1;
else return p[head];//每次就是头部这里出去
}
int push(int x)
{
if ((tail+1)%mCapacity ==head) //这代表满了
{
return -1;
}
else
{
p[tail] = x;
tail = (tail + 1) % mCapacity;
return 1;
}
}
int pop()
{
if (head==tail)//空
{
return -1;
}
else
{
int ret = front();
head = (head + 1) % mCapacity;
return ret;
}
}
int getsize()
{
return (tail-head+mCapacity)%mCapacity;
}
};
int main()
{
quque qu(5);
for (int i=0;i<5;i++)
{
qu.push(i);
}
cout << qu.getsize() << endl;;
int n = qu.getsize();
for (int i = 0;i < 100;i++)
{
cout << "qu.getsize()=" << qu.getsize() << endl;
int front = qu.pop();
cout << front << endl;
}
}
链表实现队列(注意head指针实现 删除 tail指针实现插入,借助dummpy节点,需要需要注意的是当只有一个节点进行删除的时候,注意需要tail=front,防止丢失tail指针)
#include<iostream>
using namespace std;
struct Node
{
int data;
struct Node *next;
Node()
{
data = 0;
next = nullptr;
}
Node(int x)
{
data = x;
next = nullptr;
}
};
struct queue
{
struct Node * head, *tail;
queue()
{
head = new (Node);
tail = head;
cout << "...构造函数" << endl;
}
};
int push(queue &que,int x)
{
Node * newNode = new Node(x);
que.tail->next = newNode;
que.tail = que.tail->next;
return -1;
}
int pop(queue &que)
{
int ret = -1;
/*if (que.head!=que.tail)*///证明链中至少存在一个元素
//cout << que.head->data << " " << que.tail->data << endl;
if (que.head != que.tail)
{
ret = que.head->next->data;//100
if (que.head->next->next==nullptr)
{
que.head->next = que.head->next->next;
que.tail = que.head;
}
else
{
que.head->next = que.head->next->next;
}
}
else
{
cout << "我们发现相等啊" << endl;
}
return ret;
}
int getsize(queue que)
{
Node * p = que.head->next;
int size = 0;
while (p!=NULL)
{
p = p->next;
size++;
}
return size;
}
int main()
{
queue qu;
for (int i=0;i<5;i++)
{
push(qu, i);
//cout << "getsize()=" << getsize(qu) << endl;
}
for (int i = 0;i < 10;i++)
{
cout << "pop=" << pop(qu) << endl;
}
题目2.1:
思路:
题目其实已经给出思路了
代码
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
class Solution {
public:
vector<int> deCode(vector<int>& nums) {
vector<int> ret_v;
queue<int> qu;
for (int i=0;i<nums.size();i++)
{
qu.push(nums[i]);
}
while (!qu.empty())
{
//将删除的元素放到 ret中
ret_v.push_back(qu.front());
qu.pop();
if (!qu.empty())
{
// 然后将元素放到队列的尾端
int front = qu.front();
qu.pop();
qu.push(front);
}
}
return ret_v;
}
};
int main()
{
vector<int> nums{ 6,3 ,1,7,5,8,9,2,4 };
Solution s;
vector<int> ret_v=s.deCode(nums);
for (auto one: ret_v)
{
cout << one << " ";
}
}
题目2.2
思路:
桌子上面的牌用vector保存
哼哈二将手中的牌用 队列保存就可以了。
需要注意一点 拖拉机里面的收牌加到尾部方式是,依次将弹出的牌加到尾部就可以了。
代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
class Solution {
private:
void doLoad(vector<int> &restore, queue<int> &qu)
{
int front = qu.front();
qu.pop();
auto it = find(restore.begin(), restore.end(), front);
if (it != restore.end())
{
vector<int> append_v;
append_v.push_back(front);//拖拉机里面的第一个数就是我们的front
//需要弹出 将其补到restore的尾端
int j = int(restore.size()) - 1;//避免无符号转换
while (j >= 0 && restore[j] != front)//这里肯定能找到 restore[j]
{
append_v.push_back(restore[j]);
restore.pop_back();//这个千万不要忘记
j--;//不要忘记了
}
append_v.push_back(restore[j]);//这个是最后一个
restore.pop_back();
for (int val : append_v)
{
qu.push(val);
}
}
else
{//直接加入到restore中
restore.push_back(front);
}
}
public:
/*
返回true代表nums1会赢 否则false
*/
bool doTractor(vector<int>& nums1,vector<int>& nums2)
{
vector<int> restore;
queue<int> qu1;
for (auto value : nums1) qu1.push(value);
queue<int> qu2;
for (auto value : nums2) qu2.push(value);
while (!qu1.empty()&&!qu2.empty())
{
cout << "qu1.size()=" << qu1.size() << " qu2.size()=" << qu2.size() << " restore.size()=" << restore.size() << endl;
doLoad(restore, qu1);//先进行qu1的操作
if (qu1.empty()) break;
doLoad(restore, qu2);再进行qu2的操作
if (qu2.empty()) break;
}
cout << "结果返回值" << endl;
cout << "qu1.size()=" << qu1.size() << " qu2.size()=" << qu2.size() << " restore.size()=" << restore.size() << endl;
if (!qu1.empty())
{
cout << "小哼赢了" << endl;
while (!qu1.empty())
{
cout << " " << qu1.front();
qu1.pop();
}
cout << endl;
cout << "桌子上面的牌" << endl;
for (int val:restore)
{
cout << " " << val;
}
cout << endl;
return true;
}
else
{
cout << "小哈赢了" << endl;
while (!qu2.empty())
{
cout << " " << qu2.front();
qu2.pop();
}
cout << endl;
cout << "桌子上面的牌" << endl;
for (int val : restore)
{
cout << " " << val;
}
cout << endl;
return false;
}
}
};
int main()
{
vector<int> nums1{ 2,4,1,2,5,6 };
vector<int> nums2{3,1,3,5,6,4};
Solution s;
bool ret_b = s.doTractor(nums1, nums2);
//cout << ret_b << endl;
}
我的代码运行结果
第三章
题目3.1
leetcode题目链接
题目
46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
思路:
dfs的思想,这里的index并不代表元素的下标,只是代表元素的个素,我习惯在dfs里面用index表示递归的深度。
代码:
class Solution {
private:
vector<vector<int>> ret_vv;
void dfs(vector<int>& nums,vector<bool>& used,vector<int>& temp,int index)
{
if(index>=nums.size())
{
ret_vv.push_back(temp);
return;
}
for(int i=0;i<nums.size();i++)
{
if(used[i]==true) continue;
temp.push_back(nums[i]);
used[i]=true;
dfs(nums,used,temp,index+1);
temp.pop_back();
used[i]=false;
}
}
public:
vector<vector<int>> permute(vector<int>& nums)
{
vector<bool> used(nums.size(),false);
vector<int > temp;
int index=0;
dfs(nums,used,temp,index);
return ret_vv;
}
};
提升:
leetcode题目链接
47. 全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
思路:
我们可以发现此题与题目3.1只有一点不同,返回所有不重复的全排列。
这里面涉及到一个同一树层剪枝和树枝剪枝
方法一:同一树层(这个更快)
if(i>0&&nums[i]==nums[i-1]&&isUse_v[i-1]==false) continue;
方法二:树枝剪枝
if(i>0&&nums[i]==nums[i-1]&&isUse_v[i-1]==true) continue;
代码:
class Solution {
private:
void dfs(int index,vector<int>& nums,vector<bool> &isUse_v,vector<vector<int>> &result_vv,vector<int>&temp_v)
{
if(index>=nums.size())
{
result_vv.push_back(temp_v);
return;
}
for(int i=0;i<nums.size();i++)
{
if(isUse_v[i]==true) continue;
if(i>0&&nums[i]==nums[i-1]&&isUse_v[i-1]==false) continue;
temp_v.push_back(nums[i]);
isUse_v[i]=true;
dfs(index+1,nums,isUse_v,result_vv,temp_v);
isUse_v[i]=false;
temp_v.pop_back();
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>>result_vv;
vector<int>temp_v;
vector<bool> isUse_v(nums.size(),false);
sort(nums.begin(), nums.end());
dfs(0,nums,isUse_v,result_vv,temp_v);
return result_vv;
}
};
第四章
题目4.1
全排列问题
思路:
dfs 上面题目3.1已经写过了。
代码:
同层剪枝(假设不能有重复的排列情况)
class Solution {
private:
void dfs(int index,vector<int>& nums,vector<bool> &isUse_v,vector<vector<int>> &result_vv,vector<int>&temp_v)
{
if(index>=nums.size())
{
result_vv.push_back(temp_v);
return;
}
for(int i=0;i<nums.size();i++)
{
if(isUse_v[i]==true) continue;
if(i>0&&nums[i]==nums[i-1]&&isUse_v[i-1]==false) continue;
temp_v.push_back(nums[i]);
isUse_v[i]=true;
dfs(index+1,nums,isUse_v,result_vv,temp_v);
isUse_v[i]=false;
temp_v.pop_back();
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>>result_vv;
vector<int>temp_v;
vector<bool> isUse_v(nums.size(),false);
sort(nums.begin(), nums.end());
dfs(0,nums,isUse_v,result_vv,temp_v);
return result_vv;
}
};
题目4.2:
思路:
dfs,注意每次的递归终止条件是index>=nums.size()
A+B=C
最后的temp中 需要分离出 A,B,C
代码:
#include<iostream>
#include<vector>
using namespace std;
class Solution {
private:
bool Compare(int temp)
{
//A+B=C;
int a[3];
int i = 0;
while (temp != 0)
{
a[i] = temp % 1000;
temp = temp / 1000;
i++;
}
//A+B==C?
if (a[2] + a[1] == a[0])
{
cout << a[2] << "+" << a[1] << "=" << a[0] << endl;
return true;
}
else return false;
}
void dfs(vector<int>& nums, vector<bool>& used, int temp, int index )
{
if (index>= nums.size())
{
if (Compare(temp)) sum++;
return;
}
for (int i=0;i<nums.size();i++)
{
if (used[i] == true) continue;
used[i] = true;
dfs(nums, used, temp * 10 + nums[i], index+1);//千万要注意我们没有传 temp引用,ndex引用
used[i] = false;
}
}
int sum = 0;
public:
int permuteSumDiff(vector<int>& nums)
{
int temp = 0;//这个是存每次递归的和
int index = 0;
vector<bool> used(nums.size(),false);//标志位
dfs(nums, used, temp,index );
return sum;
}
};
int main()
{
vector<int> numbers{ 1,2,3,4,5,6,7,8,9 };
Solution s;
int sum=s.permuteSumDiff(numbers);
cout << "sum=" << sum << endl;
}
结果展示:
题目4.3
迷宫最短路径
思路:
其实这就是迷宫问题,由于我对于迷宫问题,喜欢将第一个点先拎出来,然后进行dfs
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
private:
int global_step = INT_MAX;
vector<vector<int>> dxy{ {0,1},{1,0},{0,-1}, {-1,0}};//走的方向依次是 右下左上
bool isArea(vector<vector<int>> &maze,int temp_x,int temp_y)
{
if (temp_x < 0 || temp_x >= maze.size() || temp_y < 0 || temp_y >= maze[0].size()) return false;
return true;
}
void dfs(vector<vector<int>> &maze, vector<vector<bool>> &used,int current_i, int current_j, int target_i, int target_j, int &step)
{
if (current_i == target_i&& current_j == target_j)
{
global_step = min(global_step,step);
return;
}
for (int i=0;i < dxy.size();i++)
{
int temp_i = current_i + dxy[i][0];
int temp_j = current_j + dxy[i][1];
if (isArea(maze, temp_i, temp_j) && maze[temp_i][temp_j] == 0 &&used[temp_i][temp_j] == false)
{
used[temp_i][temp_j] = true;
step++;
dfs(maze, used, temp_i, temp_j, target_i, target_j, step);
step--;
used[temp_i][temp_j] = false;
}
}
}
public:
int getShortpath(vector<vector<int>> &maze, int start_i, int start_j, int target_i, int target_j) //maze中0代表可走,1代表是障碍物
{
vector<vector<bool>> used(maze.size(), vector<bool>(maze[0].size(), false));
if (isArea(maze, start_i, start_j) && used[start_i][start_j] == false)//这里防止刚开始位置越界
{
used[start_i][start_j] = true;
int step = 0;
dfs(maze, used, start_i, start_j, target_i, target_j, step);
}
return global_step;
}
};
int main()
{
vector<vector<int>> maze{
{0,0,1,0},
{0,0,0,0},
{0,0,1,0},
{0,1,0,0},
{0,0,0,1} };
int start_i = 0;
int start_j = 0;
int target_i = 3;
int target_j = 2;
Solution s;
int path=s.getShortpath(maze, start_i, start_j, target_i, target_j);
cout << "path=" << path << endl;
}
结果展示:
题目4.4
迷宫广度优先搜索
思路:
首先我们需要明确一点,在迷宫问题中采用bfs(广度优先遍历)会得到满足一个符合条件的。但可能不是最短路径。主要是进行迭代
代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
private:
vector<vector<int>> dxy{ {0,1},{1,0},{0,-1}, {-1,0} };//走的方向依次是 右下左上
bool isArea(vector<vector<int>> &maze, int temp_x, int temp_y)
{
if (temp_x < 0 || temp_x >= maze.size() || temp_y < 0 || temp_y >= maze[0].size()) return false;
return true;
}
public:
int getShortpath(vector<vector<int>> &maze, int start_i, int start_j, int target_i, int target_j) //maze中0代表可走,1代表是障碍物
{
vector<vector<bool>> used(maze.size(), vector<bool>(maze[0].size(), false));
if (isArea(maze, start_i, start_j) && used[start_i][start_j] == false)//这里防止刚开始位置越界
{
used[start_i][start_j] = true;
int step = 0;
queue<pair<int, int>> qu;
qu.emplace(start_i, start_j);
while (!qu.empty())
{
int n = qu.size();
for (int i=0;i<n;i++)
{
auto node = qu.front();
qu.pop();//一定记得pop
int current_i = node.first;
int current_j = node.second;
//cout << "current_i=" << current_i << " current_j=" << current_j << " step="<< step << endl;
if (current_i == target_i && current_j == target_j)
{
return step;
}
for (int i = 0;i < dxy.size();i++)
{
int temp_i = current_i + dxy[i][0];
int temp_j = current_j + dxy[i][1];
if (isArea(maze, temp_i, temp_j) &&maze[temp_i][temp_j]==0&& used[temp_i][temp_j] == false)
{
cout << "temp_i=" << temp_i << " temp_j=" << temp_j << " step=" << step << endl;
used[temp_i][temp_j] = true;
qu.emplace(temp_i, temp_j);
}
}
}
cout << "*******************" << endl;
for (int i=0;i<used.size();i++)
{
for (int j = 0;j<used[0].size();j++)
{
cout << used[i][j] << " ";
}
cout << endl;
}
cout << endl;
cout << "*******************" << endl;
step++;
}
}
return INT_MAX;
}
};
int main()
{
vector<vector<int>> maze
{
{0,0,1,0},
{0,0,0,0},
{0,0,1,0},
{0,1,0,0},
{0,0,0,1} };
int start_i = 0;
int start_j = 0;
int target_i = 3;
int target_j = 2;
Solution s;
int path = s.getShortpath(maze, start_i, start_j, target_i, target_j);
cout << "path=" << path << endl;
}
题目4.5
思路递归过程中用floodfill方法,其实就是used[i][j]=true后,不在进行恢复,减少重复操作,然后在每个合格点再进行判断消除的怪兽数目,保存可以消灭怪兽的最多数
代码:略
题目4.6
floodfilld应用
求飞机降落点中的岛屿快的总面积
思路:
我是比较喜欢
代码:
#include<iostream>
#include<vector>
using namespace std;
class Solution {
private:
vector<vector<int>> dxy{ {0,1},{1,0},{0,-1}, {-1,0} };//走的方向依次是 右下左上
bool isArea(vector<vector<int>>& grid, int temp_i, int temp_j)
{
if (temp_i < 0 || temp_i >= grid.size() || temp_j < 0 || temp_j >= grid[0].size()) return false;
return true;
}
int s = 0;//这个是可以这样进行赋值的
void dfs(vector<vector<int>>&grid, vector<vector<bool>>& used, int current_i, int current_j)
{
for (int i = 0;i < dxy.size();i++)
{
int temp_i = current_i + dxy[i][0];
int temp_j = current_j + dxy[i][1];
if (isArea(grid, temp_i, temp_j) && grid[temp_i][temp_j] > 0 && used[temp_i][temp_j] == false)
{
used[temp_i][temp_j] = true;
s++;
grid[temp_i][temp_j] = s;
dfs(grid, used, temp_i, temp_j);
//used[temp_i][temp_j] = false; floodfill 就是不要这句话
}
}
}
public:
int islandArea(vector<vector<int>>& grid,int start_i,int start_j)
{
vector<vector<bool>> used(grid.size(), vector<bool>(grid[0].size(), false));
if (isArea(grid,start_i,start_j)&&used[start_i][start_j]==false)
{
if (grid[start_i][start_j] > 0)
{
used[start_i][start_j] = true;
s++;
grid[start_i][start_j] = s;
dfs(grid, used, start_i, start_j);
}
}
cout << "*******************" << endl;
for (int i = 0;i < grid.size();i++)
{
for (int j = 0;j < grid[0].size();j++)
{
//cout << grid[i][j] << " ";
printf("%5d", grid[i][j]);
}
cout << endl;
}
cout << endl;
cout << "*******************" << endl;
return s;
}
};
int main()
{
vector<vector<int>> maze{
{1,2,1,0,0,0,0,0,2,3},
{3,0,2,0,1,2,1,0,1,2},
{4,0,1,0,1,2,3,2,0,1},
{3,2,0,0,0,1,2,4,0,0},
{0,0,0,0,0,0,1,5,3,0},
{0,1,2,1,0,1,5,4,3,0},
{0,1,2,3,1,3,6,2,1,0},
{0,0,3,4,8,9,7,5,0,0},
{0,0,0,3,7,8,6,0,1,2},
{0,0,0,0,0,0,0,0,1,0},
};
int start_i = 5;
int start_j = 7;
Solution s;
int sum = s.islandArea(maze, start_i, start_j);
cout << "sum=" << sum << endl;
}
运行结果
我们可以看到floodfill的运行效果截图,
题目4.7
leetcode200. 岛屿数量
题目:
思路:
floodfill,相比较题目4.6而言,需要在main函数中套上循环遍历grid
代码:
class Solution {
private:
vector<vector<int>> dxy{ {0,1},{1,0},{0,-1}, {-1,0} };//走的方向依次是 右下左上
bool isArea(vector<vector<char>>& grid, int temp_i, int temp_j)
{
if (temp_i < 0 || temp_i >= grid.size() || temp_j < 0 || temp_j >= grid[0].size()) return false;
return true;
}
int count = 0;//这个是可以这样进行赋值的
void dfs(vector<vector<char>>&grid, vector<vector<bool>>& used, int current_i, int current_j)
{
for (int i = 0;i < dxy.size();i++)
{
int temp_i = current_i + dxy[i][0];
int temp_j = current_j + dxy[i][1];
if (isArea(grid, temp_i, temp_j) && grid[temp_i][temp_j] !='0' && used[temp_i][temp_j] == false)
{
used[temp_i][temp_j] = true;
dfs(grid, used, temp_i, temp_j);
//used[temp_i][temp_j] = false; floodfill 就是不要这句话
}
}
}
public:
int numIslands(vector<vector<char>>& grid)
{
vector<vector<bool>> used(grid.size(), vector<bool>(grid[0].size(), false));
for (int start_i=0;start_i<grid.size();start_i++)
{
for (int start_j = 0;start_j < grid[0].size();start_j++)
{
if (isArea(grid, start_i, start_j) && used[start_i][start_j] == false)
{
if (grid[start_i][start_j] != '0')
{
used[start_i][start_j] = true;
count++;
dfs(grid, used, start_i, start_j);
}
}
}
}
return count;
}
};
题目4.8
思路:
这题首先从start_i=0,start_j=0出开始找路,所以第一个水管是
中形状,然后采用dfs看有没够水管能够与第一个水管接上的,接上了就继续dfs,依次寻找,直到到达出口,当某一个水管接不上时,如果无论怎么旋转方向都不满足,回溯,转换当前不满足水管的方向,如果还是不满足,那么就可以后退一个,再次判定,代码中也是需要used判断水管位置的访问状态的
代码:
略
第五章:
图论
第六章
最短路径
题目:求某个点到某个结束点的最短路径
其实这个问题涉及到的问题很广,实用性贼强,下面介绍三个算法
游戏中寻路算法
6.1.1方法一:广度优先搜索算法
//bfs寻路算法 确定也明显 没有像 迪杰斯特拉算法中的最短路径一样。也就是没有权值,不能找到最短的
代码
//bfs寻路算法 确定也明显 没有像 迪杰斯特拉算法中的最短路径一样。也就是没有权值,不能找到最短的
//used[temp_i][temp_j] = true; bfs没有对应的 false这是和bfs不一样的
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
private:
vector<vector<int>> dxy{ {0,1},{1,0},{0,-1}, {-1,0} };//走的方向依次是 右下左上
bool isArea(vector<vector<int>> &maze, int temp_x, int temp_y)
{
if (temp_x < 0 || temp_x >= maze.size() || temp_y < 0 || temp_y >= maze[0].size()) return false;
return true;
}
public:
int getShortpath(vector<vector<int>> &maze, int start_i, int start_j, int target_i, int target_j) //maze中0代表可走,1代表是障碍物
{
vector<vector<bool>> used(maze.size(), vector<bool>(maze[0].size(), false));
if (isArea(maze, start_i, start_j) && used[start_i][start_j] == false)//这里防止刚开始位置越界
{
used[start_i][start_j] = true;
int step = 0;
queue<pair<int, int>> qu;
qu.emplace(start_i, start_j);
while (!qu.empty())
{
int n = qu.size();
for (int i = 0;i < n;i++)
{
auto node = qu.front();
qu.pop();//一定记得pop
int current_i = node.first;
int current_j = node.second;
//cout << "current_i=" << current_i << " current_j=" << current_j << " step="<< step << endl;
if (current_i == target_i && current_j == target_j)
{
return step;
}
for (int i = 0;i < dxy.size();i++)
{
int temp_i = current_i + dxy[i][0];
int temp_j = current_j + dxy[i][1];
if (isArea(maze, temp_i, temp_j) && maze[temp_i][temp_j] == 0 && used[temp_i][temp_j] == false)
{
cout << "temp_i=" << temp_i << " temp_j=" << temp_j << " step=" << step << endl;
used[temp_i][temp_j] = true;
qu.emplace(temp_i, temp_j);
}
}
}
cout << "*******************" << endl;
for (int i = 0;i < used.size();i++)
{
for (int j = 0;j < used[0].size();j++)
{
cout << used[i][j] << " ";
}
cout << endl;
}
cout << endl;
cout << "*******************" << endl;
step++;
}
}
return INT_MAX;
}
};
int main()
{
vector<vector<int>> maze
{
{0,0,1,0},
{0,0,0,0},
{0,0,1,0},
{0,1,0,0},
{0,0,0,1} };
int start_i = 0;
int start_j = 0;
int target_i = 3;
int target_j = 2;
Solution s;
int path = s.getShortpath(maze, start_i, start_j, target_i, target_j);
cout << "path=" << path << endl;
}
题目:
6.1.2迪杰斯特拉算法 可以找到所有的节点最短路径,
求以某一起始点到另外所有结点的最短路径dist
思路:
首先对应的二维矩阵是
其次我们需要明白 迪杰斯特拉(Dijkstra)是针对连通图来说的。
代码最重要的两个变量
vector<int> visit(n,0);//0代表未访问节点,1代表访问节点
vector<int> dist(n, 0);//后面会根据start进行初始化,这里初始化值是任何值都没关系
主要思路是,首先将start节点先拎出来,然后每次将最小的dist的没有访问的节点找到作为中转节点,然后以中转节点更新,dist的从起始节点到非访问节点的更小值。
代码:
//单源最短路径,迪杰斯特拉算法
//总体思路,是将每次最短的还没有访问的节点作为中转节点,然后从寻找从中转节点到其他节点和之前的到start的节点路径作比较,
//更新每个未访问节点的最短路径,将中转节点设置成访问节点,进入下一次循环。
#include<iostream>
#include<vector>
#define INF 999999//路径中不可达边 因此graph也应该这么写不可达边的长度
using namespace std;
class Solution {
public:
vector<int> Dijkstra(vector<vector<int>>& graph,int start)
{
int n = graph.size();
vector<int> visit(n,0);//0代表未访问节点,1代表访问节点
vector<int> dist(n, 0);//后面会根据start进行初始化,这里初始化值是任何值都没关系
//首先将第一个start节点的信息提取出来,为循环做出准备
if (start>=n)
{
cout << "起点位置不合法" << endl;
return dist;
}
visit[start] = 1;// 设置起点位置已访问1
for (int i=0;i<n;i++)//设置起点位置为初始位置,初始化dist
{
dist[i] = graph[start][i];
}
for (int i=1;i<n;i++)//第一个节点已经单独拎出来了个节点,所以我们需要循环n-1次
{
int minNumber = INF;
int mind=1111;//我们从这里可以看出无论此处mind初始值是多少,如果不是连通图,可能会造成,死循环, 因此使用该算法的图应该是连通图
for (int j=0;j< dist.size();j++)//寻找中转节点 记住当前中转节点一定是未访问节点
{
if (visit[j]==0&& minNumber> dist[j])
{
minNumber = dist[j];
mind = j;
}
}
//cout << "mind=" << mind << endl;
//visit[mind] = 1; //这个放在这里也是可以的
//中转节点找到后,我们需要更新当前以中转节点和原点的 到其他非访问节点距离
for (int j=0;j<n;j++)//这里的j代表着节点的下标的意思
{
if (visit[j]==0&&dist[j]>dist[mind]+graph[mind][j])//这一步说明了graph中不能有INT_MAX不然可能会溢出
{
dist[j] = dist[mind] + graph[mind][j];
}
}
visit[mind] = 1;//应该将中转节点设置成已访问
}
return dist;
}
};
int main()
{
int n = 6;
vector<vector<int>> graph(n, vector<int>(n, INF));
for (int i=0;i<n;i++)
{
graph[i][i] = 0;
}
graph[0][1] = 100;
graph[1][0] = 100;
graph[0][2] = 1200;
graph[2][0] = 1200;
graph[1][2] = 900;
graph[2][1] = 900;
graph[1][3] = 300;
graph[3][1] = 300;
graph[2][3] = 400;
graph[3][2] = 400;
graph[2][4] = 500;
graph[4][2] = 500;
graph[3][4] = 1300;
graph[4][3] = 1300;
graph[3][5] = 1400;
graph[5][3] = 1400;
graph[4][5] = 1500;
graph[5][4] = 1500;
Solution s;
vector<int> dist;
for (int j=0;j<n;j++)
{
cout << "以索引值为start=" << j << "设置为起始点" << endl;
dist = s.Dijkstra(graph, j);
for (int i = 0;i < n;i++)
{
cout << " " << dist[i];
}
cout << endl;
}
}
结果:
缺点:
迪杰斯特拉算法在寻路的网格中是无目的性的,(因为我们将每个节点的最短路径都找出来了,而不是找起点到给定终点的最短路径)
6.1.3方式三:A*算法视频参考链接
**A*算法,**可以确定最短路径(总代价最小) 总代价=当前代价+预估代价
当前代价:我们可以使用 当前位置到起点位置的步数
预估代价,说明不是一个确切的数字,我们可以使用 曼哈顿距离 表示当前位置和目标位置 这两个坐标的绝对值差的和 即|x1-x2|+|y1-y2|
思路:
总代价=当前代价+预估代价 我们每次将当前的节点的总代价,加入到优先队列priority_queue(注意,我们需要重写仿函数 从cmp,使得堆顶最小),
struce cmp
{
operator()(node1,node2)
{
return node1.step_cost+node1.cost_so_far>node2.step_cost+node2.cost_so_far
}
]
代码:
待定
第七章
题目7.1
leetcode排序链接
堆排序
思路:
用数据实现堆排序
进行堆排序输出的过程其实可以看做是删除堆顶元素的重复的过程
堆排序输出
vector<int> temp_v = nums;//进行堆排序输出
//大根堆建好后,开始进行堆顶元素
cout << "堆排序后的数组逻辑为" << endl;
for (int i=n-1;i>=0;i--)//这其实可以看做是一个删除的过程
{
swap(temp_v[0], temp_v[i]);
sortHeap(temp_v, 0, i);//每次将最后一个位置确定后,循环n次1,这样数组就是有序的
}
删除堆顶元素
void pop(vector<int>& nums)
{
int n = nums.size();
swap(nums[0], nums[n - 1]);
cout << "删除的栈顶元素是" << nums.back() << endl;
nums.pop_back();
n = nums.size();//nums中已经删除一个元素了。因此需要重新取大小
sortHeap(nums, 0, n);
}
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
private:
void sortHeap(vector<int>& nums,int index, int n)//我们需要建立大根堆
{
int max = index;
int left_index = index * 2 + 1;
int right_index = index * 2 + 2;
if (left_index<n&&nums[left_index]>nums[max])
{
max = left_index;
}
if (right_index<n&&nums[right_index]>nums[max])
{
max = right_index;
}
if (max != index)
{
swap(nums[max],nums[index]);
sortHeap(nums, max, n);//这是一个下溯的过程
}
}
public:
vector<int> Heap(vector<int>& nums) {
int n = nums.size();
for (int index=n/2-1;index >=0;index--) //index代表着我们的需要比对的初始化的数字的位置(上溯过程)
{
sortHeap(nums, index, n);
}
vector<int> temp_v = nums;//进行堆排序输出
//大根堆建好后,开始进行堆顶元素
cout << "堆排序后的数组逻辑为" << endl;
for (int i=n-1;i>=0;i--)//这其实可以看做是一个删除的过程
{
swap(temp_v[0], temp_v[i]);
sortHeap(temp_v, 0, i);//每次将最后一个位置确定后,循环n次1,这样数组就是有序的
}
//cout << endl;
return temp_v;
}
//插入
void insert(vector<int>& nums,int x)
{
nums.push_back(x);
int n = nums.size();
for (int index = n / 2 - 1;index >= 0;index--) //index代表着我们的需要比对的初始化的数字的位置(上溯过程)
{
sortHeap(nums, index, n);
}
}
//删除元素
void pop(vector<int>& nums)
{
int n = nums.size();
swap(nums[0], nums[n - 1]);
cout << "删除的栈顶元素是" << nums.back() << endl;
nums.pop_back();
n = nums.size();//nums中已经删除一个元素了。因此需要重新取大小
sortHeap(nums, 0, n);
}
};
int main()
{
vector<int> nums{ 4,6,2,3,5,8,6,9,4,1,6 };
Solution s;
vector<int> ret=s.Heap(nums);
for (int value:ret)
{
cout << " " << value;
}
cout << endl;
int n = nums.size();
for (int i=0;i<n;i++)
{
s.pop(nums);
}
s.insert(nums,5);
s.insert(nums,4);
s.insert(nums,10);
n = nums.size();
cout << "插入元素后重新删除" << endl;
for (int i = 0;i < n;i++)
{
s.pop(nums);
}
}
运行结果
题目7.2
leetcode 684链接
确认图中有没有环
思路:
代码:
#include <iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 1e6 + 10;
int parent[MAXN], RANK[MAXN];
void Init()
{
for (int i=0;i< MAXN;i++)
{
parent[i] = i;
RANK[i] = 1;
}
}
//寻找x的头部(非递归写法)
int Find(int x)
{
int x_root = x;
while (parent[x_root]!= x_root)
{
x_root = parent[x_root];
}
return x_root;
}
寻找x的头部(递归写法)
//int Find(int x)
//{
//
// if (parent[x] == x)
// {
// return x;
// }
// return Find(parent[x]);
//}
int Union(int u, int v)
{
u = Find(u);
v = Find(v);
if (u != v)
{
if (RANK[u] > RANK[v] )
{
parent[v] = u;
}
else if(RANK[v] > RANK[u])
{
parent[u] = v;
}
else if (RANK[v]= RANK[u])
{
parent[v] = u;
RANK[u]++;
}
return 1;
}
else
{
return 0;
}
}
int main()
{
Init();
vector<vector<int>> vv{
{0,1},{1,2},{1,3},
{2,5},{2,4},{3,4}
};
for (int i=0;i< vv.size();i++)
{
int u = vv[i][0];
int v = vv[i][1];
if (Union(u, v) == 0)
{
cout << " 图中存在环" << endl;
return 0;
}
}
cout << " 图中不存在环" << endl;
return 1;
}