给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target){
unordered_map<int,int> m;
int size = nums.size();
for(int i=0;i<size;++i){
// 此处不能用nums[i]作为判断条件,因为若是0就自动停止了
int another = target-nums[i];
if(m.find(another)!=m.cend()){ // 如果在map中
return {m[another],i};
}
m.insert({nums[i],i});
}
return {};
}
};
思路:使用map,减少二次搜索的成本
O(n^2) -> O(nlogn)
执行用时 :4 ms, 在所有 cpp 提交中击败了99.76%的用户
内存消耗 :10 MB, 在所有 cpp 提交中击败了41.19%的用户
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode * l1, ListNode * l2) {
int len1 = 1, len2 = 1; // 两个链表长度信息
ListNode * p = l1, *q = l2; // 定义新节点p,q
while(p->next!=NULL){
++len1; p=p->next;
}
while(q->next!=NULL){
++len2; q=q->next;
}
// 若长度不同,则在后补零
if(len1>len2){
for(int i=1;i<=len1-len2;++i){
q->next = new ListNode(0);
q=q->next;
}
}else{
for(int i=1;i<=len2-len1;++i){
p->next = new ListNode(0);
p=p->next;
}
}
p = l1; q = l2; // p,q归位
int c = 0; // 进位符
ListNode * l3 = new ListNode(-1); // 保存结果的链表(返回值)
ListNode * w = l3; // 移动节点w
int i = 0; // 每位加和
while(p!=NULL){
i = c + p->val + q->val; // 每位加和,包括进位
w->next = new ListNode(i%10); // 每位的结果
c = (i>=10) ? 1 : 0; // 是否进位
w = w->next;
p = p->next;
q = q->next;
}
// 若最后一位还需进位
if(c){
w->next = new ListNode(1);
w = w->next;
}
return l3->next;
}
};
思路:需要考虑不等长情况补零,以及满十进位的问题;巧用指针!!
执行用时 :28 ms, 在所有 cpp 提交中击败了76.16%的用户
内存消耗 :10.2 MB, 在所有 cpp 提交中击败了91.83%的用户
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
(子串是连续的一段,子序列可以是不连续的)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int j = 0, r = 0;
//vector m;
string m; // 建议用string而非vector,利用的空间更小,链表的空间占用也较大
if(s.empty()) return 0;
else{
for(char i : s){
auto it = find(m.begin(),m.end(),i);
if(it == m.end()){
++j;
m.push_back(i); // 这种插入是倒序的,要注意!
}
// 遇到重复字符
else{
r = (r>=j) ? r : j;
m.erase(m.begin(),it+1);
m.push_back(i);
j = m.size();
}
}
r = (r>=j) ? r : j;
}
return r;
}
};
思路:(滑动窗口思想)涉及到容器中字符的查找,一开始想使用map或set,但因为涉及到删除最先插入的N个元素的顺序问题,map或set无法解决,故只能使用顺序容器,建议使用空间和速度更快的string;
使用vector:
执行用时 :16 ms, 在所有 cpp 提交中击败了67.35%的用户
内存消耗 :9.3 MB, 在所有 cpp 提交中击败了86.91%的用户
使用string:
执行用时 :12 ms, 在所有 cpp 提交中击败了81.38%的用户
内存消耗 :9 MB, 在所有 cpp 提交中击败了95.11%的用户
==============================================
另一种更好的解法(可以参考思路):
利用128个元素的向量(当做ASCII码的索引)
class Solution{
public:
int findstr(string s)
{
//s[start,end) 前面包含 后面不包含
int start(0), end(0), length(0), result(0);
int sSize = int(s.size());
vector<int> vec(128, -1);
while (end < sSize)
{
char tmpChar = s[end];
//仅当s[start,end) 中存在s[end]时更新start
if (vec[int(tmpChar)] >= start)
{
start = vec[int(tmpChar)] + 1;
length = end - start;
}
vec[int(tmpChar)] = end;
end++;
length++;
result = max(result, length);
}
return result;
}
};
执行用时 :4 ms, 在所有 cpp 提交中击败了99.17%的用户
内存消耗 :9.7 MB, 在所有 cpp 提交中击败了85.30%的用户
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
vector<int> num;
for(int i : nums1){num.push_back(i);}
for(int i : nums2){num.push_back(i);}
sort(num.begin(),num.end());
int y = num.size()%2;
int s = num.size();
if(y == 0){
return (num[s/2-1]+num[s/2])*0.5;
}else{
return (num[(s+1)/2-1]);
}
}
};
思路:最简单的集中排序求解
执行用时 :24 ms, 在所有 C++ 提交中击败了57.08%的用户
内存消耗 :10.5 MB, 在所有 C++ 提交中击败了74.23%的用户
==============================================
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int length1 = nums1.size();
int length2 = nums2.size();
int i,j,k;
vector<int> temp(length1+length2, 0);
for(i=0,j=0,k=0;i<length1&&j<length2;++k)
{
if(nums1[i]<nums2[j])
{
temp[k] = nums1[i];
i++;
}
else
{
temp[k] = nums2[j];
j++;
}
}
while(i<length1)
temp[k++]=nums1[i++];
while(j<length2)
temp[k++]=nums2[j++];
if((length1+length2)%2 ==1)
return temp[(length1+length2)/2];
else
return (temp[(length1+length2)/2]+temp[(length1+length2)/2-1])/2.0;
}
};
思路:使用了归并排序进行排序,然后再取中位数;
执行用时 :28 ms, 在所有 C++ 提交中击败了35.04%的用户
内存消耗 :9.8 MB, 在所有 C++ 提交中击败了85.00%的用户
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
string result = s;
auto iter = s.begin();
auto left = iter, right = iter;
int len = 0, maxlen = 0;
while(iter!=s.end()){
if(iter>=s.begin()&&iter+1!=s.end()){ // 单中心,优先往左右扩展
left = iter - 1;
right = iter + 1;
len = 1;
for(;left>=s.begin()&&right<s.end();--left,++right){
if(*left==*right){
len = len + 2;
}
else{
break;
}
}
if(len > maxlen){
result.assign(++left,right);
maxlen = len;
}
}
if(iter+1!=s.end()&&*iter==*(iter+1)){ // 中间两数相等
len = 2;
left = iter - 1;
right = iter + 2;
for(;left>=s.begin()&&right<s.end();--left,++right){
if(*left==*right){
len = len + 2;
}
else{
break;
}
}
if(len > maxlen){
result.assign(++left,right);
maxlen = len;
}
}
++iter;
}
return result;
}
};
思路:中心扩展法,遍历一次,最差复杂度为O(n^2),空间复杂度O(1);注意区分中间有一个字符和中间有两个字符的情况;
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31−1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
class Solution {
public:
int reverse(int x) {
string s = to_string(x); // 转换为string
int num = 0, sign = 1; // 结果num,正负号sign
if(*s.begin()=='+'||*s.begin()=='-'){
if(*s.begin()=='-') sign = -1; // 如果为负
s.assign(s.begin()+1,s.end());
}
std::reverse(s.begin(),s.end()); // 翻转字符串
try{
num = stoi(s); // 尝试执行string转int
}catch(exception){
return 0; // 如果失败,返回0
}
return num * sign; // 不然返回结果
}
};
// 一种更简单的循环计算方法
class Solution {
public:
int reverse(int x) {
int num = 0;
while(x!=0){
if(num>214748364||num<-214748364) return 0;
num = num * 10 + x % 10;
x = x / 10;
}
return num;
}
};
思路:简单的题,灵活利用string的各种功能,使用了try/catch异常,注意泛型函数的使用;注:int的范围是-2147483648~2147483647.
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
class Solution {
public:
int myAtoi(string str) {
if(str.empty()) return 0;
long int num = 0;
int sign = 1;
auto iter = str.begin();
while(*iter==' '){ // 开头有空格
++iter;
}
if(iter!=str.end()&&(*iter=='+'||*iter=='-')){ // 有+-号
if(*iter=='-') sign = -1;
++iter;
}
if(iter!=str.end()&&(*iter<='9'&&*iter>='0')){ // 如果这时第一个字符是数字
while(iter!=str.end()){ // 遍历连续数字
if(*iter<='9'&&*iter>='0'){ // 如果是数字
num = num * 10 + (*iter - '0');
if(sign*num>2147483647){ // 如果超出int范围
return INT_MAX;
}
else if(sign*num<-2147483648){
return INT_MIN;
}
}
else{ // 如果遇到别的字符
break;
}
++iter;
}
}
else{
return 0;
}
return static_cast<int>(num * sign);
}
};
思路:没啥难度,注意考虑周全;
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
class Solution {
public:
bool isMatch(string s, string p) {
if(s.empty()&&p.empty()) return true; // 如果两数组为空,则匹配
return isMatchCore(s,p,0,0); // 不然则进入递归
}
bool isMatchCore(string& s,string& p,int i,int j){
if(i>=s.size()&&j>=p.size()) return true; // 如果两个指针都到头了,则匹配
if(i<s.size()&&j>=p.size()) return false; // 如果字符串没到头但模板到头了,则不匹配
if(j+1<p.size()&&p[j+1]=='*'){ // 遇到x*形式
if(s[i]==p[j]||(p[j]=='.'&&i<s.size())){ // 当前字符相等或者是.*形式时
return isMatchCore(s,p,i,j+2) // 没有匹配,直接跳过
|| isMatchCore(s,p,i+1,j+2) // 匹配最后一个字符
|| isMatchCore(s,p,i+1,j); // 匹配当前一个字符
}
else{ // 当前字符就不匹配
return isMatchCore(s,p,i,j+2); // 跳过c*段
}
}
else{ // 当前不是x*模式(字符串可能已走完)
if(s[i]==p[j]||(p[j]=='.'&&i<s.size())){ // 当前字符相等,或遇到.模式
return isMatchCore(s,p,i+1,j+1);
}
return false;
}
}
};
思路:正则表达式匹配问题,最好的方法是动态规划,匹配当前部分,然后匹配后面的部分。利用递归解决比较方便,但复杂度较高;有空可以了解一下dp数组循环方法;
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
示例:输入: [1,8,6,2,5,4,8,3,7] 输出: 49
class Solution {
public:
int maxArea(vector<int>& height) {
if(height.empty()||height.size()==1) return 0; // 如果为空或只有一个值
int left = 0, right = height.size()-1; // 定义左右两端指针
int temp, max = 0; // 存放最大容量
while(right>left){ // 遍历,直到到头
// 计算当前最大容量,然后将两个指针中较短的那个向中间移动;注意最后的底长要加1,因为前面有自增量操作;
temp = (height[left]<=height[right]?height[left++]:height[right--])*(right-left+1);
max = temp>max?temp:max;
}
return max;
}
};
思路:想法并不难,但不是很好想;左右两端指针,向中间移动;盛水容量只和当前两个指针中较短的那个有关,如果将较长的指针向中间移动,只会使得容量变得更小,所以每次移动较短的指针,这样可能会继续增大容量;
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
class Solution {
public:
// I 1 V 5 X 10 L 50
// C 100 D 500 M 1000
int romanToInt(string s) {
if(s.empty()) return 0;
int iter = 0;
int sum = 0;
while(iter!=s.size()){
if(isLessThanNext(s,iter)){ // 如果遇到特殊情况IV、IX、XL、XC、CD、CM
sum = sum + getnum(s[iter+1]) - getnum(s[iter]);
iter = iter + 2;
}
else{
sum = sum + getnum(s[iter]);
iter = iter + 1;
}
}
return sum;
}
bool isLessThanNext(string& s,int i){
if(i+1!=s.size()){ // 当前下标i不是最后一字符
if(getnum(s[i])<getnum(s[i+1])){
return true;
}
else return false;
}
return false;
}
int getnum(char c){
switch(c){
case 'I' : return 1;
case 'V' : return 5;
case 'X' : return 10;
case 'L' : return 50;
case 'C' : return 100;
case 'D' : return 500;
case 'M' : return 1000;
}
return 0;
}
};
思路:比较简单的题,只需要区分两种情况:之间加上当前字符表示的值、或判断当前字符小于下一个字符时,加上下一个字符减去当前字符的数字;
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty()) return ""; // 不到两个字符串
if(strs.size()==1) return strs[0]; // 如果只有一个字符串,则返回自身
int j = 0; // 公共前缀位数j
string result; // 结果字符串
bool isCommon; // 如果当前字符是公共字符
while(j<strs[0].size()){ // 公共前缀位数j
isCommon = true;
for(int i=0;i<strs.size()-1;++i){ // 每个字符串i
if(j>=strs[i].size()||j>=strs[i+1].size()||strs[i][j]!=strs[i+1][j]){
isCommon = false;
break;
} // 若当前字符串的j位和下一个字符串的j位不相等或j位越界;
}
if(isCommon){ // 循环完毕,当前字符是公共字符
result.push_back(strs[0][j]); // 输出该公共字符
++j; // 继续循环,不要忘了递增j
}
else{
break;
}
}
return result;
}
};
思路:从每个字符串的第一个字符开始判断,如果他们都相等,则说明这个字符是公共前缀的字符;然后依次判断,直到出现第一个不是公共字符的前缀,则停止;
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int> > result; // 结果数组
if(nums.size()<3) return result; // 如果数组元素小于3个,直接返回空结果
sort(nums.begin(),nums.end()); // 数组排序
if(nums.front()>0||nums.back()<0) return result; // 如果全为负或者全为正,肯定无结果
int temp, i = 0; // temp保存暂时的和,i为当前第一个数的下标
int left, right; // 定义第二个数和第三个数的下标
while(i<nums.size()&&nums[i]<=0){ // 当三数和的最小值都大于0时,和不可能等于0,循环结束
left = i + 1;
right = nums.size() - 1;
while(left<right){ // 遍历,直到left和right相遇
temp = nums[i] + nums[left] + nums[right];
if(temp==0){ // 如果当前符合结果
result.push_back({nums[i],nums[left],nums[right]}); // 输出
++left; // left右移
while(left<right&&nums[left]==nums[left-1]) ++left; // 排除当前的重复数字
--right; // right左移
while(left<right&&nums[right]==nums[right+1]) --right; // 排除当前的重复数字
}
else if(temp>0) --right;
else ++left;
}
++i;
while(i<nums.size()&&nums[i]==nums[i-1]) ++i; // 一定要注意越界问题!
}
return result;
}
};
思路:避免使用暴力搜索和扩展额外空间,该题可以先进行排序,然后根据排序后的数组列出三个指针,第一个指针指向数组第一个数字,其余两个指向后面的数组的头尾,并逐渐向中间遍历;注意代码简洁!注意数组下表越界问题!注意重复的组合处理问题!!
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
case ‘2’ : return {“a”,“b”,“c”};
case ‘3’ : return {“d”,“e”,“f”};
case ‘4’ : return {“g”,“h”,“i”};
case ‘5’ : return {“j”,“k”,“l”};
case ‘6’ : return {“m”,“n”,“o”};
case ‘7’ : return {“p”,“q”,“r”,“s”};
case ‘8’ : return {“t”,“u”,“v”};
case ‘9’ : return {“w”,“x”,“y”,“z”};
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
// 不用递归的笨方法,但不影响效率
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> result;
vector<string> letter;
if(digits.empty()) return result;
for(int i=0;i<digits.size();++i){
letter = NumToLetter(digits[i]); // 获得当前数字代表的字符vector
if(!letter.empty()){
CombineLetter(letter,result); // 将当前数字代表的字符添加到结果数组中
}
}
return result;
}
vector<string> NumToLetter(char c){
switch(c){
case '2' : return {"a","b","c"};
case '3' : return {"d","e","f"};
case '4' : return {"g","h","i"};
case '5' : return {"j","k","l"};
case '6' : return {"m","n","o"};
case '7' : return {"p","q","r","s"};
case '8' : return {"t","u","v"};
case '9' : return {"w","x","y","z"};
default : return {};
}
}
void CombineLetter(vector<string>& letter,vector<string>& result){
if(result.empty()){ // 若result还为空
for(auto s : letter){
result.push_back(s);
}
}
else{ // 若result已有值
// 先用letter中除第一个字符外的其它字符创建新的组合添加到result队尾
int size = result.size(); // 提前保存这时result的大小(很重要)
for(int i=1;i<letter.size();++i){ // 从letter中除去第一个字符后循环
for(int j=0;j<size;++j){ // 循环result
string temp = result[j] + letter[i];
result.push_back(temp);
}
}
// 再用letter中的第一个字符填充result中的原有组合
for(int i=0;i<size;++i){
result[i].append(letter[0]);
}
}
}
};
思路:一个数字一个数字添加,然后直接在result里组合加上这一个字母的组合,部分实现细节不太好想,但也不难; 缺点:可以用一个数组或map解决的数字字母对应问题,却写成了麻烦的函数;总体利用DFS回溯思想更简单;
// DFS回溯法
class Solution {
vector<string> NumToLetter = { // 自建哈希表数组
"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
vector<string> letterCombinations(string digits) {
vector<string> result; // 结果矩阵
if(digits.empty()) return result; // 如果数组为空
string temp; // 暂存目前的深度优先搜索结果
DFS(digits,0,temp,result); // 利用递归进行深度优先遍历
return result;
} //
void DFS(string& digits, int pos, string& temp, vector<string>& result){
if(pos>=digits.size()){ // 如果当前组合数目(如"ae")和digits(如”12“)长度相等了
result.push_back(temp); // 保存当前的结果
}
else{
string s = NumToLetter[digits[pos]-'0']; // 得到与当前遍历到的数字相对应的那个字符串
for(int i=0;i<s.size();++i){ // 遍历该字符串
temp.push_back(s[i]); // 组合当前这个字符串中的其中一个字符
DFS(digits,pos+1,temp,result); // 遍历下一个数字
temp.pop_back(); // 取消组合当前这个字符,转换到组合下一个字符
}
}
}
};
思路:比较难想的思路,类似于求全排列的递归形式,但也不相同;类似于求从每个组中都抽出一个字符然后求排列;熟练利用DFS和递归思想是关键;
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:给定的 n 保证是有效的。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==nullptr) return nullptr;
ListNode* left = head;
ListNode* right = head;
for(int i=0;i<n;++i){ // 多移动一次,让left停在要删除节点的前面节点
right = right->next;
}
if(right!=nullptr){ // 不是删除头节点时
while(right->next!=nullptr){
left = left->next;
right = right->next;
}
ListNode* temp = left->next;
left->next = left->next->next;
delete temp; temp = nullptr;
}
else{ // 要删除头节点
ListNode* temp = left;
head = head->next;
delete temp; temp = nullptr;
}
return head;
}
};
思路:注意链表删除操作时的余量,另外注意删除的如果是头节点的情况;
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
class Solution {
public:
bool isValid(string s) {
if(s.empty()) return true;
stack<char> stk;
for(int i=0;i<s.size();++i){
if(s[i]=='('||s[i]=='{'||s[i]=='['){ // 如果是左括号,则入栈
stk.push(s[i]);
}
else{ // 如果是右括号,栈不为空且栈首对应则出栈
if(!stk.empty()&&
((s[i]==')'&&stk.top()=='(')
||(s[i]=='}'&&stk.top()=='{')
||(s[i]==']'&&stk.top()=='['))){
stk.pop();
}
else{
return false;
}
}
}
if(stk.empty()) return true;
else return false;
}
};
思路:利用栈进行挨个匹配,注意使用stack.top()的时候要注意前提是栈不为空;另外注意匹配的准则是栈最后要弹空才行;
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1==nullptr) return l2;
if(l2==nullptr) return l1;
ListNode* node;
if(l1->val<l2->val){
node = l1;
node->next = mergeTwoLists(l1->next,l2);
}
else{
node = l2;
node->next = mergeTwoLists(l1,l2->next);
}
return node;
}
};
思路:可以利用递归或非递归的方法解决,递归方法很巧妙比较难想,循环的方法直观简单但比较繁琐;