两数之和
- 暴力,两层循环直接比较
- 双指针,一个指最左一个指最右,用右边的向左迭代,但实际上和暴力没什么差别
- 建哈希表
// 暴力
vector<int> twoSum(vector<int>& nums, int target) {
int left=0;
int len = nums.size();
vector<int> res;
for(; left<len-1; left++){
for(int right = left+1; right<len; ++right){
if(nums[left]+nums[right]==target) {
res = {left, right};
return res;
}
}
}
return res;
}
// 双指针
vector<int> twoSum(vector<int>& nums, int target) {
int left=0;
int len = nums.size();
int right = len-1;
for(; left<len; ++left){
while(left<right){
if(nums[left]+nums[right] == target) return {left, right};
right--;
}
right = len-1;
}
return {};
}
// 建hash
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
for(int idx=0; idx<nums.size(); ++idx){
auto tem = hash.find(target-nums[idx]);
if(tem != hash.end()) return {idx, tem->second};
hash[nums[idx]] = idx;
}
return {};
}
从提交的结果反馈上, 倒是可以看出双层循环的比较省空间,哈希的则比较省时间
两数相加
这道题一开始想着用一个储存栈记录两个链表的值,弹栈计算出相加的和(sum),再循环sum/10,得到返回链表。写完后发现这样实在是太麻烦,可以用一个数记录每一个位置对应的进位数,直接一次遍历l1、l2,同时记录return就行了
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head = nullptr, *tail = nullptr;
int tem = 0;
while(l1 || l2){
ListNode* node = nullptr;
int n1 = l1 ? l1->val : 0;
int n2 = l2 ? l2->val : 0;
tem += n1+n2;
if(tem > 9) node = new ListNode(tem%10);
else node = new ListNode(tem);
tem /= 10;
if(!head){
head = node;
tail = head;
}else {
tail->next = node;
tail = tail->next;
}
if(l1) l1 = l1->next;
if(l2) l2 = l2->next;
}
while(tem){
tail->next = new ListNode(tem%10);
tem /= 10;
tail = tail->next;
}
return head;
}
关于链表节点的访问
首先需要研究节点的定义,一般都是:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
要区分的是节点和节点指针,比如ListNode node就是一个节点,要是想通过node访问下一个节点,可以直接node.next,但是对于一个链表,比如ListNode* L,它相当于一个游标,在链表上不停地根据要求变换位置。那么它在新建节点并连接的时候可以写成:
// method
*L = node;
L = L->next;
要是把node作为一个指针,那么上面两种方法就不需要加上引用or解引用
如果报错是有关内存访问失败的,优先考虑边界条件,然后就是看创建节点的方式是否出错
我一开始用的是创建方式是直接ListNode node; 但是这样其实没有给它分配内存,如果是一个类可以用这种方式创建一个新对象。对于创建结构体的对象,还是用new来吧:比如
ListNode *head = new ListNode(); // 这个小括号中间能不能有值取决于构造函数
无重复字符的最长子串
- 双指针,一个遍历所有元素,另一个遍历已经走过的元素,比较两者是否出现相同的元素
- 滑动窗口,可以用一个字符串来记录走过的元素(其实这俩基本都是一个思路)
思路看起来还是挺简单的,但在代码实现的时候有很多细节需要注意(我调了好久):
- 不含有重复字符的意思是,“abcb”这样的也不行,每个字符只能出现一次
- 对于“dvdf”这样的,在遍历到第二个d时,要从v开始记录
int lengthOfLongestSubstring(std::string s) {
if(s=="") return 0;
int idx=0, tag=0, i=0;
int cnt=0, flag=0;
while(i<s.size()){
for(idx=tag; idx<i; ++idx){
if(s[i]==s[idx]) {
tag = ++idx;
cnt = i-tag; // 这里cnt直接计算,最好不要试图用类似于cnt++的手段
break;
}
}
cnt++;
i++;
if(cnt>flag) flag = cnt;
}
return flag;
}
对于滑动窗口,参考中使用的是unordered_set<char>