LeetCode C/C++语言版详解(16-20)
PS:分享下C转C++的原因
为了节省时间,我还是快速上手了一下C++。我发现只要学习过C语言,C++的语句有些不看解释都能看得懂它的意思,这就很舒服。我要分享一下一篇优秀的文章,《从放弃C语⾔到使⽤C++刷算法的简明教程》by 柳婼,这篇文章详细的讲了一下C和C++优缺点的对比,因为在CSDN上分享资源不能设置0积分,所以我直接用百度网盘链接分享了
链接:https://pan.baidu.com/s/1Ow8LWnbgNuyPI0naxPaN9A
提取码:legk
16 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
题目越往后,我发现根本就没有C语言实现的博客了,我来开荒了!
int inc (const void * a,const void *b)
{
return *(int *)a - *(int *)b;
}
int threeSumClosest(int* nums, int numsSize, int target){
int result = 0;
if(numsSize <= 3)// 判断是否小于三,小于就直接返回相加的值
{
for(int i = 0; i < numsSize; i++)
result += nums[i];
return result;
}
qsort(nums, numsSize, sizeof(int), inc);// qsort函数(数组,数组个数,所占字节数,内置函数),不懂可以自己去搜一下,这里的调用的函数是上面的inc
result = nums[0] + nums[1] + nums[2];// 初始化的结果值
int dis = abs(result-target);// 与target相减后取绝对值,然后循环比较,绝对值越小,则相加的数越接近
for(int i = 0; i < numsSize; i++)
{
int j = i + 1;
int k = numsSize - 1;
while(j < k)
{
int temp = abs(nums[i] + nums[j] + nums[k] - target);
if(temp < dis)// 优先判断是否最接近
{
dis = temp;
result = nums[i] + nums[j] + nums[k];
}
if(nums[i] + nums[j] + nums[k] < target) // 相加比taget小,则j向后移
{
j += 1;
}
else if(nums[i] + nums[j] + nums[k] > target)// 相加比taget大,则k向前移
{
k -= 1;
}
else// 如果刚好,那就是相等,直接返回
{
return nums[i] + nums[j] + nums[k];
}
}
}
return result;
}
17 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
递归
/**
* Return an array of size *returnSize.
* Note: The returned array must be malloced, assume caller calls free().
*/
void save(char* digits, char** map, char* before, int len, int start, char*** result, int* size) {
if(start == len)j
{
(*size)++;
result[0] = (char**)realloc(result[0], *size * sizeof(char*));// 不懂可以去查一下malloc、realloc、calloc的区别
result[0][*size - 1] = (char*)malloc((len + 1) * sizeof(char));
memcpy(result[0][*size - 1], before, len * sizeof(char));
result[0][*size - 1][len] = '\0';
return;
}
char* temp = map[digits[start] - '1'];// 1对应的是没有,所以对应下标为0,故-‘1’
while(*temp)// 遍历这一段字符串的每一个字符
{
before[start] = *temp;//并且将该字符为开头的字符串放入before中
save(digits, map, before, len, 1 + start, result, size);
/*
*start初值为0,当它遍历第一层,则1 + start,遍历第二层则为1 + (1 + start)
*在上面的if判断中,start如果跟len长度相等,就往result存值
*/
temp++;
}
}
char** letterCombinations(char* digits, int* returnSize) {
int len = strlen(digits);
*returnSize = 0;// 我执行了几遍并且打印出来都是随机的,所以returnSize给的时候是个随机数,所以要初始化一下
if(len == 0)
return NULL;
char* map[9] = {"", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};// 初始化1到9对应的字母
char** result = NULL;
char* before = (char*)malloc(len * sizeof(char));
save(digits, map, before, len, 0, &result, returnSize);
/*
*传递digit这个字符串,思路从左到右相减‘1’,来得到其map值
*map存储1-9中的对应字母字符串
*before里存的是个遍历中的1-9相对应的一个字符串,比如 2 对应的 “abc”,那么before在对应2的时候里面存储的就是“abc”
*len是对应的长度,如果是输入了两个数字,那么len就是2,如果输入为三个数字,那么len就是3,
*因为输入两个数字,拼出来的是两位的字母,输入三位则是三位的字母
*0是当前起始位置
*result就是用来存放返回的所有值的
*returnSize就是递归函数中对应的size,用来存放有多少个不同值
*/
return result;
}
18 四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
四数之和就是在三数之和的基础上再套一层循环即可
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int n = nums.size();
set<vector<int> > s;
for(int i = 0;i < n;i++)
{
if(i > 0 && nums[i - 1] == nums[i])
continue;
for(int j = i + 1;j < n;j++)
{
int start = j + 1,end = n - 1;
while(start < end)
{
int temp = nums[start] + nums[end] + nums[i] + nums[j];
if(temp == target && i < j)
{
s.insert(vector<int>{nums[start],nums[end],nums[i],nums[j]});
while(start < end && nums[start] == nums[start + 1])
start++;
while(start < end && nums[end] == nums[end - 1])
end--;
start++;
end--;
}
else if(temp > target)
end--;
else
start++;
}
}
}
return vector<vector<int> >(s.begin(),s.end());
}
};
19 删除链表的倒数第N个节点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
这个思路是真的机智!!!
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(!head | !head -> next) return NULL;// 判断是否只有0或1个
ListNode * fast = head, *slow = head;
for(int i = 0; i < n; i++)// 首先你想取倒数第几个的数值,那么,我先把这个空隙放好,就是这个fast
{
fast = fast -> next;
}
if(!fast)
{
return head -> next;
}
while(fast -> next)// 然后从这个空隙一直循环到没有位置,这样我的slow就会到达倒数第那个值的地方了
{
fast = fast -> next;
slow = slow -> next;
}
slow -> next = slow -> next -> next;// 直接将它的下一个等于下下个就行
return head;
}
};
20 有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
思路是如果成对就把这两个消掉
class Solution {
public:
bool isValid(string s) {
int i = 0;
while(i<s.length())// 如果有配对则将s去掉这两个,并且判断i是否往前移,如果没有配对,则i向后走
{
if(s[i+1]-s[i] == 1 || s[i+1]-s[i] ==2)// 我查了一下ASCII表,除了()以外,其他两种括号都间隔一个字符,所以相邻是否为1或者为2,如果成立则这两个消除
{
s.replace(i,2,"");
if(i>0)
{
i--;
}
}
else
{
i++;
}
}
if(s != "")// 如果s中还有,证明不配对,反之则配对
return false;
else
return true;
}
};