leetcode解题思路分析(四)22-28题
- 括号生成
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
很容易想到采用回溯法解决该题,通过画出树分析递归规律可得如下代码
class Solution {
public:
void backtrace(int left, int right, int n, string& s, vector<string>& res) {
if (left == n && right == n) {
res.push_back(s);
return;
}
if (left < n) {
s += "(";
backtrace(left + 1, right, n, s, res);
s.pop_back();
}
if (right < left) {
s += ")";
backtrace(left, right + 1, n, s, res);
s.pop_back();
}
}
vector<string> generateParenthesis(int n) {
vector<string> res;
string s;
backtrace(0, 0, n, s, res);
return res;
}
};
- 合并k个排序链表
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
本题在做过合并2个链表之后就变得很简单:采用二分法逐个合并直至剩下一个即可
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int size = lists.size();
if (size == 0) {
return nullptr;
}
if (size == 1) {
return lists[0];
}
while (size > 1)
{
for (int i = 0; i < size / 2; i++)
{
lists[i] = mergeTwoLists(lists[i], lists[i + size / 2]);
}
if (size % 2)
{
lists[size / 2] = lists[size - 1];
size = size / 2 + 1;
}
else
{
size = size / 2;
}
}
return lists[0];
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *preHead, *ptr;
preHead = new ListNode(-1);
ptr = preHead;
while (l1 != NULL && l2 != NULL)
{
if (l1->val <= l2->val)
{
ptr->next = l1;
l1 = l1->next;
}
else
{
ptr->next = l2;
l2 = l2->next;
}
ptr = ptr->next;
}
ptr->next = (l1 == NULL? l2 : l1);
return preHead->next;
}
};
- 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
非常简单的链表操作题
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *preHead = new ListNode(-1);
ListNode *tmp, *ahead;
preHead->next = head;
ahead = preHead;
while (head != NULL && head->next != NULL)
{
tmp = head->next;
ahead->next = tmp;
head->next = tmp->next;
tmp->next = head;
ahead = head;
head = head->next;
}
return preHead->next;
}
};
- K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
本题是24题的提高,为了翻转K个一组,则需要遍历k个,让每一个指针指向前一个节点,然后最前面的节点指向下一组的开始。因此考虑迭代解决
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
int d = 0;
auto node = head;
while (node != NULL) {
if (++d >= k) break;
node = node->next;
}
if (d < k) return head;
ListNode* prev = NULL;
ListNode* curr = head;
for (int i = 0; i < k; ++i) {
auto node = curr->next;
curr->next = prev;
prev = curr;
curr = node;
}
head->next = reverseKGroup(curr, k);
return prev;
}
};
- 删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
对已经排序好的数组,我们采取双指针法,检测到不同的元素再和相同元素第二位进行互换,然后指针前进即可
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.size() == 0)
return 0;
int i = 0;
for (int j = 1; j < nums.size(); j++)
{
if (nums[j] != nums[i])
{
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
};
- 移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
本题可以采取和上题类似的策略:对每个重复的元素则赋值给后面不同的元素。但是注意这里需要保证不会重复赋值,因此比较可行的方法是进行节点的交换
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
if (size == 0)
return 0;
int ptr = 0;
int end = 0;
for (int i = 0; i < size; i++)
{
if (nums[i] == val)
{
if (end < i)
end = i;
if (end == size)
return ptr;
while (nums[end] == val)
{
end++;
if (end == size)
{
return ptr;
}
}
int tmp = nums[ptr];
nums[ptr] = nums[end];
nums[end] = tmp;
end++;
ptr++;
}
else
{
ptr++;
}
}
return ptr;
}
};
但是上述做法的代码看起来颇为繁琐,其实可以更进一步的简化思想:我们只要每次把不同于val的值赋值在前列就可以完成了
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
if (nums.size() == 0)
return 0;
int i = 0;
for (int j = 0; j < nums.size(); j++)
{
if (nums[j] != val)
{
nums[i] = nums[j];
i++;
}
}
return i;
}
};
还有没有办法优化性能呢?是有的。上述方法存在一个普遍问题:对于检索过的val的值最后还会检查一遍。但是其实是不需要检查的:我们可以将重复的值替换为末尾的值, 并且不再检查它
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int i = 0;
int n = nums.size();
while (i < n) {
if (nums[i] == val) {
nums[i] = nums[n - 1];
// reduce array size by one
n--;
} else {
i++;
}
}
return n;
}
};
- 实现 strStr()
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
本题采用暴力检索的方式:找到一个相同元素则开始继续后续检查,如果检查完不一样则继续向后检索
class Solution {
public:
int strStr(string haystack, string needle) {
int i = 0, j = 0;
while(i < haystack.size() && j < needle.size())
{
if(haystack[i] == needle[j])
{
i++;
j++;
}
else
{
i = i - j +1 ;
j = 0;
}
}
if(j == needle.size())
return i - j;
return -1;
}
};
还没有评论,来说两句吧...