通往【LeetCode - 两数之和】的任意门
解法一:暴力解
采用两层for循环,第一次层循环选取第一个数,第二次循环选取第二个数,第二层循环找到符合的数则直接返回结果。
时间复杂度:O(n^2)
空间复杂度:O(1)
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
for (int i = 0; i < nums.size(); i ++) {
for (int j = i + 1; j < nums.size(); j ++) {
if (nums[i] + nums[j] == target) {
res.push_back(i);
res.push_back(j);
return res;
}
}
}
return res;
}
解法二:使用map
第一次遍历数组,初始化map,判断map是否存在key为nums[i]
的键值对,不存在则将(nums[i], i)
插入map中,存在则继续下一轮循环。
第二次遍历数组,若能够在map中找到key为target - nums[i]
,且value != i
的键值对,则返回结果。
时间复杂度:O(n)
空间复杂度:O(n)
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
// 定义map并初始化
map<int, int> numMap;
for (int i = 0; i < nums.size(); i ++) {
if (numMap.find(nums[i]) == numMap.end()) {
numMap.insert(pair<int, int>(nums[i], i));
}
}
for (int i = 0; i < nums.size(); i ++) {
map<int, int>::iterator it = numMap.find(target - nums[i]);
// 注意第二个条件,保证两个下标不同
if (it != numMap.end() && (*it).second != i) {
res.push_back(i);
res.push_back((*it).second);
return res;
}
}
return res;
}
通往【LeetCode - 两数相加】的任意门
两个链表相加,主要要解决两个链表长度不一致以及进位的问题。
长度不一致问题:可分为三种情况,第一,两个链表都还有未处理的节点;第二,只有链表一还有未处理的节点;第三,只有链表二还有未处理的节点。
进位问题:设置一个变量标记低一位的运算是否有进位。
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
// 判断边界条件
if (l1 == NULL || l2 == NULL) {
return l1 == NULL ? l2 : l1;
}
// 创建存储结果链表的虚拟头结点
ListNode *head = new ListNode(-1, NULL);
ListNode *cur = head;
// 前一位的进位
int addBit = 0;
// l1与l2都还有未计算的结点
while (l1 != NULL && l2 != NULL) {
int res = l1->val + l2->val + addBit;
if (res >= 10) {
addBit = 1;
cur->next = new ListNode(res - 10, NULL);
} else {
addBit = 0;
cur->next = new ListNode(res, NULL);
}
cur = cur->next;
l1 = l1->next;
l2 = l2->next;
}
// 仅l1还有未计算的结点
while (l1 != NULL) {
int res = l1->val + addBit;
if (res >= 10) {
addBit = 1;
cur->next = new ListNode(res - 10, NULL);
} else {
addBit = 0;
cur->next = new ListNode(res, NULL);
}
cur = cur->next;
l1 = l1->next;
}
// 仅l2还有未计算的结点
while (l2 != NULL) {
int res = l2->val + addBit;
if (res >= 10) {
addBit = 1;
cur->next = new ListNode(res - 10, NULL);
} else {
addBit = 0;
cur->next = new ListNode(res, NULL);
}
cur = cur->next;
l2 = l2->next;
}
// 判断最后是否有进位
if (addBit == 1) {
cur->next = new ListNode(1, NULL);
}
ListNode* temp = head->next;
// 释放虚拟头结点
head->next = NULL;
delete head;
head = NULL;
return temp;
}
通往【LeetCode - 无重复字符的最长子串】的任意门
利用map缓存当前无重复字符的字符及其对应位置,采用滑动窗口的思想求出无重复字符子串的最大长度
int lengthOfLongestSubstring(string s) {
if (s == "") {
return 0;
}
// 将字符串转化为char数组
char buf[s.size()];
s.copy(buf, s.size());
// 用于记录当前子串的字符及对应下标
map<char, int> charMap;
// 用于记录无重复字符的子串的最大值
int max = 0;
// 当前子串的开始与结束位置,[left, right)
int left = 0, right = 0;
// 利用滑动窗口的思想求无重复字符的最长子串
while (right < s.size()) {
// 判断是否出现重复字符
map<char, int>::iterator it = charMap.find(buf[right]);
if (it != charMap.end()) {
// 存在则将left 到 前一次出现该字符的位置 的字符从map中删除
int val = (*it).second;
for (int i = left; i <= val; i++) {
charMap.erase(buf[i]);
}
// 更新max与left
max = max > (right - left) ? max : (right - left);
left = val + 1;
charMap.insert(pair<char,int>(buf[right], right));
} else {
charMap.insert(pair<char,int>(buf[right], right));
}
right ++;
}
// 最后判断是否需要更新max
max = max > (right - left) ? max : (right - left);
return max;
}
通往【LeetCode - 最长回文子串】的任意门
解法一:暴力枚举
string longestPalindrome(string s) {
// 处理边界条件
if (s.size() < 2) {
return s;
}
// 将字符串转化为char数组
char buf[s.size()];
s.copy(buf, s.size());
// 最长回文子串的长度,起始位置,结束位置
int maxLen = 0;
// 最长回文子串buf[start, end]
int start = 0, end = -1;
// 暴力枚举,枚举每个位置的字符都作为起始位置和结束位置
for(int i = 0; i < s.size(); i ++) {
for(int j = i; j < s.size(); j ++) {
// 先判断当前子串是否大于最大长度
// 若当前子串长度小于最大长度,则不需要进行是否为回文串的判断
if ((j - i + 1) > maxLen && palindrome(buf, i, j)) {
maxLen = j - i + 1;
start = i;
end = j;
}
}
}
return s.substr(start, maxLen);
}
// 判断buf[low, high]是否为回文子串
bool palindrome(char *buf, int low, int high) {
while (low < high) {
if (buf[low] != buf[high]) {
return false;
}
low ++;
high --;
}
return true;
}
解法二:中心扩散法
string longestPalindrome(string s) {
// 处理边界条件
if (s.size() < 2) {
return s;
}
// 将字符串转化为char数组
char buf[s.size()];
s.copy(buf, s.size());
// 最大回文子串的长度,起始位置,结束位置
int maxLen = 1;
int start = 0, end = 0;
// 遍历字符,以每个字符作为回文串的中心进行扩展
int low = 0, high = 0;
for(int i = 0; i < s.size(); i ++) {
low = i;
high = i;
// 若中心字符左右相邻两边存在与其相同的字符,则将这些相同的字符一起作为扩散的中心
while (low - 1 >= 0 && buf[i] == buf[low - 1]) low --;
while (high + 1 < s.size() && buf[i] == buf[high + 1]) high ++;
// 两边扩散
while (low > 0 && high < s.size() - 1 && buf[low - 1] == buf[high + 1]) {
low --;
high ++;
}
// 判断是否更新最大回文串信息
if (high - low + 1 > maxLen) {
maxLen = high - low + 1;
start = low;
end = high;
}
}
return s.substr(start, maxLen);
}
通往【LeetCode - 盛最多水的容器】的任意门
解法:贪心算法 - 双指针
可以将容积抽象为面积大小,因此决定容积大小的因素有两个:第一,两根柱子之间的距离;第二,构成容器的两根柱子的高度的较小值。
因为容积大小由两个因素共同决定,我们无法直接使用贪心算法。如果要使用贪心算法,那么我们必须让遍历过程中有其中一个因素是从最优逐渐衰减的。首先想到的是排序,但由于排序会打乱柱子之间的顺序,从而影响到因素一。那么,我们应该让遍历过程中柱子之间的距离是从大到小的,因此我们初始化两个指针,分别指向数组头部和尾部。
最后需要解决的是,在遍历过程中如何贪心柱子高度才能使结果最优。由于柱子之间的距离是逐渐减小的,头尾指针中的较小者不可能再次成为答案,所以我们可以让高度较小的指针移动,直至头尾指针相遇,则得出最优解。
时间复杂度:O(n)
空间复杂度:O(1)
int maxArea(vector<int>& height) {
int max = 0, l = 0, r = height.size() - 1;
int maxHeight = 0;
while (l < r) {
// 计算当前的容积,并判断是否需要更新容积最大值
int min = height[l] > height[r] ? height[r] : height[l];
int area = min * (r - l);
if (area > max) {
max = area;
maxHeight = min;
}
// 高度较小者移动
if (height[l] < height[r]) {
l ++;
} else {
r --;
}
}
return max;
}
通往【LeetCode - 删除链表的倒数第 N 个结点】的任意门
解法:双指针
在单链表中,假定要删除倒数第2个结点,我们需要其前驱结点,即倒数第3个结点。
由于删除的结点有可能是头结点,为方便统一处理,我们设置虚拟头结点(-1)
假定链表为[1, 2, 3, 4, 5],需要删除倒数第2个结点
- 第一步:最初p,q指针都指向虚拟头结点
p↓ ↓q
(-1) -> 1 -> 2 -> 3 -> 4 -> NULL- 第二步:先让指针q向后移动 2 + 1 次
p↓ ↓q
(-1) -> 1 -> 2 -> 3 -> 4 -> NULL- 第三步:指针p,q同步向后移动,当q = NULL时则结束循环
p↓ ↓q
(-1) -> 1 -> 2 -> 3 -> 4 -> NULL- 第四步:删除倒数第2个结点,返回 虚拟头结点->next
p->next = p->next->next;
return 虚拟头结点->next;
时间复杂度:O(n)
空间复杂度:O(1)
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 创建虚拟头结点,并将p,q指针都指向虚拟头结点
ListNode* p = new ListNode(-1, head);
ListNode* h = p;
ListNode* q = p;
while (n > -1) {
q = q->next;
n --;
}
while (q != NULL) {
q = q->next;
p = p->next;
}
// 删除并释放倒数第n个结点
ListNode* temp = p->next;
p->next = p->next->next;
delete temp;
temp = NULL;
// 删除并释放虚拟头结点
temp = h->next;
delete h;
h = NULL;
return temp;
}