算法系列:导航/路径规划算法

Posted 朔方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法系列:导航/路径规划算法相关的知识,希望对你有一定的参考价值。


1. 二维数组的查找
2. 替换空格
3. 从尾到头打印链表
4. 重建二叉树
5. 用两个栈实现队列
6. 旋转数组的最小数字
7. 斐波那契数列
8. 跳台阶
9. 变态跳台阶
10. 矩阵覆盖
11. 二进制中1的位数
12. 数值的整数次方
13. 调整数组顺序使奇数位于偶数前面
14. 链表中倒数第k个结点
15. 反转链表
16. 合并两个排序的链表
17. 树的子结构
18. 二叉树的镜像
19. 顺时针打印矩阵
20. 包含min函数的栈
21. 栈的压入、弹出序列
22. 从上往下打印二叉树
23. 二叉搜索树的后序遍历序列
24. 二叉树中和为某一值的路径
25. 复杂链表的复制
26. 二叉搜索树与双向链表
27. 字符串的排列
28. 数组中出现次数超过一半的数字
29. 最小的K个数
30. 连续子数组的最大和
31. 整数中1出现的次数(从1到n整数中1出现的次数)
32. 把数组排成最小的数
33. 丑数
34. 第一个只出现一次的字符
35. 数组中的逆序对
36. 两个链表的第一个公共结点
37. 数字在排序数组中出现的次数
38. 二叉树的深度
39. 平衡二叉树
40. 数组中只出现一次的数字
41. 和为S的连续正数序列
42. 和为S的两个数字
43. 左旋转字符串
44. 翻转单词顺序列
45. 扑克牌顺子
46. 孩子们的游戏(圆圈中最后剩下的数)
47. 求1+2+3+…+n
48. 不用加减乘除做加法
49. 把字符串转换成整数
50. 数组中重复的数字
51. 构建乘积数组
52. 正则表达式匹配
53. 表示数值的字符串
54. 字符流中第一个不重复的字符
55. 链表中环的入口结点
56. 删除链表中重复的结点
57. 二叉树的下一个结点
58. 对称的二叉树
59. 按之字形顺序打印二叉树
60. 把二叉树打印成多行
61. 序列化二叉树
62. 二叉搜索树的第k个结点
63. 数据流中的中位数
64. 滑动窗口的最大值
65. 矩阵中的路径
66. 机器人的运动范围

1. 二维数组的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

/* 思路: 依次比较右上角的数字;如果该数字大于要查找的数字,则剔除列;如果该数字大于要查找的数字,则剔除行;
复杂度:O(m+n), 行数m,列数n */
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
bool found=false;
if (array.empty())
return found;
int rows, columns, row, column;
rows = array.size();
columns = array[0].size();
row = 0;
column = columns - 1;
while(row < rows && column >= 0)
{
if(array[row][column] == target)
{
found = true;
break;
}
else if (array[row][column] > target)
-- column;
else
++ row;
}
return found;
}
};


2. 替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

