41. First Missing Positive
题目描述: 给定一堆无序的整数,有正有负,找出第一个缺失的正整数,例如:
Example :
Input: [1,2,0]
Output: 3
Example 2:
Input: [3,4,-1,1]
Output: 2
Example 3:
Input: [7,8,9,11,12]
Output: 1
解题思路: 如果给的序列是连续的那必然是最小的或者最大的一个,如果不连续的话,就从1开始找咯,找到那个数没有出现过就行了。时间消耗主要是在确定某个数是不是在序列中,如果用数组妥妥的超时,那么就用哈希表来查找,这样消耗的时间就很少了。
AC代码:
# python
class Solution(object):
def firstMissingPositive(self, nums):
if nums == []: # 列表空直接返回 0
return 1
dic = {} # 建字典,内部使用的hash算法来存储键的所以可以直接拿来当做哈希表
for i in nums:
if i > 0: # 把每个正整数值映射到hash表中去
dic[i] = 1
i = 1
while i < max(nums)+2:
try: # 从以开始查询键是否存在,不存在的就是答案,这时候python会抛出一个异常,把他捕获下来再返回结果就行了
a = dic[i]
except:
return i
i += 1
return 1
53. Maximum Subarray
题目描述: 找一个数组中,区间和最大的区间。
解题思路: 题目看似简单其实还是要想一下的,用pack来保存当前状态,从第一个数开始往后遍历,如果pack已经是负的了,那么把pack里面的数全部丢掉,只保留这个新的值。否则见到一个数就往pack里面装,这样就有可能使pack的结果变下从而丢失正解,所以再用一个ans来记录pack所经历过的最大值,便是正解。
AC代码:
// c++
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size() == 0) return 0;
if(nums.size() == 1) return nums[0];
long long pack = nums[0];
long long ans = pack; // 先放第一个值进去先
for(int i = 1 ; i < nums.size() ; i ++)
{
if(nums[i] + pack < nums[i]) // 更新pack
pack = nums[i];
else
pack += nums[i];
ans = max(ans,pack); // 记录最大值
}
return ans;
}
};
54. Spiral Matrix
题目描述: 旋转矩阵? 给一个矩阵,然后螺旋走位,把值输出出来。
Example 1:
Input:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
Output: [1,2,3,6,9,8,7,4,5]
Example 2:
Input:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]
解题思路: 总共就四个方向,上下左右,而且每次都是规律变化的也就是 -右-下-左-上-右-下… 先弄一个可以移动的点,然后根据当前的方向,改变坐标值,移动到下一个位置,在这期间判断一下是否会走出矩阵,或者走到之前走到过的地方,如果有,那么改变方向,如果三个方向都没办法走了,也就是走到最中心了,算法结束。
AC代码:
// c++
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> res;
int m = matrix.size();
if (m == 0 ) return res; // 空矩阵直接返回
int n = matrix[0].size();
int i = 0,j = 0,cnt = n*m,to = 0; // 变量to记录方向
bool vis[1000][1000]; // 标记每个点是否已经走过了
memset(vis,false,sizeof(vis)); // 初始化vis
res.push_back(matrix[0][0]); // 起始点先走过去
vis[0][0] = true;
int four_to[4][2] = {0,1,1,0,0,-1,-1,0}; // 四个方向的坐标计算方法
while(cnt--)
{
i += four_to[to][0];
j += four_to[to][1]; // 计算坐标往前走
cout<<i<<" "<<j<<endl;
if(i < 0 || j < 0 || j >= n || i >= m || vis[i][j]) // 判断是否需要改变方向
{
i -= four_to[to][0]; // 需要改变方向,先退回去
j -= four_to[to][1];
to = (to+1)%4; // 改变方向
i += four_to[to][0]; // 重新出发
j += four_to[to][1];
if(i < 0 || j < 0 || j >= n || i >= m || vis[i][j]) return res;
// 如果仍然要改变方向,则说明已经走到头了
}
res.push_back(matrix[i][j]);
vis[i][j] = true;
}
return res;
}
};
55. Jump Game
题目描述: 给一个数组,记录从每个位置出发往后可以向后跳跃的最远距离,问,一个给定的数组,能不能从起始点跳到最后一个点。
解题思路: 刚开始想的是dfs直接从起始点往后跳就是,能跳到最后一点就结束算法,返回结果。但是如果给的是可以跳过去的话,算法会比较快结束,如果没法跳过去,那么算法的时间就是巨大的了,已经不是几次方的级别了。看了别人的题解后,恍然大悟。就拿一个值n,记录当前可以到达的最远距离,那么每到一个位置,n要么不变,要么就是array[i]+i,那么当i>n的时候就说明i的位置是无法到达的所以就可以结束算法了。否则等循环结束,说明最后一点就是可到达的。
AC代码:
# python
class Solution(object):
def canJump(self, nums):
ans = False
m = 0 # 记录最远可到达距离
for i,n in enumerate(nums):
if i > m :
return False
m = max(m,i+n) # 更新 m 的最大值
return True
56. Merge Intervals
题目描述: 给一堆无序的区间,有一些区间是有交叉的,有交叉的就把他们合并。
Example 1:
Input: [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
Example 2:
Input: [[1,4],[4,5]]
Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.
解题思路: 既然是无序的,那就先排个序,按照每个区间的起始时间由小到大排好序,然后比较每个区间的结束时间和下一个区间的开始时间,如果结束时间大于开始时间,说明是有交叉的,那么合并这两个区间,开始时间就是前一个区间的开始时间,结束时间取两个区间结束时间的最大值。
AC代码:
// c++
class Solution {
public:
static bool compare(vector<int> a,vector<int> b){
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
int len = intervals.size();
vector<vector<int>> res;
vector<int> tmp;
int beg,en;
sort(intervals.begin(),intervals.end(),compare); // 给区间排序
for(int i = 0 ; i < len ;i ++)
{
beg = intervals[i][0];
en = intervals[i][1];
while(i+1<len && intervals[i+1][0] <= en){ // 判断是否有交叉,有交叉就合并
en = max(intervals[i+1][1],en);
i ++;
}
tmp.push_back(beg);
tmp.push_back(en);
res.push_back(tmp);
tmp.clear();
}
return res;
}
};
57. Insert Interval
题目描述: 和上一题差不多,一堆区间,不过这次是按起始时间排好序的区间,另外再加一个新的区间,新区间可能与旧区间有交叉。
解题思路: 解题核心逻辑也和上题差不过,本来我直接复制上一题的代码,然后先把新区间push进去,然后直接用上次的代码,就过了,然后发现时间挺慢的,既然是排好序的,那么其实就可以省掉排序的时间了,这也是主要的时间消耗,只要找到新区间对应的插入位置把他插入,然后再合并区间就ok了
AC代码:
// c++
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
int flag = 0;
for(vector<vector<int>> ::iterator pos = intervals.begin(); pos !=intervals.end() ; pos ++ )
{ // 找插入位置,因为是排好序的所以挺方便
if((*pos)[0] > newInterval[0])
{
intervals.insert(pos,newInterval);
flag = 1;
break;
}
}
if(!flag) intervals.push_back(newInterval);
int len = intervals.size();
vector<vector<int>> res;
vector<int> tmp;
int beg,en,i;
for( i = 0; i < len ;i ++)
{
beg = intervals[i][0];
en = intervals[i][1];
while(i+1 < len && intervals[i+1][0] <= en){ // 判断是否有交叉,有交叉就合并
en = max(intervals[i+1][1],en);
i ++;
}
tmp.push_back(beg);
tmp.push_back(en);
res.push_back(tmp);
tmp.clear();
}
return res;
}
};
58. Length of Last Word
题目描述: 一个字符串最后一个单词的长度
解题思路: 水题
AC代码:
# python
class Solution(object):
def lengthOfLastWord(self, s):
try:
s = s.split()[-1]
return len(s)
except:
return 0
59. Spiral Matrix II
题目描述: 再一次旋转矩阵,不过这次不太一样,上次是螺旋走位,然后输出,这次是螺旋走位,然后按顺序填值进去,给定一个n,填满一个n*n的矩阵。
Example:
Input: 3
Output:
[
[ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ]
]
解题思路: 和之前那题差不多的思路,四个方向,判断什么时候改变方向,由于矩阵也是自己建的,所以还省掉了建vis数组的麻烦,先开一个n*n的矩阵全部填成0,那么不是0的地方就是走过的了。
AC代码:
// c++
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res;
for (int i = 0 ; i < n ; i ++ ) // 初始化n*n的矩阵全部填成0
{
vector<int> tmp;
res.push_back(tmp);
for (int j = 0 ; j < n ; j ++ )
res[i].push_back(0);
}
int i = 0,j = -1,cnt = 0,to = 0;
int four_to[4][2] = {0,1,1,0,0,-1,-1,0};
while(++cnt <= n*n)
{
i += four_to[to][0];
j += four_to[to][1];
if(i < 0 || j < 0 || j >= n || i >= n || res[i][j] != 0) // 改变方向
{
i -= four_to[to][0];
j -= four_to[to][1];
to = (to+1)%4;
i += four_to[to][0];
j += four_to[to][1];
if(i < 0 || j < 0 || j >= n || i >= n || res[i][j] != 0)
return res;
}
res[i][j] = cnt; // 填值
}
return res;
}
};
60. Permutation Sequence
题目描述: 又是全排列,这次要求给一组数,输出所有排列
解题思路: 上次是用递归写的,这次懒得写了,直接调库函数
AC代码:
// c++
class Solution {
public:
string getPermutation(int n, int k) {
vector <int> res;
for(int i = 0; i < n ; i++)
res.push_back(i+1);
for(int i = 0; i < k-1; i++)
next_permutation(res.begin(),res.end());
string s;
for(int i = 0; i < n ; i++)
s += res[i]+'0';
return s;
}
};
61. Rotate List
题目描述: 给一个链表和一个k,把从倒数第k个位置开始的表接到头上去。
Example 1:
Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:
Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
解题思路: 思路也比较简单,弄两个指针,一个先跑k步,然后两个一起跑,等跑得快的那个到达链表末尾了,跑得慢的那个也就到了倒数第k个位置了,然后把这一段整个挪到最前面去。不过这题还是好多坑,要很小心的计算这两个坐标,还有就是k可以走到尾巴,然后再从头开始走,那么k就给的很大,这时候当k第一次到达队尾的时候,长度也就计算出来了,然后k对长度取个模,这样可以省下很多时间。
AC代码:
// c++
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
ListNode*slow = head,*fast = head,*pre;
if(head == NULL)
return NULL;
if(head->next == NULL)
return head;
for(int i = 0 ; i < k ; i ++)
{
fast = fast->next;
if(fast == NULL)
{
fast = head;
k = (k-i-1)%(i+1);
cout<<k<<endl;
i = -1;
}
}
while(fast->next)
{
fast = fast->next;
slow = slow->next;
}
fast->next=head;
head = slow->next;
slow->next=NULL;
return head;
}
};
62. Unique Paths
题目描述: 在一块n*m的板子上,一个机器人想从左上角走到右下角,机器人只能往右或者往下走,每次走一格,问走到右下角有多少种方法。
解题思路: 典型的dp问题,第一行只能有一种方法,第一列也只能有一种方法,那么其他地方,要么从上面走下来,要么从左边坐过来,所以就只需要这两个值加起来。那么答案就出来了。
AC代码:
// c++
class Solution {
public:
int uniquePaths(int m, int n) {
long long ans[101][101] = {0};
for(int i = 0 ; i < n ; i ++) // 初始化第一行
ans[0][i] = 1;
for(int i = 0 ; i < m ; i ++) // 初始化第一列
ans[i][0] = 1;
for(int i = 1 ; i < m ; i ++)
for(int j = 1 ; j < n ; j ++)
ans[i][j] = ans[i-1][j] + ans[i][j-1];
return ans[m-1][n-1];
}
};
63. Unique Paths II
题目描述: 上一题的进阶版,还是从左上角走到右下角,但是中间有障碍物,用1来表示,如下:
Example 1:
Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
解题思路: 还是dp的思想,有障碍物的地方让他们等于0就行了,然后在初始化第一行和第一列的时候,注意路上有没有障碍物,如果之前的路径有障碍物那么初始化为0。接着就从(1,1)点开始遍历,左边加上边的值,走完最后答案就求出来了。
AC代码:
# python
class Solution(object):
def uniquePathsWithObstacles(self, obstacleGrid):
new_grid = [[0 for x in i] for i in obstacleGrid] 初始化 背包,全填0
if(obstacleGrid[0][0] == 1): # 如果第一个就是障碍物直接返回0
return 0
new_grid[0][0] = 1
for i in range(1,len(new_grid[0])):# 初始化第一行
if 1 not in obstacleGrid[0][:i+1] : # 判断路径中是否有障碍物
new_grid[0][i] = 1
for i in range(1,len(new_grid)): # 初始化第一列
if 1 not in [obstacleGrid[j][0] for j in range(i+1)]: # 判断路径中是否有障碍物
new_grid[i][0] = 1
for i in range(1,len(new_grid)): # 遍历一遍
for j in range(1,len(new_grid[0])):
if (obstacleGrid[i][j] != 1):
new_grid[i][j] = new_grid[i-1][j] + new_grid[i][j-1]
return new_grid[len(new_grid)-1][len(new_grid[0])-1]
64. Minimum Path Sum
题目描述: 给一个矩阵,走过的路径就把值累加起来,还是从左上走到右下,问路径的值加起来最小是多少。
解题思路: 还是一个dp的问题啊,只要把前两题的方法数改成记录路径和就行了。
AC代码:
// c++
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
for (int i = 1 ; i < grid[0].size() ; i ++) // 初始化第一行
grid[0][i] = grid[0][i]+grid[0][i-1];
for (int i = 1 ; i < grid.size() ; i ++) // 初始化第一列
grid[i][0] = grid[i][0]+grid[i-1][0];
for (int i = 1 ; i < grid.size() ; i ++) // dp
for (int j = 1 ; j < grid[0].size() ; j ++)
grid[i][j] += min(grid[i-1][j],grid[i][j-1]);
return grid[grid.size()-1][grid[0].size()-1];
}
};