目录
一.Leetcode17:电话号码的字母组合
1.问题描述
2.问题分析与求解
3.递归函数的建立
4.题解代码
二.leetcode118. 杨辉三角(二维vector的运用)
一.Leetcode17:电话号码的字母组合
1.问题描述
- 电话号码的字母组合 - 力扣(Leetcode)
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
注意:字符串digits的长度为0到4闭区间范围内。
C++题解接口:
class Solution
{
public:
vector<string> letterCombinations(string digits)
{
}
};
2.问题分析与求解
以digits="23"为例建立字符排列组合的树形图:
根据三叉树的结构我们尝试建立递归(树的每一个节点就是一次函数调用):
三叉树的每层的层数我们记为level(level的最大值由digits字符串的有效字符个数决定)
为了确定每一层树节点中的phonestr字符串我们需要建立一个简单的映射表strmap:
string strmap[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
映射表strmap是一个由10个string对象构成的数组:
每个string对象中存储的字符串与其下标的映射关系刚好与题目中字符与按键的映射关系一致。
3.递归函数的建立
建立递归函数首部:
void _deepSearch (const string&digits , vector&answer , int level, string&temstr)
digits是题目给出的按键字符串,answer是用于存储各个可能字母组合的由string对象构成的vector,level用于控制递归的深度(层数),当level等于digits字符串的长度时停止递归,temstr是存储临时字母组合的string对象,当递归达到最深层时,就将temstr对象存入answer中。
递归函数中控制递归条件的语句:
if(level == digits.size())
{
answer.push_back(temstr); 递归到最深层,将temstr存入answer中
return ;
}
每一次函数调用中确定phonestr字符串(记录了每层递归中参与组合的字符)的语句:
string phonestr(strmap[digits[level]-'0']);
多叉树向更深层次展开的语句:
for (int i = 0; i < phonestr.size(); i++)
{
temstr.push_back(phonestr[i]); 将字符尾插到字符组合中
_deepSearch(digits, answer, level + 1, temstr);
temstr.pop_back(); 完成递归调用后要将该层中插入的字符删掉完成回溯
}
完整的递归函数代码:
void _deepSearch(const string& digits, vector& answer, int level, string& temstr)
{
if (level == digits.size())
{
answer.push_back(temstr);
return;
}
string phonestr(strmap[digits[level] - '0']);
for (int i = 0; i < phonestr.size(); i++)
{
temstr.push_back(phonestr[i]);
_deepSearch(digits, answer, level + 1, temstr);
temstr.pop_back();
}
}
递归树遍历顺序:
4.题解代码
class Solution
{
private:
string strmap[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
void _deepSearch (const string&digits , vector<string>&answer , int level, string&temstr)
{
if(level == digits.size())
{
answer.push_back(temstr); //将可能的字母组合存入answer容器中
return ;
}
string phonestr(strmap[digits[level]-'0']);
for(int i =0; i<phonestr.size();i++)
{
temstr.push_back(phonestr[i]);
_deepSearch(digits,answer,level+1,temstr);
temstr.pop_back(); //尾插字符后再尾删字符完成字符串回溯
}
}
public:
vector<string> letterCombinations(string digits)
{
vector<string> answer;
if(digits.empty()) //检查digits是否为空字符串
{
return answer;
}
string temstr; //用于存储每个'树叶'临时得到的字母组合
_deepSearch(digits,answer,0,temstr);
return answer;
}
};
注意:
题解接口函数中注意检查digits是否为空字符串
二.leetcode118. 杨辉三角(二维vector的运用)
- 杨辉三角 - 力扣(Leetcode)
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
示例 :
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
C语言题解接口:
int generate(int numRows, int* returnSize, int returnColumnSizes)
{
}
本题如果使用C语言求解需要在堆区上模拟出一个numRows行numRows列的二维数组。
int parr = (int )malloc(sizeof(int)numRows);
int i =0;
for(i=0;i<numRows;i++)
{
parr[i]= (int *)malloc(sizeof(int)*(i+1));
}
图解堆区上的二维数组:
C语言中堆区上的动态二维数组有如下几个不方便使用的小缺点:
在堆区上多次用malloc申请随机地址的空间,会使堆区上的内存碎片增多,内存利用率降低
多个堆区上的内存区块释放时很麻烦,让内存管理变得繁琐,容易造成内存泄漏
多级指针和动态的指针数组未经封装,会让代码的可阅读性和可维护性降低
本题使用C语言写起来比较恶心,用C++的二维vector写起来会舒服很多
C++题解接口:
class Solution
{
public:
vector<vector<int>> generate(int numRows)
{
}
};
题解代码:
class Solution
{
public:
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> answer(numRows);
int i =0;
int j=0;
for(i=0;i<numRows;i++)
{
answer[i].resize(i+1);
answer[i][0]=1;
answer[i][i]=1;
}
for(i=2;i<numRows;i++)
{
for(j=1;j<i;j++)
{
answer[i][j]=answer[i-1][j]+answer[i-1][j-1];
}
}
return answer;
}
};
vector的带参构造函数其中一个重载形式:
explicit vector (size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
可见需要用到动态二维数组时,vector使用起来会非常方便