/* 思路:首先计算原字符串长度,空格个数;然后计算替换之后的长度;设置两个指针分别指向原,新字符串的尾部,逐个赋值;
复杂度:O(n) */
class Solution {
public:
void replaceSpace(char *str,int length) {
if(str == nullptr || length <=0)
return;

int original_length = 0;
int number_of_space = 0;
int i = 0;
while(str[i] != \'\\0\')
{
++ original_length;
if(str[i] == \' \')
++ number_of_space;
++ i;
}

if (number_of_space <= 0)
return;

int new_length = original_length + 2*number_of_space;

int index_of_original = original_length;
int index_of_new = new_length;

while(index_of_original>=0 && index_of_new>=index_of_original)
{
if(str[index_of_original] == \' \')
{
str[index_of_new--] = \'0\';
str[index_of_new--] = \'2\';
str[index_of_new--] = \'%\';
}
else
{
str[index_of_new--] = str[index_of_original];
}
-- index_of_original;
}

}
};


3. 从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。

/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
// 思路:借助辅助栈,或者使用递归;
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> reverse_list;
stack<int> nodes;

ListNode *p_node = head;
while(p_node != nullptr)
{
nodes.push(p_node->val);
p_node = p_node->next;
}

int tempVal;
while(!nodes.empty())
{
tempVal = nodes.top();
reverse_list.push_back(tempVal);
nodes.pop();
}
return reverse_list;
}
};


4.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
/* 思路(递归):根据前序遍历的第一个数字创建根节点;在中序便利找到根节点的位置;确定左右子树节点数量;递归构建左右子树;*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.empty() || vin.empty() || pre.size()!=vin.size())
return nullptr;

vector<int> left_pre, right_pre, left_vin, right_vin;
TreeNode *node = new TreeNode(pre[0]);

int left_length = 0;
while(pre[0]!=vin[left_length] && left_length < pre.size())
++ left_length;

for(int i=0; i<left_length; i++)
{
left_pre.push_back(pre[i+1]);
left_vin.push_back(vin[i]);
}

for(int i=left_length+1; i<pre.size(); i++)
{
right_pre.push_back(pre[i]);
right_vin.push_back(vin[i]);
}
node->left = reConstructBinaryTree(left_pre, left_vin);
node->right = reConstructBinaryTree(right_pre, right_vin);

return node;
}
};


5.用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

/*思路:stack1:负责压栈,stack2负责弹栈(如果为空,则将stack1中元素压入stack2);*/
class Solution
{
public:
void push(int node) {
stack1.push(node);
}

int pop() {
if(stack2.empty())
{
while(!stack1.empty())
{
int val = stack1.top();
stack1.pop();
stack2.push(val);
}
}
int val = stack2.top();
stack2.pop();
return val;
}

private:
stack<int> stack1;
stack<int> stack2;
};


6. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

/*简单方法*/
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray)
{ //数组为空时
if(rotateArray.size() == 0)
return -1;
//前部分数据旋转
for(int i = 0; i < rotateArray.size() - 1; i++)
{
if (rotateArray[i] > rotateArray[i + 1])
return rotateArray[i + 1];
}
//全部数据旋转,相当于没有旋转,最小数即为第一个数
return rotateArray[0];
}
};

/*思路:二分查找思想*/
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int length = rotateArray.size();
if (length == 0)
return 0;

int left = 0, right = length-1;
int mid;
while(rotateArray[left] >= rotateArray[right])
{
if(left == right - 1)
return rotateArray[right];

mid = (left + right)/2;

if(rotateArray[left] == rotateArray[mid] &&
rotateArray[mid] == rotateArray[right])
{
int min_num = rotateArray[left];
for(int i=left; i < right; i++)
min_num = rotateArray[i]<min_num? rotateArray[i]:min_num;
return min_num;
}
if(rotateArray[left] <= rotateArray[mid])
left = mid;
else
right = mid;

}
return rotateArray[left];

}
};


7.斐波那契数列
要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39

/* 思路: 循环,保存中间结果(递归的话,重复计算太多)*/
class Solution {
public:
int Fibonacci(int n) {
if(n<=0)
return 0;
if(n==1)
return 1;
int fib1=1, fib2=0;
int fibn;
for(int i=2; i<=n; i++)
{
fibn = fib1+fib2;

fib2 = fib1;
fib1 = fibn;
}
return fibn;
}
};


8. 跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

class Solution {
public:
int jumpFloor(int number) {
if(number == 1)
return 1;

int pre1=1, pre2=1;
int cur;
for(int i=2; i<=number; i++)
{
cur = pre1 + pre2;

pre2 = pre1;
pre1 = cur;
}

return cur;
}
};


9.变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法

思路:其实是隔板问题,假设n个台阶,有n-1个空隙,可以用0~n-1个隔板分割,c(n-1,0)+c(n-1,1)+...+c(n-1,n-1)=2^(n-1),其中c表示组合。 有人用移位1<<--number,这是最快的。

class Solution {
public:
int jumpFloorII(int number) {
int jump_number = 1;
for(int i=0; i<number-1; i++)
jump_number = jump_number * 2;
return jump_number;
}
};


/**********更加简单的方法**********/

class Solution {
public:
int jumpFloorII(int number) {
return 1<<(--number);
}
};


10. 矩阵覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

/* 思路:
第一块有两种方式:横着放和竖着放
横这放对应为发f(n-2);
竖着放下一步的放方法为f(n-1);
所以总的放的方法为f(n)=f(n-1)+f(n-2);
*/

