1. Two Sum
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
1、 暴力破解
遍历所有组合可能性,其中数字使用了两次,应该说不满足题目要求的,但能通过所有测试。。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
int i, j;
int length = nums.size();
for (i = 0; i < length; i++) {
for (j = i + 1; j < length; j++) {
if (nums[i] + nums[j] == target) {
ret.push_back(i);
ret.push_back(j);
break;
}
}
}
return ret;
}
};
2、滑动窗口结合unordered_map
键值对为元素值与对应下标,采用两遍哈希表或一遍哈希表。两遍哈希表属于先建立后查找,一遍哈希表属于边建立边查找。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
if(nums.size() <2)
return ret;
unordered_map<int, int> map; // c++11中unordered_map更合适,hash_map已放弃
for(size_t pos = 0; pos < nums.size(); pos++){ // 这里采用先初始化添加元素
map[nums[pos]] = pos;
}
for(size_t pos = 0; pos < nums.size(); pos++){ // 然后查找元素
int findkey = target - nums[pos];
auto itr = map.find(findkey);
if(itr != map.end() && pos != itr->second){
ret.push_back(pos);
ret.push_back(itr->second);
break;
}
}
return ret;
// for(size_t i = 0; i<nums.size(); i++){ // 这里属于一边查找一边添加元素
// auto itr = map.find(target - nums[i]);
// if(itr == map.end() ){
// map.insert(pair<int, int>(nums[i], i) );
// //map[nums[i]] = i;
// }
// else{
// ret.push_back(itr->second);
// ret.push_back(i);
// break;
// }
// }
// return ret;
}
};
2. Add Two Numbers
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example:
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 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) {
if(l1 == nullptr) // 任意一个列表为空的情况
return l2;
if(l2 == nullptr)
return l1;
ListNode *ret, *head;
head = ret = new ListNode(0);
int addFlag = 0; // 初始不产生进位
while(l1 != nullptr || l2 != nullptr){
int sum = 0;
if(l1 != nullptr){ // 列表长度相等或其中一个列表较长
sum += l1->val;
l1 = l1->next;
}
if(l2 != nullptr){
sum += l2->val;
l2 = l2->next;
}
sum += addFlag;
ret->next = new ListNode(sum%10);
addFlag = sum/10; // 设置新的进位
ret = ret->next;
}
if(addFlag == 1) // 两个列表长度相等且最高位产生进位,需要追加一个进位结点
ret->next = new ListNode(1);
return head->next;
}
};
3. Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters.
Example 1:
Input: “abcabcbb”
Output: 3
Explanation: The answer is “abc”, with the length of 3.
Example 2:
Input: “bbbbb”
Output: 1
Explanation: The answer is “b”, with the length of 1.
Example 3:
Input: “pwwkew”
Output: 3
Explanation: The answer is “wke”, with the length of 3.
Note that the answer must be a substring, “pwke” is a subsequence and not a substring.
1、自己原始的暴力破解,与方法2的暴力破解相比,把判断allUnique函数直接写到lengthOfLongestSubstring函数中,反而能勉强通过所有测试,但是时间消耗增加了近40倍,内存近20倍。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size() < 2)
return s.size();
int maxlen = 0;
for (size_t i = 0; i < s.length()-1; i++) {
unordered_set<char> set;
for (size_t j = i; j < s.length(); j++) {
if(set.find(s[j]) != set.end() )
break;
set.insert(s[j]);
maxlen = set.size() > maxlen ? set.size() : maxlen;
}
}
return maxlen;
}
};
2、滑动窗口,采用了set作窗口(数组也应该可以),右边添加字符,左边删除字符直到set里面没有重复字符,那么最大无重复字符串长度,即为添加字符过程动态窗口的最大宽度。这里涉及到最恶化情况,l与r分别需要扫过一遍字符串。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ans = 0;
int len = s.length();
if(len < 2)
return len;
unordered_set<char> set;
int l = 0, r = 0; // l 为滑动窗口左, r为右;
while(l < len && r < len){
if(set.find(s[r]) == set.end() ){
set.insert(s[r++]);
int tmp = set.size();
ans = ans > tmp ? ans : tmp;
}
else
set.erase(s[l++]);
}
return ans;
// for(int i = 0; i <= s.length()-1; i++){ // 暴力破解失败,解释是题目更新后通不过
// for(int j = i+1; j <= s.length(); j++){
// if(!allUnique(s, i, j)) // 若为重复子字符串就结束当前循环
// break;
// ans = ans > (j-i) ? ans : (j-i);
// }
// }
// return ans;
}
// 采用两种数据结构的判断函数,分别为set和map,实际map的键值对中的值没发挥作用
bool allUnique(string s, int start, int end){
unordered_set<char> set;
for(size_t i = start; i < end; i++){
if(set.find(s[i]) != set.end() )
return false;
set.insert(s[i]);
}
return true;
}
// bool allUnique(string s, int start, int end){
// unordered_map<char, int> map; // 这里map相对于set意义不大
// for(size_t i = start; i < end; i++){
// auto itr = map.find(s[i]);
// if(itr != map.end() && itr->second != i)
// return false;
// map[s[i]] = i;
// }
// return true;
// }
};
3、优化的滑动窗口,原始窗口是右边是一次次添加,左边是一次次删除,直到达到要求的不重复字符位置,改进的思想是左边一次就移动到指定位置,那么采用map键值对来存储字符和字符下标。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ans = 0;
int len = s.length();
if(len < 2)
return len;
unordered_map<char, int> map;
for(int i = 0, j = 0; j < len; j++){
auto itr = map.find(s[j]);
if(itr != map.end() ){
int newI = itr->second;
i = newI > i ? newI:i; // 这里的目的是防止i向左移动,因为哈希表中左边的重复字符可能出现多次。
}
ans = ans > (j-i+1)?ans:(j-i+1);
map[s[j]] = j+1;
}
return ans;
};
附:
1、参考C++ STL 之 unordered_set 使用(包括unordersd_map)链接地址:https://blog.csdn.net/qq_32172673/article/details/85160180
为了以后复习巩固,如有错误请见谅。
贵有恒,何必三更起五更睡;最无益,只怕一日曝十日寒。