笔者按照目录刷题,对于每一道题,力争使用效率最高(时间复杂度最低)的算法,并所有经过C++代码实现AC。(文中计算的复杂度都是最坏状况复杂度)
由于考虑到大部分读者已经在Leetcode浏览过题目了,因此每道题都按照 解题思路 -> 实现代码 -> 问题描述 的顺序进行讲解。
(笔者目前已刷 40 题,已更新解法 10 题,最近一段时间会频繁更新)能够点击下方连接,直达gitbook:
https://codernie.gitbooks.io/leetcode-solutions/content/
也欢迎你们关注个人微信公众号(大雄的学习人生),有问题能够随时后台回复我,多多探讨。node
嵌套两层循环:第一层:i 从 0 到 n - 2;第二层:j 从 i + 1 到 n - 1;判断 nums[i] + nums[j] == target ,若是成立则是正确答案git
从 0 到 n - 1 依次遍历,利用map存放每个数值的下标,在map中寻找是否有使(nums[i] + x == target)成立的x的存在,若是存在则返回i和它的下标(即myMap[ target - nums[i] ])。算法
复杂度分析:由于只遍历了一次数组,map每次的查询的时间复杂度为O(logN)因此总体复杂度为O(N*logN)、若是这里使用hash_map能够将查询复杂度下降到O(1),从而使得总体复杂度为O(N),可是hash_map不是标准的C++库,因此这里没有使用。express
// 1. Two Sum vector<int> twoSum(vector<int>& nums, int target) { map<int, int> myMap; vector<int> result; for (int i = 0; i < nums.size(); i++) { if (myMap.find(target - nums[i]) == myMap.end()) { myMap[nums[i]] = i; } else { result = {myMap[target - nums[i]], i}; break; } } return result; }
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:ide
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
从左到右遍历链表,依次相加,每个位置生成一个新的结点便可。学习
时间复杂度:O( max( len(l1), len(l2) ) )this
考虑边界条件:spa
1.进位的的处理:carry表示进位,当最后一位还有进位时,即便 l1 和 l2 均为NULL的状况下,还须要生成一个新的结点,因此while的条件中加入了 carry != 0 判断项。
2.返回头结点:当头结点为NULL的时候记录头结点,而且让p等于头结点;后续状况让 p->next 等于新的结点,并让 p 指向 p->next。
// 2. Add Two Numbers ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode *head = NULL, *p; int carry = 0, sum; while (l1 != NULL || l2 != NULL || carry != 0) { sum = 0; if (l1 != NULL) { sum += l1->val; l1 = l1->next; } if (l2 != NULL) { sum += l2->val; l2 = l2->next; } sum += carry; carry = sum / 10; sum %= 10; ListNode *newNode = new ListNode(sum); if (head == NULL) { head = newNode; p = newNode; } else { p->next = newNode; p = p->next; } } return head; }
You are given two non-empty linked lists representing two non-negative integers. The digits are stored inreverse orderand 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.
从每一个点遍历、依次遍历这个点后的每个点、经过遍历该点以前的点判断该点是否出现过。听上去有点拗口,代码在下方,这个暴力方法Leetcode也能够AC,可是不推荐使用。
头标记指向当前最长无重复字符串的头部,尾标记指向其尾部。经过一个 map 来记录出现过的字符最后出现的位置。
依次遍历数组,若是当前字符已出现过,则让头标记指向其最后出现过的位置的后一个位置。而后每次经过头、尾标记计算当前无重复字符串的长度,并与已知最大值做比较。这里查询map的复杂度为 O(logN),遍历的复杂度为 O(N),所以总体复杂度为 O(N*logN)。若是这里使用hash_map能够将查询复杂度下降到O(1),从而使得总体复杂度为O(N),可是hash_map不是标准的C++库,因此这里没有使用。
// 3. Longest Substring Without Repeating Characters // 暴力解法 int lengthOfLongestSubstring_bruteForce(string s) { int res = 0, sum; for (int i = s.size() - 1; i >= 0; i--) { sum = 1; for (int j = i - 1; j >= 0; j--) { bool flag = true; for (int k = i; k > j; k--) { if (s[j] == s[k]) { flag = false; break; } } if (flag) { sum++; } else { break; } } res = max(res, sum); } return res; } // 头尾标记法 int lengthOfLongestSubstring(string s) { map<char, int> myMap; int res = 0; for (int i = 0, j = 0; j < s.size(); j++){ if (myMap.find(s[j]) != myMap.end()) { i = max(i, myMap[s[j]] + 1); } myMap[s[j]] = j; res = max(res, j - i + 1); } return res; }
Given a string, find the length of thelongest substringwithout repeating characters.
Examples:
Given"abcabcbb"
, the answer is"abc"
, which the length is 3.
Given"bbbbb"
, the answer is"b"
, with the length of 1.
Given"pwwkew"
, the answer is"wke"
, with the length of 3. Note that the answer must be asubstring,"pwke"
is asubsequenceand not a substring.
这道题咋一看像二分查找,可是仔细看题,发现有两个有序数组,并且不是让咱们找一个特定的数,而是要找两个数组合并后的中位数,这样一看就比较难了,也难怪归类为hard类别。这道题除了一下介绍的二分查找法,还有两个数组分别进行二分查找的方法,不过代码量相对更多(也多是由于笔者水平不够致使代码量过大),并且看了下面的二分查找法后,感叹于该算法做者的脑洞,因此在这里只介绍该种方法。
将两个数组合并,而后进行快排,中间的数即中位数。因为题目说了复杂度不能超过O(log(m + n)),因此这个方法固然回Time Limit Excess,因此咱们得探究一种更高效的解法。
首先分别把两个数组分红两边,大概为下面这种形式:(A表示nums1, B表示nums2)
left_part | right_part A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
由于有序数列的性质,咱们知道只要咱们知足如下两个条件,咱们就能够找到中位数了:
条件一:len(A_left) + len(B_left) = len(A_right) + len(B_right)
条件二:max[A_left, B_left] <= min[A_right, B_right]
为了使问题简单化,咱们先只考虑 m + n 为偶数的状况下,只要知足上述两个条件,咱们的中位数就等于左边的最大值加上右边的最小值除以二。
为了知足条件一,咱们只要令** i + j == m + n - i - j (+ 1)**便可(这里加一是为了以后考虑 m + n 为奇数的状况)
而为了知足条件二,根据有序数组的性质,咱们知道只须要知足 A[i - 1] <= B[j] 且 B[j - 1] <= A[i] 便可。
接下来开始咱们的算法探究:
假设咱们首先随机选择一个 i (这里 0 <= i < m),因此咱们根据条件一,能够求得 j = (m + n + 1) / 2 - i;
为了知足条件二,咱们开始分别比较 A[i - 1] 与 B[j] 和 B[j - 1] 与 A[i]:
不难知道可能会有四种状况:
那咱们如何缩小和扩大咱们的 i 呢,那就是采用二分查找的方式啦,首先将 i 置为数组A的中间下标,若是须要增大,则把其设为上半区的中间下标,反之则设为下半区的中间下标,因此这种搜索方式的时间复杂度和二分查找的时间复杂度同样,为了使时间复杂度尽可能的小,咱们使A成为长度更小的那个数组,若是初始A比B长,咱们则交换它们的位置。
考虑边界条件:
1.若是不存在知足条件二的状况会怎么样呢?也就是 i 走到了数组A的尽头,依旧无法知足条件二,这个时候咱们不难知道若是 i 走到数组A的最左端,那么它必定是在不断地经历状况4,而这时 A[0] > B[j],那么咱们不难知道这时left_part的最大值就是B[j - 1];反之咱们也能够推出 i 到了A的最右端、j 到了B的最左端或者最右端的状况。
2.m + n 为奇数。这个时候咱们不难推出 left_part 的最大值就是中位数。
3.A或B为空数组,由于当数组空的时候没法对数组进行下标访问,因此咱们在进行二分查找前就应该对该状况进行特殊处理,处理方式也是很简单的啦。
// 4. Median of Two Sorted Arrays double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { // 使得 nums1 短于 nums2 int m = nums1.size(); int n = nums2.size(); if (m > n) { vector<int> temp = nums1; nums1 = nums2; nums2 = temp; m = m + n; n = m - n; m = m - n; } // 考虑数组长度为0的边界状况 if (m == 0) { if (n == 0) { return 0; } else { if (n % 2 == 1) { return nums2[n / 2]; } else { return (double)(nums2[n / 2] + nums2[n / 2 - 1]) / 2; } } } int iMin = 0, iMax = m, sizeSum = (m + n + 1) / 2, i, j; while (iMin <= iMax) { i = (iMax + iMin) / 2; j = sizeSum - i; if (nums2[j - 1] > nums1[i] && i < iMax) { iMin = i + 1; } else if (nums1[i - 1] > nums2[j] && i > iMin) { iMax = i - 1; } else { int maxLeft, minRight; if (i == 0) { maxLeft = nums2[j - 1]; } else if (j == 0) { maxLeft = nums1[i - 1]; } else { maxLeft = max(nums1[i - 1], nums2[j - 1]); } if ((m + n) % 2 == 1) { return maxLeft; } if (i == m) { minRight = nums2[j]; } else if (j == n) { minRight = nums1[i]; } else { minRight = min(nums1[i], nums2[j]); } return (double)(maxLeft + minRight) / 2; } } return 0; }
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
字符串有 n(n-1)/2 个子串,对每一个子串进行检测,看其是不是回文子串。所以复杂度为 O(N^3)。
把字符串的每一个点分别当成回文子串的中间点,开始往两端扩展,不断检测,直到两端不相等为止。所以复杂度为 O(N^2)。
用 dp[i][j] 表示下标为 i 开头 j 结尾的子串是不是回文子串。
转移方程:dp[i][j] = (dp[i + 1][j - 1] && s[i] == s[j]) 【含义:当且仅当子串首尾两端相等,且去除首尾两端依旧是回文串时,该子串才会是回文串】
初始条件:对于每一个长度为1的子串 dp[i][i] 都为回文串;对于每一个长度为2的子串 dp[i][i + 1],当其首尾两端相等时,其为回文串,不然不是。
// 5. Longest Palindromic Substring (动态规划) string longestPalindrome(string s) { int length = s.size(); if (length == 0) return s; int resI = 0, resJ = 0; bool dp[length + 1][length + 1]; for (int i = 0; i <= length; i++) dp[i][i] = true; for (int i = 0; i < length; i++) { if (s[i] == s[i + 1]) { dp[i][i + 1] = true; if (resJ - resI < 1) { resI = i; resJ = i + 1; } } else { dp[i][i + 1] = false; } } for (int gap = 2; gap < length; gap++) { for (int i = 0; i + gap < length; i++) { int j = i + gap; if (s[i] == s[j] && dp[i + 1][j - 1]) { dp[i][j] = true; if (resJ - resI < j - i) { resI = i; resJ = j; } } else { dp[i][j] = false; } } } return s.substr(resI, resJ - resI + 1); }
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd" Output: "bb"
这道题倒没有特别的方法,就按照题目意思来模拟 Z 字形便可,用一个字符串数组来存放每一行的字符串,最后进行拼接便可。
考虑边界条件:
当numRows等于1的时候,由于point没法增长也没法减少,因此没办法共用后面的代码,考虑到numRows等于1的时候,答案就是原字符串,因此这里直接返回s便可。
// 6. ZigZag Conversion string convert(string s, int numRows) { if (numRows == 1) return s; string res; bool shouldIncrease = true; string strArr[numRows]; int point = 0; for (char c : s) { strArr[point] += c; if (point == numRows - 1) { shouldIncrease = false; } else if (point == 0) { shouldIncrease = true; } if (shouldIncrease) { point++; } else { point--; } } for (string str: strArr) { res += str; } return res; }
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 s, int numRows);
Example 1:
Input: s = "PAYPALISHIRING", numRows = 3 Output: "PAHNAPLSIIGYIR"
Example 2:
Input: s = "PAYPALISHIRING", numRows = 4 Output: "PINALSIGYAHRPI" Explanation: P I N A L S I G Y A H R P I
挨个遍历,不断把末位数赋给新的值便可。
考虑边界条件:
当结果溢出时返回0,因此为了避免让中间值溢出,采用 long 类型来保存结果。
// 7. Reverse Integer int reverse(int x) { long result = 0, longX = abs((long)x); while (longX > 0) { result = result * 10 + longX % 10; longX /= 10; } result = (x > 0) ? result : -result; if (result > INT32_MAX || result < INT32_MIN) { return 0; } else { return (int)result; } }
Given a 32-bit signed integer, reverse digits of an integer.
Example 1:
Input: 123 Output: 321
Example 2:
Input: -123 Output: -321
Example 3:
Input: 120 Output: 21
Note:
Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−2^31, 2^31 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.
遍历字符串而后进行分状况讨论:( isInit 表示数字是否已经开始,经过 isInit 的值判断是否为开头,若是为 true 表示不是开头)
(1) 空格:若是为开头空格则continue,不然跳出循环
(2) 正负号:若是为开头正负号则设置isNeg的值,不然跳出循环
(3) 数字:将 isInit 置为true,累加结果
(4) 其余符号:跳出循环
考虑边界条件:
当结果溢出时根据正负返回 INT32_MAX 或者 INT32_MIN,因此为了避免让中间值溢出,采用 long 类型来保存结果。
// 8. String to Integer (atoi) int myAtoi(string str) { long result = 0; bool isInit = false; bool isNeg = false; for (char c : str) { if (c == ' ') { if (isInit) { break; } else { continue; } } else if (c == '-' || c == '+') { if (!isInit) { isInit = true; } else { break; } isNeg = (c == '-'); } else if (c >= 48 && c <= 57) { isInit = true; result = result * 10 + (c - 48); if (result > INT32_MAX) { return isNeg ? INT32_MIN : INT32_MAX; } } else { break; } } return (int)(isNeg ? -result : result); }
Implementatoi
which converts a string to an integer.
The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value.
The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.
If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed.
If no valid conversion could be performed, a zero value is returned.
Note:
' '
is considered as whitespace character.Example 1:
Input: "42" Output: 42
Example 2:
Input: " -42" Output: -42 Explanation: The first non-whitespace character is '-', which is the minus sign. Then take as many numerical digits as possible, which gets 42.
Example 3:
Input: "4193 with words" Output: 4193 Explanation: Conversion stops at digit '3' as the next character is not a numerical digit.
Example 4:
Input: "words and 987" Output: 0 Explanation: The first non-whitespace character is 'w', which is not a numerical digit or a +/- sign. Therefore no valid conversion could be performed.
Example 5:
Input: "-91283472332" Output: -2147483648 Explanation: The number "-91283472332" is out of the range of a 32-bit signed integer. Thefore INT_MIN (−231) is returned.
利用第七题的代码,将数字反转,判断与原数字是否相等便可,这里考虑到负数所有都不是回文数字,因此直接返回false。
// 9. Palindrome Number int reverse(int x) { long result = 0, longX = abs((long)x); while (longX > 0) { result = result * 10 + longX % 10; longX /= 10; } result = (x > 0) ? result : -result; if (result > INT32_MAX || result < INT32_MIN) { return 0; } else { return (int)result; } } bool isPalindrome(int x) { if (x < 0) { return false; } else { return (x == reverse(x)); } }
Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward.
Example 1:
Input: 121 Output: true
Example 2:
Input: -121 Output: false Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.
Example 3:
Input: 10 Output: false Explanation: Reads 01 from right to left. Therefore it is not a palindrome.
Follow up:
Coud you solve it without converting the integer to a string?
动态规划思想解答这道题:
用 dp[i][j] 表示 s 的前 i 个字符组成的字符串和 p 的 前 j 个字符组成的字符串是否匹配。
转移方程:
当 p[j - 1] == '*' 时:由于 * 能够表示匹配零位或者多位,正则匹配这里要作贪心考虑,分三种状况,只要其中一种知足即为true:
当 p[j - 1] != '*' 时,dp[i][j] 当且仅当 dp[i - 1][j - 1]为true时,而且最后一位匹配成功时,才为true。
初始状态:
显然,当 s 不为空,p 为空的时候dp[i][j] = false;
其次,当 s 为空,p不为空的时候,考虑到 * 能够匹配零位,因此利用状态转移方程判断其是否应该为true。
// 10. Regular Expression Matching bool isMatch(string s, string p) { int n = s.size(); int m = p.size(); // initial bool dp[n + 1][m + 1]; for (int i = 0; i < n + 1; i++) { for (int j = 0; j < m + 1; j++) { dp[i][j] = false; } } // start dp[0][0] = true; for (int i = 1; i < n + 1; i++) { dp[i][0] = false; } for (int j = 1; j < m + 1; j++) { if (j % 2 == 0) { dp[0][j] = dp[0][j - 2] && p[j - 1] == '*'; } else { dp[0][j] = false; } } // trans bool compare; for (int i = 1; i < n + 1; i++) { for (int j = 1; j < m + 1; j++) { if (p[j - 1] != '*') { dp[i][j] = dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.'); } else { compare = (s[i - 1] == p[j - 2] || p[j - 2] == '.'); dp[i][j] = dp[i][j - 2] || (dp[i - 1][j - 2] && compare) || (dp[i - 1][j] && compare); } } } return dp[n][m]; }
Given an input string (s
) and a pattern (p
), implement regular expression matching with support for'.'
and'*'
.
'.' Matches any single character. '*' Matches zero or more of the preceding element.
The matching should cover theentireinput string (not partial).
Note:
s
could be empty and contains only lowercase lettersa-z
p
could be empty and contains only lowercase lettersa-z
, and characters like .
or *
.Example 1:
Input: s = "aa" p = "a" Output: false Explanation: "a" does not match the entire string "aa".
Example 2:
Input: s = "aa" p = "a*" Output: true Explanation: '*' means zero or more of the precedeng element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:
Input: s = "ab" p = ".*" Output: true Explanation: ".*" means "zero or more (*) of any character (.)".
Example 4:
Input: s = "aab" p = "c*a*b" Output: true Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches "aab".
Example 5:
Input: s = "mississippi" p = "mis*is*p*." Output: false