前言
前一段时间在刷leetcode的习题,刷了100道题,但是并没有很深刻的感觉(可能是因为自己陷入了为了刷题而刷题的状态中,忘了思考了)。按照自己一贯做事的方案,如果没有很深刻的感觉,那就适时的进行总结。回顾曾走过的路,让自己走的更踏实些。
我准备对所刷题目的解答进行汇总。对每一个题目我首先提供我自己的一些解法思路,也有有可能加入一些别人精彩的思路。考虑到工程量的浩大,可能并不会事事巨细。这个仅仅是我自己的浅薄的总结,不到之处,还请大家批评指正。
为了阅读方便,我将按照题目编号顺序写出解答。注意:这些解答在leetcode上通过,但并不表明它们是没有bug的,如果大家发现了bug,欢迎大家指正。
对于每一个题目,我将按照如下的模式进行阐述。
- 题目编号. 标题
- 题目内容
- 题意: 中文简单分析
- 思路: 1. 2. 3. …
- 代码: 简单想法+代码
- 注意: 额外的注意事项
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.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
题意:给定一个int型数组以及一个target,得到两个元素的下标使得这两个元素的和等于target。
思路:
- n个元素,两两一组,一共有n*(n-1)/2组,可在O(n^2)的时间复杂度上得出结果。
- 更优一点的方案,首先进行排序,然后对n个元素,按顺序选择。如果选择的某一个元素array[i],且i为结果,那么target-array[i]也必在数组中。因此将问题转换为查找target-array[i]的问题,由于已排序,那么可采用二分查找。总的时间复杂度为排序时间+逐个尝试并查找时间 = O(nlgn) + O(nlgn) = O(nlgn).
- 上面其实给出了另外的hash的思路。首先将数组中的所有元素存入hash表中,然后选择数组的一个元素array[i],判断target-array[i]是否也在数组中,这相当于判断是否在hash表中。总的时间复杂度为O(n) + O(n) = O(n).
- 我并没有想到其他更优的方案,因为根据直觉来看,至少需要O(n)的时间复杂度,因为任何一个元素都有可能成为结果,我们至少对每个元素要查看一次。
代码:
只给出hash思想的代码。首先对所有的元素构建hash,然后查找target-array[i]在hash表中是否存在。同时为了返回结果中的索引,在存入hash的时候同时存入元素的索引。
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> umi;
for(int i = 0; i < nums.size(); ++i){
umi[nums[i]] = i;
}
for(int i = 0; i < nums.size(); ++i){
auto it = umi.find(target-nums[i]);
if(it != umi.end() && it->second != i){
return {i, it->second};
}
}
return {};
}
注意:
上面的代码有一个小的技巧。为什么要判断it->second != i? 考虑array = [2 2 3], target=4: 这样hash表中只有一个元素,当find(target-nums[i])的时候,肯定会找到2,但是这个2可能是nums[i]对应的索引,而不是target-nums[i]对应的索引。
2. Add Two Numbers
You are given two linked lists representing two non-negative numbers. 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.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
题意: 两个非负数相加求和。与普通的求和不一样的是,这两个数是由链表存储的。例如上面的342+465=807,并将结果也存入链表中返回。
思路:
1. 遍历链表,分别得到两个int变量,然后相加,并将结果转换为链表
2. 两个链表同时遍历,遍历的过程就是累加的过程。由于链表本身存储就是先低位再高位的形式,因此可以直接相加求和即可,注意进位。
代码: 两个链表同时遍历,相加,进位,同时注意链表长度可能不一样,先到尾部的链表相当于后面补0.也就是说342+65 = 342 + 065 = 407
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode tmp(0);
ListNode *p = &tmp;
int high = 0;
while(l1 || l2){
int cur = ((l1) ? l1->val : 0) + ((l2) ? l2->val:0) + high;
if(cur >= 10){
high = 1;
cur -= 10;
}else{
high = 0;
}
ListNode* q = new ListNode(cur);
p->next = q;
p = q;
if(l1)
l1 = l1->next;
if(l2)
l2 = l2->next;
}
if(high){
p->next = new ListNode(high);
}
return tmp.next;
}
注意: 上面代码有两个讨巧之处。 第一个是tmp节点的使用,这个是额外的链表头节点,利用这个节点可以减少head==NULL这样的判断; 第二个是(l1) ? l1->val : 0这样的用法,如果节点有效,则使用其val,否则使用0,这样就可以不同长度的两个链表的操作统一化了。当然也可以不这样做,当其中某一个链表为空的时候,就停止while循环,然后将不空的链表直接接到已经求得的链表尾部即可,特别类似于两个有序链表的合并问题。
6. ZigZag Conversion
The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: “PAHNAPLSIIGYIR”
Write the code that will take a string and make this conversion given a number of rows:
string convert(string text, int nRows);
convert(“PAYPALISHIRING”, 3) should return “PAHNAPLSIIGYIR”.
题意: 将一个字符串竖着排成一个之字形,然后按照行序输出。
思路:
1. 此题目很简单,肯定是O(n)时间复杂度,将列序转为行序的即可。关键是分析之字形,发现其行数比斜线的个数多2个。
2.
代码: 代码很简单,就是简单定义numRows个字符串,然后遍历输入字符串,逐个塞入到对应的行字符串中,首先处理一列numRows个字符,再处理斜线的numRows-2个字符。
string convert(string s, int numRows) {
int i = 0;
vector<string> vs(numRows);
while(i < s.size()){
for(int j = 0; j < numRows && i <s.size(); ++j){
vs[j].push_back(s[i++]);
}
for(int j = numRows-2; j >=1 && i < s.size(); --j){
vs[j].push_back(s[i++]);
}
}
string res;
for(auto i : vs){
//printf("%s\n", i.c_str());
res += i;
}
return res;
}
注意: 没啥可注意的,代码简单清晰。
7. Reverse Integer
Reverse digits of an integer.
Example1: x = 123, return 321
Example2: x = -123, return -321
题意: int变量的反转
思路:
1. 先转为字符串,再字符串反转
2. 逐个得出数,并进行乘10加即可。
代码:简单易懂,res= res*10 + c这个很常用,注意记忆。
int reverse(int x) {
if(x == 0)
return 0;
int minus = 1;
if(x < 0){
minus = -1;
x = -x;
}
long xx = x, res = 0;
char c;
while(xx){
c = xx % 10;
res = res * 10 + c;
//printf("%d ", c);
xx /= 10;
}
res *= minus;
if(res > std::numeric_limits<int>::max() || res < std::numeric_limits<int>::min() )
return 0;
return res;
}
注意: int反转可能导致导致越界,此处我将x转换为long型的xx,再进行的处理,其实这个地方的做法有些多余,只需要将res定义为long类型,不需要xx就行。