概述
1.电话号码的字母组合, 这题其实是更新一个vector数组,输入有多长更新几次,每次erase后,len会改变。
2.Z字形变换,只要关注当前行,和前进的方向(即数组的下标,何时该往哪个数组里append)
3.整数反转,自己做出来的,整数转字符串,逆序,再用stringstream做,判断是否溢出
4.删除链表中倒数第N个节点,自己做出来的,双指针,考虑两种特殊情况,1只有一个节点。2要删的是头节点
5.全排列,这题比较难想,给一个数组,使用回溯法求排列树,以交换代替压栈存储。回溯函数三参数(数据集,结果,当前层)
6.搜索旋转排序数组。这题关键是二分查找和异或操作,返回值也比较精简!
1.电话号码的字母组合
题目描述:如同9宫格键盘一样,数字2-9有分别代表着不同的字母,给定一个只包括数字2-9的字符串,返回可能组成的不同的字符串组合。
思路:
1.把9个数字所表示的字符传放入vector容器
2.定义结果存放容器c,
往里面压入"",
c为[""]
第一次遍历后c中元素为["","a","b","c"], 第一次erase后为["a","b","c"]
第二次遍历后c中元素为["a","b","c","ad","ae"...] 第二次erase后为[...]
详情看代码:len表示前一次处理好的字符串的个数,
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> a{ "abc","def","ghi","jkl","mno","pqrs","tuv","wxyz" };
vector<string> c;
if (digits.empty()) return c;
c.push_back("");
for (int i = 0; i < digits.size(); ++i) {
int res = digits[i] - '2';
int len = c.size();
cout << "len is :" << len << endl;
for (int j = 0; j < len; ++j) { //遍历前一次对应的字符
for (auto m : a[res]) { //遍历当前按键所对应的字符
c.push_back(c[j] + m); //压入c
}
}
c.erase(c.begin(), c.begin() + len); //c是数组先存放空格,第二次存放首个数字代表的几个字母,第三次存放第二个数字代表的字符基于前一次,每次都要擦除前一次(相当于中间过程)的数据
}
return c;
}
};
2.Z字形变换
题目描述:
给一个字符串和一个行数,把字符串以从上往下、从左到右Z字形排列。返回按行序返回。
示例 1:
输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"
示例 2:
输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
解释:
L D R
E O E I I
E C I H N
T S G
思路:本题核心就是一个容器,存放n个字符串,根据移动的方向,往对应的容器中压字符就可以了。
1.定义rows容器,存放字符。rows是一个有min(numRow,s.size())个元素的字符串数组容器
2.定义当前行和方向,curRow,goingDown
3.遍历s,当前行的字符存入rows,如果当前行为第0行,或者最后一行,方向改变。
如果方向向下,当前行加一,否则减一
class Solution {
public:
string convert(string s, int numRows) {
if(1 == numRows){return s;}
vector<string>temp(min(int(s.size()),numRows));
int curRow = 0;
bool goingdown = false;
for(char c:s){
temp[curRow] += c;
if(curRow == 0|| curRow == numRows-1)
goingdown = !goingdown;
curRow += goingdown ? 1:-1;
}
string ret = "";
for(auto ch:temp){ret += ch;}
return ret;
}
};
3.整数反转,自己写出来 的
题目描述:给定一个有符号整数,反转他。120反转后21,等反转后如果超过32位系统的int类型范围,则返回0;
思路:
1.先把x转化成字符串
2.反转这个字符串,因为reverse()不是solution的成员函数,所以不能用。。。因此用swap依次交换,交换前把符号提出
____后面莫名其妙又可以用reverse直接反转,真奇怪!std::reverse(s.begin(),s.end())
3.使用stringstream把反转后的字符串转化为int,并判断是否溢出
class Solution {
public:
int reverse(int x) {
string s = "";
s += to_string(x);
cout<<s<<endl;
if(s[0] == '+'||s[0] =='-'){
int i = 1;
int k = s.size()-1;
while(i<k){swap(s[i++],s[k--]);}
}else{
//reverse(s.begin(),s.end());
int i = 0;
int k = s.size()-1;
while(i<k){swap(s[i++],s[k--]);}
cout<<s<<endl;
}
stringstream ss;
ss<<s;
int res;
ss>>res;
cout<<int(pow(2,31)-1)<<endl;
if(res<-int(pow(2,31))||res>int(pow(2,31))-1) return 0;
return res;
}
};
4.删除链表的倒数第N个节点,自己A出来
题目描述:给定一个链表和一个数字,删除倒数第n个节点,尝试用一趟扫描实现。
思路:双指针。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* p = head;
ListNode* q = head;
if(n == 1 && head->next == nullptr){return nullptr;}
//ListNode* k = head;
int k = n;
while(k&&p->next){p = p->next;k--;}
if(k == 1){
head = head->next;
return head;
}
while(p->next){
p = p->next;
q = q->next;
}
ListNode* temp = q->next;
q->next = temp->next;
delete temp;
return head;
}
};
5.全排列(比较难想)
给定一个没有重复数字的数组,返回他所有可能的全排列
参考:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-c-by-she-wo-qi-shui/
思路:用回溯法解决。回溯法求排列树
1.把回溯核心分离出来,backtrack(vector<int>&nums,vector<vector<int>>&res,int i),回溯函数参数一般有三个,一个放数据集,一个放结果,一个表示当前层级
2.如果数组为空,那就压入res
3.遍历数组(从i开始),交换nums[i]和nums[j]
4.递归处理,backtrack(nums,res,i+1)
5.再次交换,这一步在正常的回溯算法中一般是s.pop_back(),回溯到上一步,在此地同样作用,交换回到上一种状态
class Solution {
public:
void backtrack(vector<int> &nums,vector<vector<int>>&res,int i){
if(nums.size() == i) res.push_back(nums); // 如果i已经和数组一样长,表示这一种组合方式结束
for(int j = i;j<nums.size();++j){
swap(nums[i],nums[j]);
backtrack(nums,res,i+1);
swap(nums[i],nums[j]);
}
}
void swap(int& a,int& b){
int temp = a;
a = b;
b = temp;
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>res;
backtrack(nums,res,0);
return res;
}
};
6.搜索旋转排序数组
题目描述:给定一个旋转数组,和一个查找目标,logn查找,如果找到返回位置,找不到返回-1
思路:折半查找,用异或解决简直无敌
1.定义数组开头和结尾的指针
2.二分查找,注意循环结束的条件不带等号。
3.定义mid,
4.核心,三个条件异或,(开头元素大于目标)^(开头元素大于中间元素)^(目标大于中间元素)
0^0 is 0 0^1 is 1 1^1 is 0 1^0 is 1
5.返回位置,return start == end&&target == nums[start] ? start:-1;
class Solution {
public:
int search(vector<int>& nums, int target) {
//折半查找
int start = 0;
int end = nums.size()-1;
while(start<end){
int mid = (start+end)>>1;
if((nums[start]>nums[mid])^(nums[start]>target)^(target>nums[mid])) start = mid+1;
else end = mid;
}
return start==end&&target == nums[start] ? start:-1;
}
};