class Solution {
public:
int rectCover(int number) {
if(number <= 2)
return number;
int pre1 = 2, pre2 = 1;
int cur;
for(int i=2; i<number; i++)
{
cur = pre1 + pre2;

pre2 = pre1;
pre1 = cur;
}
return cur;
}
};


11. 二进制中1的位数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

/************ 简单方法 ************/
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
unsigned int flag = 1;
while(flag)
{
if(n & flag)
++ count;
flag = flag << 1;
}
return count;
}
};

/******* 巧妙方法 *******/
思路:一个整数减去1,在与原整数做与运算,会将最右边的一个1变成0.
那么二进制中有多少个1,可进行这样的操作多少次;
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while(n)
{
++ count;
n = (n-1)&n;
}
return count;
}
};


12. 数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

/*思路 需要考虑以下几种情况
1) base的正负;
2) base是否等于0;
3) exponent的正负;
4) exponent是否为1;*/

class Solution {
public:
double Power(double base, int exponent) {
if (base>-0.0000001 && base<0.0000001 && exponent<0)
return 0.0;

double result = 1.0;

unsigned int abs_exponent = (unsigned int) exponent;
if(exponent < 0)
abs_exponent = (unsigned int) (-exponent);

/*
for(int i=0; i<abs_exponent; i++)
result = result * base;
*/
//
if(abs_exponent == 0)
return 1.0;
if(abs_exponent == 1)
return base;

result = base;
abs_exponent = abs_exponent >> 1;
while(abs_exponent)
{
result *= result;
abs_exponent = abs_exponent >> 1;
}
if(exponent & 0x1 == 1)
result *= base;
//
if(exponent < 0 && result > 0.0)
result = 1.0 / result;
return result;
}
};


13. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

注:相比剑指offer书要难(要保证相对顺序不变)
class Solution {
public:
void reOrderArray(vector<int> &array) {
int length = array.size();
if(length==0 || length==1)
return;

int index_even=0, index_odd;
while(index_even<length)
{
while(index_even<length && !isEven(array[index_even]))
++ index_even;
index_odd = index_even+1;
while(index_odd<length && isEven(array[index_odd]))
++ index_odd;

if(index_odd<length)
{
int temp = array[index_odd];
for(int i=index_odd; i>index_even; i--)
array[i] = array[i-1];
array[index_even] = temp;
}
else
break;
}

}

bool isEven(int number){
if((number & 0x1) == 0)
return true;
return false;
}

};

 

/*************方法二 申请空间***********/
class Solution {
public:
void reOrderArray(vector<int> &array) {
int length = array.size();
if(length==0 || length ==1)
return;

vector<int> res;
for(int i=0; i<length; i++)
{
if((array[i]&0x1) != 0)
res.push_back(array[i]);
}

for(int i=0; i<length; i++)
{
if((array[i]&0x1) == 0)
res.push_back(array[i]);
}
array = res;
}
};


14. 链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。

/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
// 定义快慢指针,快的先走K步;
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead == nullptr || k==0)
return nullptr;

ListNode *pAhead = pListHead;
ListNode *pAfter = pListHead;

for(int i=0; i<k-1; i++)
{
if(pAhead->next != nullptr)
pAhead = pAhead->next;
else
return nullptr;
}

while(pAhead->next != nullptr)
{
pAhead = pAhead->next;
pAfter = pAfter->next;
}

return pAfter;

}
};


15. 反转链表
输入一个链表,反转链表后,输出链表的所有元素。

/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
/* 思路:定义三个指针,分别指向当前结点,前一个结点,后一个结点 */
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr)
return nullptr;
if(pHead->next == nullptr)
return pHead;

ListNode *pPreNode=pHead, *pCurNode=pHead->next, *pNextNode;
pPreNode->next = nullptr;
while(pCurNode->next != nullptr)
{
pNextNode = pCurNode->next;

pCurNode->next = pPreNode;
pPreNode = pCurNode;
pCurNode = pNextNode;
}
pCurNode->next = pPreNode;
return pCurNode;
}
};


16.合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/

