啊哈!算法 案例用c++实现

第二章

我觉得此章节的数据结构可以实现一下

数组实现栈

#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链接
确认图中有没有环
思路:
并查集, 我认为最重要的是刚开始将parnt[x]=x  rank[any_x]=1这两个初始化很重要。 并查集,并查集,并查集,因此我们需要主要写俩个函数  并 与查

代码:


#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;
}



  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值