/*------------------------------方法一 递归版本--------------------------*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr)
return pHead2;
else if(pHead2 == nullptr)
return pHead1;

ListNode *pMerge;
if(pHead1->val <= pHead2->val)
{
pMerge = pHead1;
pHead1->next = Merge(pHead1->next, pHead2);
}
else
{
pMerge = pHead2;
pHead2->next = Merge(pHead1, pHead2->next);
}
return pMerge;
}
};


17. 树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/

/*分两步,判断根节点是否相等;判断子结构是否相等*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result = false;
if(pRoot1!=nullptr && pRoot2!=nullptr)
{
if(pRoot1->val == pRoot2->val)
result = DoesTree1HaveTree2(pRoot1, pRoot2);
if(!result)
result = HasSubtree(pRoot1->left, pRoot2);
if(!result)
result = HasSubtree(pRoot1->right, pRoot2);
}
return result;
}

bool DoesTree1HaveTree2(TreeNode *Tree1, TreeNode *Tree2)
{
if(Tree2 == nullptr)
return true;
if(Tree1 == nullptr)
return false;
if(Tree1->val != Tree2->val)
return false;

return DoesTree1HaveTree2(Tree1->left, Tree2->left) &&
DoesTree1HaveTree2(Tree1->right, Tree2->right);
}
};


18. 二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。

/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/* 思路:相当于树的遍历 */
/*-------------- 递归方法 ------------*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == nullptr || (pRoot->left==nullptr && pRoot->right==nullptr))
return;

if(pRoot->left != nullptr)
Mirror(pRoot->left);
if(pRoot->right != nullptr)
Mirror(pRoot->right);
TreeNode *temp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = temp;
}
};

/*------------- 使用栈 ------------------*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == nullptr || (pRoot->left==nullptr && pRoot->right==nullptr))
return;

stack<TreeNode*> stackNodes;
stackNodes.push(pRoot);

while(stackNodes.size() > 0)
{
TreeNode *pNode = stackNodes.top();
stackNodes.pop();

TreeNode *pTemp = pNode->left;
pNode->left = pNode->right;
pNode->right = pTemp;

if(pNode->left != nullptr)
stackNodes.push(pNode->left);
if(pNode->right != nullptr)
stackNodes.push(pNode->right);
}

}
};


19. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int rows, columns;
if(matrix.size()>0)
{
rows = matrix.size();
columns = matrix[0].size();
}

vector<int> result;

int startX = 0, endX = columns-1;
int startY = 0, endY = rows-1;
while(startX <= endX && startY<=endY)
{
if(startX<=endX && startY<=endY)
{
for(int i=startX; i<=endX; i++)
result.push_back(matrix[startY][i]);
++ startY;
}

if(startY<=endY && startX<=endX)
{
for(int i=startY; i<=endY; i++)
result.push_back(matrix[i][endX]);
-- endX;
}
if(startX<=endX && startY<=endY)
{
for(int i=endX; i>=startX; i--)
result.push_back(matrix[endY][i]);
-- endY;
}
if(startY<=endY && startX<=endX)
{
for(int i=endY; i>=startY; i--)
result.push_back(matrix[i][startX]);
++ startX;
}
}
return result;
}
};


20. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

/* 借助辅助栈,保存每次压栈之后的最小值 */
class Solution {
public:
void push(int value) {
if(s_data.empty()){
s_data.push(value);
s_min.push(value);
}
else{
s_min.push(value<s_min.top()?value:s_min.top());
s_data.push(value);
}s
}
void pop() {
s_data.pop();
s_min.pop();
}
int top() {
return s_data.top();
}
int min() {
return s_min.top();
}
private:
stack<int> s_data;
stack<int> s_min;
};


21. 栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

/* 辅助栈:模拟整个过程 */
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
int length = pushV.size();
<

以上是关于算法系列:导航/路径规划算法的主要内容,如果未能解决你的问题,请参考以下文章

路径规划A*算法及SLAM自主地图创建导航算法

路径规划算法综述

路径规划全局路径规划算法——蚁群算法(含python实现)

ROS从入门到精通5-4:路径规划插件开发案例(以A*算法为例)

路径规划基于蚁群算法栅格地图路径规划matlab

路径规划基于蚁群算法栅格地图路径规划matlab