Leetcode Solutions - Part 2
1. Two Sum
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
Solution 1
双层遍历
Solution 2
哈希表
The basic idea is to maintain a hash table for each element num in nums,
using num as key and its index (0-based) as value. For each num,search for target - num in the hash table.If it is found and is not the same element as num, then we are done.The code is as follows. Note that each time before we add num to mp,we search for target - num first and so we will not hit the same element.
18. 四数之和
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
Solution 1 暴力枚举
Solution 2 双指针法
The two pointers pattern requires the array to be sorted, so we do that first. Also, it's easier to deal with duplicates if the array is sorted: repeated values are next to each other and easy to skip.For 3Sum, we enumerate each value in a single loop, and use the two pointers pattern for the rest of the array. For kSum, we will have k - 2 nested loops to enumerate all combinations of k - 2 values.
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
Solution1 计算链表长度
Solution2 栈
Solution3 快慢指针
20. 有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
Solution1 栈
class Solution {
public:bool isValid(string s) {stack<char> st; //taking stack for keep tracking the order of the brackets..for(auto i:s) //iterate over each and every elements{if(i=='(' or i=='{' or i=='[') st.push(i); //if current element of the string will be opening bracket then we will just simply push it into the stackelse //if control comes to else part, it means that current element is a closing bracket, so check two conditions current element matches with top of the stack and the stack must not be empty...{if(st.empty() or (st.top()=='(' and i!=')') or (st.top()=='{' and i!='}') or (st.top()=='[' and i!=']')) return false;st.pop(); //if control reaches to that line, it means we have got the right pair of brackets, so just pop it.}}return st.empty(); //at last, it may possible that we left something into the stack unpair so return checking stack is empty or not..}
};
26. 删除有序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
int[] nums = […]; // 输入数组
int[] expectedNums = […]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums 已按 升序 排列
Solution1 暴力
Solution2 双指针
Solution3 双指针 优化
31. 下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
1 <= nums.length <= 100
0 <= nums[i] <= 100
Solution1 暴力
Solution2
48. 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
Solution1 原地查找
Solution2 先转置再竖直对称反转
class Solution {
public:void rotate(vector<vector<int>>& matrix) {//先转置for(int row=0;row<=matrix.size()-1;row++){for(int col=row;col<=matrix[0].size()-1;col++){swap(matrix[row][col],matrix[col][row]);}}//在竖直对称翻转for(int row=0;row<=matrix.size()-1;row++){for(int col = 0;col<=(matrix.size()-1)/2;col++){swap(matrix[row][col],matrix[row][matrix.size()-1-col]);}}}
};
49. 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
输入: strs = [“”]
输出: [[“”]]
示例 3:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母
Solution1 Map
Use an unordered_map to group the strings by their sorted counterparts. Use the sorted string as the key and all anagram strings as the value.
Moreover, since the string only contains lower-case alphabets, we can sort them using counting sort to improve the time complexity.
class Solution {
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {map<string,vector<string>>res;//遍历 for(auto val:strs){string tmp=val;//以排序后的str作为key vector<string>作为value 放进mapsort(val.begin(),val.end());if(res.find(val)==res.end()){vector<string> t={tmp};res.insert({val,t});}else{res[val].push_back(tmp);}}//遍历输出结果vector<vector<string>> result;for(auto p:res){result.push_back(p.second);}return result;}
};
54. 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
spiral-matrixAlgorithm:First we will iterate in to first row from left to right push back all the elements into a vector. After iterating, we change the top to second row (top++).
Then we will iterate from new top to bottom and push back only right most elements of each row. After iterating, we change the right to second last column (right--).
Then we will iterate in bottom row from right to left and pushback all the elements from new right to left. After iterating, we change the bottom to second last row (bottom--).
Then we will iterate from new bottom to new top and push back only left most element. After iterating, we change the left to second column (left++).
Repeat all these steps until left = right and top = bottom.
Solution1 原地遍历
class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {vector<int> res;if(matrix.empty()){return res;}int s=0,x=matrix.size()-1,z=0,y=matrix[0].size()-1;while(true){//在上边沿for(int i=s,j=z;j<=y;j++){res.push_back(matrix[i][j]);}if(++s>x) break;//在右边沿for(int i=s,j=y;i<=x;i++){res.push_back(matrix[i][j]);}if(--y<z) break;//在下边沿for(int i=x,j=y;j>=z;j--){res.push_back(matrix[i][j]);}if(--x<s) break;//在左边沿for(int i=x,j=z;i>=s;i--){res.push_back(matrix[i][j]);}if(++z>y) break;}return res;}
};
Solution2 按层遍历
class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {if (matrix.size() == 0 || matrix[0].size() == 0) {return {};}int rows = matrix.size(), columns = matrix[0].size();vector<int> order;int left = 0, right = columns - 1, top = 0, bottom = rows - 1;while (left <= right && top <= bottom) {for (int column = left; column <= right; column++) {order.push_back(matrix[top][column]);}for (int row = top + 1; row <= bottom; row++) {order.push_back(matrix[row][right]);}if (left < right && top < bottom) {for (int column = right - 1; column > left; column--) {order.push_back(matrix[bottom][column]);}for (int row = bottom; row > top; row--) {order.push_back(matrix[row][left]);}}left++;right--;top++;bottom--;}return order;}
};
56. 合并区间
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
nitially sort the array and then push the first element into the answer for speculation.
We have two condition if the first elements second part of ans array is greater than or equal to the second element first part of the
interval array.
The other condition we have to tackle is what if its not? then we push the particular element into the ans array which will be then be under speculation.
interval: [[1,3],[2,6],[8,10],[15,18]]i
We initally push the 1st element into the ans array:
ans=[[1,3]]j j points to the latest pushed element
Then we i is incremented.
[[1,3],[2,6],[8,10],[15,18]]i
Now the ans[j][1]>interval[i][0] this means there is a possiblity of merging so we merger them
Remember the way we merge is to take the second element as max(ans[j][1],interval[i][1])
cuz imagine we have this
[1,7][2,4] --->merge should be ---->[1,7]ans=[[1,6]]then we move i forward[[1,3],[2,6],[8,10],[15,18]]i
Since ans[j][1]<interval[i][0] thus not contributing to the merge.
Thus we will push this into the ans array and speculate.ans=[[1,6][8,10]]j <----j is moved forward
i is moved forward
[[1,3],[2,6],[8,10],[15,18]]i
Since ans[j][1]<interval[i][0] thus not contributing to the merge.
ans=[[1,6][8,10][15,18]]jThus yielding our final answer.vector<vector<int>> merge(vector<vector<int>>& interval) {vector<vector<int>> ans;if(interval.size()==0)return ans;sort(interval.begin(),interval.end());ans.push_back(interval[0]);int j=0;for(int i=1;i<interval.size();i++){if(ans[j][1]>=interval[i][0])ans[j][1]=max(ans[j][1],interval[i][1]);else{j++;ans.push_back(interval[i]);}}return ans;}
Solution1 排序
复杂度分析
时间复杂度:O(n\log n)O(nlogn),其中 nn 为区间的数量。除去排序的开销,我们只需要一次线性扫描,所以主要的时间开销是排序的 O(n\log n)O(nlogn)。
空间复杂度:O(\log n)O(logn),其中 nn 为区间的数量。这里计算的是存储答案之外,使用的额外空间。O(\log n)O(logn) 即为排序所需要的空间复杂度。
class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {if (intervals.size() == 0) {return {};}sort(intervals.begin(), intervals.end());vector<vector<int>> merged;for (int i = 0; i < intervals.size(); ++i) {int L = intervals[i][0], R = intervals[i][1];if (!merged.size() || merged.back()[1] < L) {merged.push_back({L, R});}else {merged.back()[1] = max(merged.back()[1], R);}}return merged;}
};
61. 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
链表中节点的数目在范围 [0, 500] 内
-100 <= Node.val <= 100
0 <= k <= 2 * 109
There is no trick for this problem. Some people used slow/fast pointers to find the tail node but I don't see the benefit (in the sense that it doesn't reduce the pointer move op) to do so. So I just used one loop to find the length first.class Solution {
public:ListNode* rotateRight(ListNode* head, int k) {if(!head) return head;int len=1; // number of nodesListNode *newH, *tail;newH=tail=head;while(tail->next) // get the number of nodes in the list{tail = tail->next;len++;}tail->next = head; // circle the linkif(k %= len) {for(auto i=0; i<len-k; i++) tail = tail->next; // the tail node is the (len-k)-th node (1st node is head)}newH = tail->next; tail->next = NULL;return newH;}
};
Solution1 暴力
Soluion2 闭环
66. 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:
输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:
1 <= digits.length <= 100
0 <= digits[i] <= 9
My solution is nothing special and isn't clever at all. I decided to post it since I thought the "official" solution article from leetcode was very poorly written and confused me more, even after I solved it on my own.So, I believe my comments below should explain the idea, but I want to add that it helps to test the more obscure test cases for this problem to understand the algorithm. For example:[9]
[9090]
class Solution {
public:vector<int> plusOne(vector<int>& digits) {int n = digits.size() - 1;for (int i = n; i >= 0; --i) { // traverse digits from the last element (least significant)// since we begin with the last digit, increasing that digit by one// results in overflow. Therefore, all elements PRIOR to digits[0]// need to be considered since there may be additional nines between// digits[0], ... , digits[n].if (digits[i] == 9) { digits[i] = 0;} else { // current digit is not 9 so we can safely increment by onedigits[i] += 1;return digits;}}// if the program runs to this point, each 9 is now a 0.// to get a correct solution, we need to add one more element with // a value of zero AND set digits[0] to 1 (in the most significant position)// to account for the carry digit.digits.push_back(0);digits[0] = 1;return digits;}
};
Solution 0 暴力
Solution 1 逆序遍历 找到9
75. 颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
The solution requires the use of tracking 3 positions, the Low, Mid and High.We assume that the mid is the "Unknown" area that we must evaluate.If we encounter a 0, we know that it will be on the low end of the array, and if we encounter a 2, we know it will be on the high end of the array.To achieve this in one pass without preprocessing (counting), we simply traverse the unknown will generating the low and high ends.Take this example:Assume our input is: 1 0 2 2 1 0 (short for simplicity).Running the algorithm by hand would look something like:1 0 2 2 1 0^ ^L HMMid != 0 || 2Mid++1 0 2 2 1 0^ ^ ^L M HMid == 0Swap Low and MidMid++Low++0 1 2 2 1 0^ ^ ^L M HMid == 2Swap High and MidHigh--0 1 0 2 1 2^ ^ ^L M HMid == 0Swap Low and MidMid++Low++0 0 1 2 1 2^ ^ ^L M HMid == 2Swap High and MidHigh--0 0 1 1 2 2^ ^L MHMid <= High is our exit case
Solution1 暴力
根据题目中的提示,我们可以统计出数组中 0, 1, 20,1,2 的个数,再根据它们的数量,重写整个数组。这种方法较为简单,也很容易想到,而本题解中会介绍两种基于指针进行交换的方法。
Solution2 单指针
我们可以考虑对数组进行两次遍历。在第一次遍历中,我们将数组中所有的 00 交换到数组的头部。在第二次遍历中,我们将数组中所有的 11 交换到头部的 00 之后。此时,所有的 22 都出现在数组的尾部,这样我们就完成了排序。
具体地,我们使用一个指针 \textit{ptr}ptr 表示「头部」的范围,\textit{ptr}ptr 中存储了一个整数,表示数组 \textit{nums}nums 从位置 00 到位置 \textit{ptr}-1ptr−1 都属于「头部」。\textit{ptr}ptr 的初始值为 00,表示还没有数处于「头部」。
在第一次遍历中,我们从左向右遍历整个数组,如果找到了 00,那么就需要将 00 与「头部」位置的元素进行交换,并将「头部」向后扩充一个位置。在遍历结束之后,所有的 00 都被交换到「头部」的范围,并且「头部」只包含 00。
在第二次遍历中,我们从「头部」开始,从左向右遍历整个数组,如果找到了 11,那么就需要将 11 与「头部」位置的元素进行交换,并将「头部」向后扩充一个位置。在遍历结束之后,所有的 11 都被交换到「头部」的范围,并且都在 00 之后,此时 22 只出现在「头部」之外的位置,因此排序完成。
class Solution {
public:void sortColors(vector<int>& nums) {int n = nums.size();int ptr = 0;for (int i = 0; i < n; ++i) {if (nums[i] == 0) {swap(nums[i], nums[ptr]);++ptr;}}for (int i = ptr; i < n; ++i) {if (nums[i] == 1) {swap(nums[i], nums[ptr]);++ptr;}}}
};
Solution3 双指针
class Solution {
public:void sortColors(vector<int>& nums) {int n = nums.size();int p0 = 0, p2 = n - 1;for (int i = 0; i <= p2; ++i) {while (i <= p2 && nums[i] == 2) {swap(nums[i], nums[p2]);--p2;}if (nums[i] == 0) {swap(nums[i], nums[p0]);++p0;}}}
};
83. 删除排序链表中的重复元素
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
输入:head = [1,1,2]
输出:[1,2]
示例 2:
输入:head = [1,1,2,3,3]
输出:[1,2,3]
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序 排列
noticed that the solutions posted here are too long and complicated. They use unnecessary variables and/or checks etc.
The solution can be much more concise. Here is my solution:class Solution {
public:ListNode *deleteDuplicates(ListNode *head) {ListNode* cur = head;while (cur) {while (cur->next && cur->val == cur->next->val)cur->next = cur->next->next;cur = cur->next;}return head;}
};
Note about freeing memory. We need to free memory when we delete a node. But don't use delete node; construct on an interview without discussing it with the interviewer. A list node can be allocated in many different ways and we can use delete node; only if we are sure that the nodes were allocated with new TreeNode(...);.
Solution1 一次遍历
方法一:一次遍历
思路与算法
由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。
具体地,我们从指针 \textit{cur}cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 \textit{cur}cur 与 \textit{cur.next}cur.next 对应的元素相同,那么我们就将 \textit{cur.next}cur.next 从链表中移除;否则说明链表中已经不存在其它与 \textit{cur}cur 对应的元素相同的节点,因此可以将 \textit{cur}cur 指向 \textit{cur.next}cur.next。
当遍历完整个链表之后,我们返回链表的头节点即可。
细节
当我们遍历到链表的最后一个节点时,\textit{cur.next}cur.next 为空节点,如果不加以判断,访问 \textit{cur.next}cur.next 对应的元素会产生运行错误。因此我们只需要遍历到链表的最后一个节点,而不需要遍历完整个链表。
复杂度分析
时间复杂度:O(n)O(n),其中 nn 是链表的长度。
空间复杂度:O(1)O(1)。
class Solution {
public:ListNode* deleteDuplicates(ListNode* head) {if (!head) {return head;}ListNode* cur = head;while (cur->next) {if (cur->val == cur->next->val) {cur->next = cur->next->next;}else {cur = cur->next;}}return head;}
};
86. 分隔链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
示例 2:
输入:head = [2,1], x = 2
输出:[1,2]
链表中节点的数目在范围 [0, 200] 内
-100 <= Node.val <= 100
-200 <= x <= 200
Approach 1: Two Pointer Approach
IntuitionWe can take two pointers before and after to keep track of the two linked lists as described above. These two pointers could be used two create two separate lists and then these lists could be combined to form the desired reformed list.AlgorithmInitialize two pointers before and after. In the implementation we have initialized these two with a dummy ListNode. This helps to reduce the number of conditional checks we would need otherwise. You can try an implementation where you don't initialize with a dummy node and see it yourself!Dummy Node InitializationIterate the original linked list, using the head pointer.If the node's value pointed by head is lesser than x, the node should be part of the before list. So we move it to before list.Else, the node should be part of after list. So we move it to after list.Once we are done with all the nodes in the original linked list, we would have two list before and after. The original list nodes are either part of before list or after list, depending on its value.Note: Since we traverse the original linked list from left to right, at no point would the order of nodes change relatively in the two lists. Another important thing to note here is that we show the original linked list intact in the above diagrams. However, in the implementation, we remove the nodes from the original linked list and attach them in the before or after list. We don't utilize any additional space. We simply move the nodes from the original list around.Now, these two lists before and after can be combined to form the reformed list.We did a dummy node initialization at the start to make implementation easier, you don't want that to be part of the returned list, hence just move ahead one node in both the lists while combining the two list. Since both before and after have an extra node at the front.
Solution1 双指针
88. 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
示例 3:
进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?
The way to think about the solution is that we will have to do a reverse sorting.
We initialize k=m+n-1 as that will be the last location of nums1.
We will keep checking for the greater element of the two arrays(i=m-1,j=n-1) and insert the values.
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3nums1 = [1,2,3,0,0,0]| |i knums2 = [2,5,6]|j
nums2[j]>nums1[i] thus nums1[k]=6
k and j are decremented.nums1 = [1,2,3,0,0,6]| |i knums2 = [2,5,6]|j
nums2[j]>nums1[i] thus nums1[k]=5
k and j are decremented.nums1 = [1,2,3,0,5,6]| |i knums2 = [2,5,6]|j
We keep following up this procedure and we get the desired reult.void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int i=m-1,j=n-1,k=m+n-1;while(i>=0&&j>=0){if(nums1[i]>nums2[j]){nums1[k]=nums1[i];i--;k--;}else{nums1[k]=nums2[j];j--;k--;}}while(i>=0)nums1[k--]=nums1[i--];while(j>=0)nums1[k--]=nums2[j--];}
Solution1 暴力直接合并后排序
Solution2 双指针
class Solution {
public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int p1 = 0, p2 = 0;int sorted[m + n];int cur;while (p1 < m || p2 < n) {if (p1 == m) {cur = nums2[p2++];} else if (p2 == n) {cur = nums1[p1++];} else if (nums1[p1] < nums2[p2]) {cur = nums1[p1++];} else {cur = nums2[p2++];}sorted[p1 + p2 - 1] = cur;}for (int i = 0; i != m + n; ++i) {nums1[i] = sorted[i];}}
};
复杂度分析
时间复杂度:O(m+n)O(m+n)。
指针移动单调递增,最多移动 m+nm+n 次,因此时间复杂度为 O(m+n)O(m+n)。
空间复杂度:O(m+n)O(m+n)。
需要建立长度为 m+nm+n 的中间数组 \textit{sorted}sorted。
Solution3 逆向双指针
class Solution {
public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int p1 = m - 1, p2 = n - 1;int tail = m + n - 1;int cur;while (p1 >= 0 || p2 >= 0) {if (p1 == -1) {cur = nums2[p2--];} else if (p2 == -1) {cur = nums1[p1--];} else if (nums1[p1] > nums2[p2]) {cur = nums1[p1--];} else {cur = nums2[p2--];}nums1[tail--] = cur;}}
};
92. 反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:
输入:head = [5], left = 1, right = 1
输出:[5]
链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
Solution1 原地反转
AlgorithmWe define a recursion function that will do the job of reversing a portion of the linked list.
Let's call this function recurse. The function takes in 3 parameters: m being the starting point of the reversal, n being the ending point for the reversal, and a pointer right which will start at the n^{th}n
thnode in the linked list and move backwards with the backtracking of the recursion. If this is not clear at the moment, the diagrams that follow will help.
Additionally, we have a pointer called left which starts from the m^{th}m
thnode in the linked list and moves forward. In Python, we have to take a global variable for this which get's changed with recursion. In other languages, where changes made in function calls persist, we can consider this pointer as an additional variable for the function recurse.
In a recursion call, given m, n, and right, we check if n == 1. If this is the case, we don't need to go any further.
Until we reach n = 1, we keep moving the right pointer one step forward and after doing that, we make a recursive call with the value of n decreased by 1. At the same time, we keep on moving the left pointer forward until m == 1. When we refer to a pointer being moved forward, it essentially means pointer.next.
So we backtrack as soon as n reaches 1. At that point of time, the right pointer is at the last node of the sublist we want to reverse and the left has already reached the first node of this sublist. So, we swap out the data and move the left pointer one step forward using left = left.next. We need this change to persist across the backtracking process.
From there on, every time we backtrack, the right pointer moves one step backwards. This is the simulation we've been mentioning all along. The backward movement is simulated by backtracking.
We stop the swaps when either right == left, which happens if the sublist size is odd, or, right.next == left which happens when during the backtracking process for an even sized sublist, the right pointer crosses left. We use a global boolean flag for stopping the swaps once these conditions are met.
Let's look at a series of diagrams explaining the process on a sample linked list. Hopefully, things would be clearer after this.This is the first step in the recursion process. We have a list given to us and the left and the right pointers start off from the head of the linked list. The first step makes a recursive call with updated values of m and n i.e. their values each reduced by 1. Also, the left and the right pointers move one step forward in the linked list.The next two steps show the movement of the left and the right pointers in the list. Notice that after the second step, the left pointer reaches it's designated spot. So, we don't move it any further. Only the right pointer progresses from here on out until it reaches node 6.As we can see, after the step 5, both the pointers are in their designated spots in the list and we can start the backtracking process. We don't recurse further. The operation performed during the backtracking is swapping of data between the left and right nodes.The right pointer crosses the left pointer after step 3 (backtracking) as can be seen above and by that point, we have already reversed the required portion of the linked list. We needed the output list to be [7 → 9 → 8 → 1 → 10 → 2 → 6] and that's what we have. So, we don't perform any more swaps and in the code, we can use a global boolean flag to stop the swapping after a point. We can't really break out of recursion per say.
118. 杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:
The pascal triangle problem has a very simple and intuitive dynamic programming approach. As the definition states, every element of a row is formed by the sum of the two numbers directly above. So, we can just apply DP and use the previously stored rows of trianlge to calculate the new rows.We can just initialize the start and end elements of each row as 1 and update only the elements between them. This will make the code simpler and avoid the need of having extra checks for edge elements of each row.
Solution1 直接求解
lass Solution {
public:vector<vector<int>> generate(int numRows) {vector<vector<int>> yanghui;for (int i = 0; i < numRows; i++){vector<int> data(i + 1);yanghui.push_back(data);for (int j = 0; j < i; ++j){if(j == 0) yanghui[i][j] = 1;else{yanghui[i][j] = yanghui[i - 1][j - 1] + yanghui[i - 1][j];}}yanghui[i][yanghui[i].size() - 1] = 1;}return yanghui;}
};
125. 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个回文串。
给你一个字符串 s,如果它是回文串,返回 true ;否则,返回 false 。
输入: “A man, a plan, a canal: Panama”
输出:true
解释:“amanaplanacanalpanama” 是回文串。
示例 2:
输入:“race a car”
输出:false
解释:“raceacar” 不是回文串。
示例 3:
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 “” 。
由于空字符串正着反着读都一样,所以是回文串。
1 <= s.length <= 2 * 105
s 仅由可打印的 ASCII 字符组成
bool isPalindrome(string s) {for (int i = 0, j = s.size() - 1; i < j; i++, j--) { // Move 2 pointers from each end until they collidewhile (isalnum(s[i]) == false && i < j) i++; // Increment left pointer if not alphanumericwhile (isalnum(s[j]) == false && i < j) j--; // Decrement right pointer if no alphanumericif (toupper(s[i]) != toupper(s[j])) return false; // Exit and return error if not match}return true;
}
Solution1 筛选 + 判断
Solution2 筛选 + 判断 02
Solution03 在原字符串上直接判断
141. 环形链表
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。
Solution1 哈希表
Solution2
142. 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -1 或者链表中的一个有效索引
Alogrithm Description:
Step 1: Determine whether there is a cycle1.1) Using a slow pointer that move forward 1 step each time1.2) Using a fast pointer that move forward 2 steps each time1.3) If the slow pointer and fast pointer both point to the same location after several moving steps, there is a cycle;1.4) Otherwise, if (fast->next == NULL || fast->next->next == NULL), there has no cycle.Step 2: If there is a cycle, return the entry location of the cycle2.1) L1 is defined as the distance between the head point and entry point2.2) L2 is defined as the distance between the entry point and the meeting point2.3) C is defined as the length of the cycle2.4) n is defined as the travel times of the fast pointer around the cycle When the first encounter of the slow pointer and the fast pointerAccording to the definition of L1, L2 and C, we can obtain:the total distance of the slow pointer traveled when encounter is L1 + L2the total distance of the fast pointer traveled when encounter is L1 + L2 + n * CBecause the total distance the fast pointer traveled is twice as the slow pointer, Thus:2 * (L1+L2) = L1 + L2 + n * C => L1 + L2 = n * C => L1 = (n - 1) C + (C - L2)*It can be concluded that the distance between the head location and entry location is equal to the distance between the meeting location and the entry location along the direction of forward movement.So, when the slow pointer and the fast pointer encounter in the cycle, we can define a pointer "entry" that point to the head, this "entry" pointer moves one step each time so as the slow pointer. When this "entry" pointer and the slow pointer both point to the same location, this location is the node where the cycle begins.
Solution1 哈希表
Solution2 双指针
class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode *slow = head, *fast = head;while (fast != nullptr) {slow = slow->next;if (fast->next == nullptr) {return nullptr;}fast = fast->next->next;if (fast == slow) {ListNode *ptr = head;while (ptr != slow) {ptr = ptr->next;slow = slow->next;}return ptr;}}return nullptr;}
};
143. 重排链表
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
链表的长度范围为 [1, 5 * 104]
1 <= node.val <= 1000
If you never solved singly linked lists problems before, or you do not have a lot of experience, this problem can be quite difficult. However if you already know all the tricks, it is not difficult at all. Let us first try to understand what we need to do. For list [1,2,3,4,5,6,7] we need to return [1,7,2,6,3,5,4]. We can note, that it is actually two lists [1,2,3,4] and [7,6,5], where elements are interchange. So, to succeed we need to do the following steps:Find the middle of or list - be careful, it needs to work properly both for even and for odd number of nodes. For this we can either just count number of elements and then divide it by to, and do two traversals of list. Or we can use slow/fast iterators trick, where slow moves with speed 1 and fast moves with speed 2. Then when fast reches the end, slow will be in the middle, as we need.
Reverse the second part of linked list. Again, if you never done it before, it can be quite painful, please read oficial solution to problem 206. Reverse Linked List. The idea is to keep three pointers: prev, curr, nextt stand for previous, current and next and change connections in place. Do not forget to use slow.next = None, in opposite case you will have list with loop.
Finally, we need to merge two lists, given its heads. These heads are denoted by head and prev, so for simplisity I created head1 and head2 variables. What we need to do now is to interchange nodes: we put head2 as next element of head1 and then say that head1 is now head2 and head2 is previous head1.next. In this way we do one step for one of the lists and rename lists, so next time we will take element from head2, then rename again and so on.
Complexity: Time complexity is O(n), because we first do O(n) iterations to find middle, then we do O(n) iterations to reverse second half and finally we do O(n) iterations to merge lists. Space complexity is O(1).
Solution0 暴力
Solution1 双指针
1.快慢指针找到中点 2.拆成两个链表 3.遍历两个链表,后面的塞到前面的“缝隙里”
void reorderList(ListNode* head) {if(head==NULL || head->next == NULL)return;//快慢指针分出两段ListNode *slow = head,*fast = head;while(fast->next && fast->next->next){slow = slow->next;fast = fast->next->next;}//后端反转ListNode *needReverser = slow->next;slow->next = NULL;needReverser = reverse(needReverser);//插入前端缝隙ListNode *cur = head;while(cur && needReverser){ListNode *curSecond = needReverser;needReverser = needReverser->next;ListNode *nextCur = cur->next;curSecond->next = cur->next;cur->next = curSecond;cur = nextCur;}}ListNode *reverse(ListNode *head){ListNode *p1 = NULL;ListNode *p2 = head;ListNode *p3 = p2;while(p2){p3 = p2->next;p2->next = p1;p1 = p2;p2 = p3; }return p1;}
146. LRU 缓存
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
最多调用 2 * 105 次 get 和 put
n this question we have to keep track of the most least recently used item in the cache. I have designed the cache using list and map in C++.
We do it by following the steps below :-When we access an item in the cache it moves to the front of the list as it is the most recently used item.
When we want to remove an item we remove it from the last of the list as it is the least recently used item in the cache.
When we insert an item we insert it into the front of the list as it is the most recently used item.
The idea is to store the keys in the map and its corrosponding values into the list...
Note : splice() function here takes the element at the m[key] and places it at the beginning of the list...
Solution1 哈希表 + 双向链表
方法一:哈希表 + 双向链表
算法
LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
这样以来,我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
对于 get 操作,首先判断 key 是否存在:
如果 key 不存在,则返回 -1−1;
如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
对于 put 操作,首先判断 key 是否存在:
如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
上述各项操作中,访问哈希表的时间复杂度为 O(1)O(1),在双向链表的头部添加节点、在双向链表的尾部删除节点的复杂度也为 O(1)O(1)。而将一个节点移到双向链表的头部,可以分成「删除该节点」和「在双向链表的头部添加节点」两步操作,都可以在 O(1)O(1) 时间内完成。
struct DLinkedNode {int key, value;DLinkedNode* prev;DLinkedNode* next;DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};class LRUCache {
private:unordered_map<int, DLinkedNode*> cache;DLinkedNode* head;DLinkedNode* tail;int size;int capacity;public:LRUCache(int _capacity): capacity(_capacity), size(0) {// 使用伪头部和伪尾部节点head = new DLinkedNode();tail = new DLinkedNode();head->next = tail;tail->prev = head;}int get(int key) {if (!cache.count(key)) {return -1;}// 如果 key 存在,先通过哈希表定位,再移到头部DLinkedNode* node = cache[key];moveToHead(node);return node->value;}void put(int key, int value) {if (!cache.count(key)) {// 如果 key 不存在,创建一个新的节点DLinkedNode* node = new DLinkedNode(key, value);// 添加进哈希表cache[key] = node;// 添加至双向链表的头部addToHead(node);++size;if (size > capacity) {// 如果超出容量,删除双向链表的尾部节点DLinkedNode* removed = removeTail();// 删除哈希表中对应的项cache.erase(removed->key);// 防止内存泄漏delete removed;--size;}}else {// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部DLinkedNode* node = cache[key];node->value = value;moveToHead(node);}}void addToHead(DLinkedNode* node) {node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;}void removeNode(DLinkedNode* node) {node->prev->next = node->next;node->next->prev = node->prev;}void moveToHead(DLinkedNode* node) {removeNode(node);addToHead(node);}DLinkedNode* removeTail() {DLinkedNode* node = tail->prev;removeNode(node);return node;}
};
分治:148. 排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
链表中节点的数目在范围 [0, 5 * 104] 内
-105 <= Node.val <= 105
Brief note about Question-We have to return the linked list after sorting it in ascending order.
Let's take an example not given in question -
Suppose head of linked list given to us is like, head: [3,-9,8,67,9]then answer should like [-9,3,8,9,67] after sorting it in ascending order.
Solution - I (Using Merge sort, Accepted)-We want to sort a linked list, then we may able to use any of the sorting algorithm and then apply on it.
We will use merge sort here, because I find it easy to implement in linked list.
Whole implementation totally based on the merge sort, so i strongly suggest you to read a article on the merge sort.
Some basic thing that we will do in applying merge sort on our linked list are-
We divide our linked liist into two equal parts until when only one element is left.
After that we start merge them on the basis of value.
Now, if we divide them into two equal parts then then how we will find mid in linked list.
We find mid of linked list using tortise and hare method or say using fast and slow pointer.
See commented code for more explanation.
Solution1 自顶向下归并排序
class Solution {
public:ListNode* sortList(ListNode* head) {return sortList(head, nullptr);}ListNode* sortList(ListNode* head, ListNode* tail) {if (head == nullptr) {return head;}if (head->next == tail) {head->next = nullptr;return head;}ListNode* slow = head, *fast = head;while (fast != tail) {slow = slow->next;fast = fast->next;if (fast != tail) {fast = fast->next;}}ListNode* mid = slow;return merge(sortList(head, mid), sortList(mid, tail));}ListNode* merge(ListNode* head1, ListNode* head2) {ListNode* dummyHead = new ListNode(0);ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;while (temp1 != nullptr && temp2 != nullptr) {if (temp1->val <= temp2->val) {temp->next = temp1;temp1 = temp1->next;} else {temp->next = temp2;temp2 = temp2->next;}temp = temp->next;}if (temp1 != nullptr) {temp->next = temp1;} else if (temp2 != nullptr) {temp->next = temp2;}return dummyHead->next;}
};
Solution2 自底向上归并排序
class Solution {
public:ListNode* sortList(ListNode* head) {if (head == nullptr) {return head;}int length = 0;ListNode* node = head;while (node != nullptr) {length++;node = node->next;}ListNode* dummyHead = new ListNode(0, head);for (int subLength = 1; subLength < length; subLength <<= 1) {ListNode* prev = dummyHead, *curr = dummyHead->next;while (curr != nullptr) {ListNode* head1 = curr;for (int i = 1; i < subLength && curr->next != nullptr; i++) {curr = curr->next;}ListNode* head2 = curr->next;curr->next = nullptr;curr = head2;for (int i = 1; i < subLength && curr != nullptr && curr->next != nullptr; i++) {curr = curr->next;}ListNode* next = nullptr;if (curr != nullptr) {next = curr->next;curr->next = nullptr;}ListNode* merged = merge(head1, head2);prev->next = merged;while (prev->next != nullptr) {prev = prev->next;}curr = next;}}return dummyHead->next;}ListNode* merge(ListNode* head1, ListNode* head2) {ListNode* dummyHead = new ListNode(0);ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;while (temp1 != nullptr && temp2 != nullptr) {if (temp1->val <= temp2->val) {temp->next = temp1;temp1 = temp1->next;} else {temp->next = temp2;temp2 = temp2->next;}temp = temp->next;}if (temp1 != nullptr) {temp->next = temp1;} else if (temp2 != nullptr) {temp->next = temp2;}return dummyHead->next;}
};
150. 逆波兰表达式求值
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
1 <= tokens.length <= 104
tokens[i] 是一个算符(“+”、“-”、“*” 或 “/”),或是在范围 [-200, 200] 内的一个整数
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
Idea:
Reverse Polish Notation was designed specifically to make computing easier with the more efficient use of a stack. So we can use a stack here to store numbers until they're used, and then each operand will use the top two values of the stack.Since the order of the numbers is still important for subtraction and division, we'll have to make sure that the two numbers are processed in their original order, which is the opposite order of the stack.After each successful operation, the result should be pushed back onto the stack until it's used. After iteration is complete, the remaining value in the stack will be our answer, so we should return stack[0].Time Complexity: O(N) where N is the length of tokens
Space Complexity: O(N) for the length of the stack, up to N / 2 + 1 values
Solution1 栈
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> stk;int n = tokens.size();for (int i = 0; i < n; i++) {string& token = tokens[i];if (isNumber(token)) {stk.push(atoi(token.c_str()));} else {int num2 = stk.top();stk.pop();int num1 = stk.top();stk.pop();switch (token[0]) {case '+':stk.push(num1 + num2);break;case '-':stk.push(num1 - num2);break;case '*':stk.push(num1 * num2);break;case '/':stk.push(num1 / num2);break;}}}return stk.top();}bool isNumber(string& token) {return !(token == "+" || token == "-" || token == "*" || token == "/");}
};
复杂度分析
时间复杂度:O(n)O(n),其中 nn 是数组 \textit{tokens}tokens 的长度。需要遍历数组 \textit{tokens}tokens 一次,计算逆波兰表达式的值。
空间复杂度:O(n)O(n),其中 nn 是数组 \textit{tokens}tokens 的长度。使用栈存储计算过程中的数,栈内元素个数不会超过逆波兰表达式的长度。
Solution2 数组模拟栈
class Solution {
public:int evalRPN(vector<string>& tokens) {int n = tokens.size();vector<int> stk((n + 1) / 2);int index = -1;for (int i = 0; i < n; i++) {string& token = tokens[i];if (token.length() > 1 || isdigit(token[0])) {index++;stk[index] = atoi(token.c_str());} else {switch (token[0]) {case '+':index--;stk[index] += stk[index + 1];break;case '-':index--;stk[index] -= stk[index + 1];break;case '*':index--;stk[index] *= stk[index + 1];break;case '/':index--;stk[index] /= stk[index + 1];break;}}}return stk[index];}
};
171. Excel 表列序号
给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。
A -> 1
B -> 2
C -> 3
…
Z -> 26
AA -> 27
AB -> 28
…
输入: columnTitle = “A”
输出: 1
示例 2:
输入: columnTitle = “AB”
输出: 28
示例 3:
输入: columnTitle = “ZY”
输出: 701
1 <= columnTitle.length <= 7
columnTitle 仅由大写英文组成
columnTitle 在范围 [“A”, “FXSHRXW”] 内
1. There are 26 letters in our alphabet and we start counting from 1, not zero.So 'Z' is 26.
2. The rest of the combinations start from a base 26AA --> 26*1+ 1 = 27 (A == 1)
AB --> 26*1+ 2 = 28 (B == 2)
AC -->26*1 + 3 = 29 (C == 3)
.....So we can write like this:result = 0
d = s[i](char) - 'A' + 1 (we used s[i] - 'A' to convert the letter to a number like it's going to be C)
result = result* 26 + dIf the given input is only one letter, it will automatically take the value s[i] - 'A' + 1 as the first result is 0.
Some More Explanation
1. For every additional digit of the string, we multiply the value of the digit by 26^n
2. here n is the number of digits it is away from the one's place.
3. This is similar to how the number 254 could be broken down as this:(2 x 10 x 10) + (5 x 10) + (4).
4. The reason we use 26 instead of 10 is because 26 is our base.For s = "BCM" the final solution would be (2 x 26 x 26) + (3 x 26) + (13)We could do this process iteratively. Start at looking at the first digit "B". Add the int equivalent of "B" to the running sum and continue.
Every time we look at the following digit multiply our running sum by 26 before adding the next digit to signify we are changing places. Example below:"B" = 2
"BC" = (2)26 + 3
"BCM" = (2(26) + 3)26 + 13
Time Complexity : O(n) one scan of string , n is number of characters in the stringCODE WITH EXPLANATION
Solution1 26进制
155. 最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
-231 <= val <= 231 - 1
pop、top 和 getMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 104 次
I came up with this simple solution using just a single stack.
Here I am using Stack of Pair of Int. The first value of the pair would store the element of the normal stack and the second value would store the minimum up to that point in the stack.
So even if the minimum element of the stack is removed from the top, we still have a backup of the next minimum element in the pair. So for every element pushed in the stack, it stores its corresponding minimum value.For example, let's do a Dry Run of an example.["MinStack","push","push","push","push","push","getMin","pop","pop","top","push","getMin"]
[[],[5],[-2],[3],[-10],[20],[],[],[],[],[30],[]]
We push 5,-2,3,-10,20 in the stack.
If the stack is empty we push {val,val} in the stack
else we push {val,min(s.top().second,val)} which is basically minimum upto that point.
Hence {5,5},{-2,-2},{3,-2},{-10,-10},{20,-10} are pushed in the stack.
To pop simply do stack.pop()
To get the top return stack.top().first;
Now we pop 20 and -10 from the stack
The elements in the stack would be {5,5},{-2,-2},{3,-2}
On pushing 30 to the stack
The elements in the stack would be {5,5},{-2,-2},{3,-2},{30,-2}.
The Output of the code would be:[null,null,null,null,null,null,-10,null,null,3,null,-2]
All the operations are one liners expect the Push operation which is a 2 liner.class MinStack {
public:vector< pair<int,int> > s;MinStack() { }void push(int val) {if(s.empty())s.push_back({val,val});elses.push_back({val,min(s.back().second,val)}); }void pop() { s.pop_back(); }int top() { return s.back().first; }int getMin() { return s.back().second; }
};
The Time complexity of each operation is O(1)
The Space complexity is O(N)
Solution1 辅助栈
Solution2 不用辅助栈
160. 相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?
O ( 1 ) SPACE SOLUTIONFirst using constant space check for last element of both lists.
If tails of both lists are different then return NULLNow we know that intersection length will be same for both lists. So we want to make length prior to the intersection also equal.
Head pointer of the longer list is moved to next till length of both lists become equalNOW we will have intersetion point at the same distance from head for both the lists.Now keep comparing heads till match found.
Solution1 哈希集合
Solution2 双指针
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if (headA == nullptr || headB == nullptr) {return nullptr;}ListNode *pA = headA, *pB = headB;while (pA != pB) {pA = pA == nullptr ? headB : pA->next;pB = pB == nullptr ? headA : pB->next;}return pA;}
};
169. 多数元素
Approach 1: Brute Force
Intuition
We can exhaust the search space in quadratic time by checking whether each element is the majority element.Algorithm
The brute force algorithm iterates over the array, and then iterates again for each number to count its occurrences. As soon as a number is found to have appeared more than any other can possibly have appeared, return it.Complexity Analysis
Time complexity : O(n^2)The brute force algorithm contains two nested for loops that each run for nn iterations, adding up to quadratic time complexity.Space complexity : O(1)The brute force solution does not allocate additional space proportional to the input size.
Solution 1 哈希表
Solution 2 排序
Solution 3 Boyer-Moore 投票法
172. 阶乘后的零
提示 n! = n * (n - 1) * (n - 2) * … * 3 * 2 * 1
输入:n = 3
输出:0
解释:3! = 6 ,不含尾随 0
示例 2:
输入:n = 5
输出:1
解释:5! = 120 ,有一个尾随 0
示例 3:
Solution1
首先题目的意思是末尾有几个0
比如6! = 【1* 2* 3* 4* 5* 6】
其中只有25末尾才有0,所以就可以抛去其他数据 专门看2 5 以及其倍数 毕竟 4 * 25末尾也是0
比如10! = 【2456810】
其中 4能拆成22 10能拆成25
所以10! = 【2*(22)5(23)(222)(2*5)】
一个2和一个5配对 就产生一个0 所以10!末尾2个0
转头一想 2肯定比5多 所以只数5的个数就行了假若N=31 31里能凑10的5为[5, 2*5, 3*5, 4*5, 25, 6*5]
class Solution {
public:int trailingZeroes(int n) {int res=0;for(int i=n;i>0;i/=5){res+=i/5;}return res;}
};
179. 最大数
给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
输入:nums = [10,2]
输出:“210”
示例 2:
输入:nums = [3,30,34,5,9]
输出:“9534330”
1 <= nums.length <= 100
0 <= nums[i] <= 109
IntuitionTo construct the largest number, we want to ensure that the most significant digits are occupied by the largest digits.AlgorithmFirst, we convert each integer to a string. Then, we sort the array of strings.While it might be tempting to simply sort the numbers in descending order, this causes problems for sets of numbers with the same leading digit. For example, sorting the problem example in descending order would produce the number 95343039534303, while the correct answer can be achieved by transposing the 33 and the 3030. Therefore, for each pairwise comparison during the sort, we compare the numbers achieved by concatenating the pair in both orders. We can prove that this sorts into the proper order as follows:Assume that (without loss of generality), for some pair of integers aa and bb, our comparator dictates that aa should precede bb in sorted order. This means that a\frown b > b\frown aa⌢b>b⌢a (where \frown⌢ represents concatenation). For the sort to produce an incorrect ordering, there must be some cc for which bb precedes cc and cc precedes aa. This is a contradiction because a\frown b > b\frown aa⌢b>b⌢a and b\frown c > c\frown bb⌢c>c⌢b implies a\frown c > c\frown aa⌢c>c⌢a. In other words, our custom comparator preserves transitivity, so the sort is correct.Once the array is sorted, the most "signficant" number will be at the front. There is a minor edge case that comes up when the array consists of only zeroes, so if the most significant number is 00, we can simply return 00. Otherwise, we build a string out of the sorted array and return it.
Solution0 暴力
Solution1 排序
自定义一种排序方式 比较 s1 + s2 和 s2 + s1
class Solution {
public:static bool cmp(int a,int b){string sa = to_string(a);string sb = to_string(b);return sa+sb>sb+sa;}string largestNumber(vector<int>& nums) {sort(nums.begin(),nums.end(),cmp);string ret;for(auto num:nums){if(!(num==0&&ret[0]=='0')) ret+=to_string(num);}return ret;}
};
187. 重复的DNA序列
DNA序列 由一系列核苷酸组成,缩写为 ‘A’, ‘C’, ‘G’ 和 ‘T’.。
例如,“ACGAATTCCG” 是一个 DNA序列 。
在研究 DNA 时,识别 DNA 中的重复序列非常有用。
给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。
输入:s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT”
输出:[“AAAAACCCCC”,“CCCCCAAAAA”]
示例 2:
输入:s = “AAAAAAAAAAAAA”
输出:[“AAAAAAAAAA”]
0 <= s.length <= 105
s[i]==‘A’、‘C’、‘G’ or ‘T’
Simple sliding window solution.
comments added for better explanation.class Solution {
public:vector<string> findRepeatedDnaSequences(string s) {vector<string> ans;map<string,int> mmap;//storing the first 10 size substring(dna sequence) //in tempstring temp=s.substr(0,10);//adding first dna sequence to mapmmap[temp]++;//now the sliding window.for(int i=10;i<s.length();i++){//remove first character from exsisting substringtemp=temp.substr(1);//add the next character in substring.temp=temp+s[i];//add the new dna sequence to map.mmap[temp]++;//if the count of given sequence is greater than 2//and it is not present in out ans vector push it in//it//we have done the find operation to keep the elements in answer vector//unique.//for example if aa...a sequence is present 4 times, it will adding 4 times//in ans according to our sliding window logic. but we want it only one time.//therefore we check in our vector if the given dna sequence is already present or not/if(mmap[temp]>1 and find(ans.begin(),ans.end(),temp)==ans.end()){ans.push_back(temp);}}return ans;}
};
Solution1 哈希表
我们可以用一个哈希表统计 ss 所有长度为 1010 的子串的出现次数,返回所有出现次数超过 1010 的子串。
代码实现时,可以一边遍历子串一边记录答案,为了不重复记录答案,我们只统计当前出现次数为 22 的子串。
class Solution {const int L = 10;
public:vector<string> findRepeatedDnaSequences(string s) {vector<string> ans;unordered_map<string, int> cnt;int n = s.length();for (int i = 0; i <= n - L; ++i) {string sub = s.substr(i, L);if (++cnt[sub] == 2) {ans.push_back(sub);}}return ans;}
};
Solution2 哈希表 滑动窗口 二进制
class Solution {const int L = 10;unordered_map<char, int> bin = {{'A', 0}, {'C', 1}, {'G', 2}, {'T', 3}};
public:vector<string> findRepeatedDnaSequences(string s) {vector<string> ans;int n = s.length();if (n <= L) {return ans;}int x = 0;for (int i = 0; i < L - 1; ++i) {x = (x << 2) | bin[s[i]];}unordered_map<int, int> cnt;for (int i = 0; i <= n - L; ++i) {x = ((x << 2) | bin[s[i + L - 1]]) & ((1 << (L * 2)) - 1);if (++cnt[x] == 2) {ans.push_back(s.substr(i, L));}}return ans;}
};
189. 轮转数组
给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
// o(n) This solution is simply reversing the array and the reversing array from 0 to k-1 and then from k to n-1. Do a dry run by taking an example on copy and you will usndersand it. // o(n) Using extra space approach we simply store the last k elemets in same order from n-k to n-1 in a temp vector and we then pushback the reaining elements in the temp vector from index 0 to n-k-1; / o(n*k) we roatate elements of the vector one by one for k times and achieve k roatations.
Solution1 使用额外的数组
Solution2 数组翻转
class Solution {
public:void reverse(vector<int>& nums, int start, int end) {while (start < end) {swap(nums[start], nums[end]);start += 1;end -= 1;}}void rotate(vector<int>& nums, int k) {k %= nums.size();reverse(nums, 0, nums.size() - 1);reverse(nums, 0, k - 1);reverse(nums, k, nums.size() - 1);}
};
202. 快乐数
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
1.hash-setThe hash-set solution is very straightforward. For every new data, we check whether it is already in the set. If no, we insert it into the set. If yes, we detect the loop. Only when the node in the loop is "1", the number is happy number.2.hash-mapThe idea is similar as hase-set. We check the node value to check whether it is in the loop.The code is as follow. The time complexity usually is O(1) (the worst may be O(n) due to conflict)3.Floyd's Cycle detection algorithmFloyd's cycle detection algorithm is a pointer algorithm that uses only two pointers, which move through the sequence at different speeds. Obviously, if there is a loop, they will meet in the loop. It is also called the "tortoise and the hare algorithm"4.Brent's Cycle detection algorithmBrent's algorithm features a moving rabbit and a stationary, then teleporting, turtle. Both turtle and rabbit start at the top of the list. The rabbit takes one step per iteration. Every once in a while, we teleport the turtle to the rabbit's position, and let the rabbit continue moving. We start out waiting just 2 steps before teleportation, and we double that each time we move the turtle. If there is a loop, they will meet in the loop.The code is as follows. The time complexity is O(λ + μ)*. However you're doing less stepping than with Floyd's (in fact the upper bound for steps is the number you would do with Floyd's algorithm). According to Brent's research, his algorithm is 24-36% faster on average for implicit linked list algorithms.(However, it cost same time as the Floyd's in the OJ ;) )
Solution1 快慢指针
//参考英文网站热评第一。这题可以用快慢指针的思想去做,有点类似于检测是否为环形链表那道题
//如果给定的数字最后会一直循环重复,那么快的指针(值)一定会追上慢的指针(值),也就是
//两者一定会相等。如果没有循环重复,那么最后快慢指针也会相等,且都等于1。class Solution {public boolean isHappy(int n) {int fast=n;int slow=n;do{slow=squareSum(slow);fast=squareSum(fast);fast=squareSum(fast);}while(slow!=fast);if(fast==1)return true;else return false;}private int squareSum(int m){int squaresum=0;while(m!=0){squaresum+=(m%10)*(m%10);m/=10;}return squaresum;}
}
Solution2 暴力
class Solution {
public:bool isHappy(int n) {int ans=0;for(int i = 0 ; i< 100;i++){while(n>0){ans += (n%10)*(n%10);n = n /10;}n = ans;if(n==1)return true;}return false;}
};
203. 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
列表中的节点数目在范围 [0, 104] 内
1 <= Node.val <= 50
0 <= val <= 50
✔️ Solution - I (Iterative using Dummy node)A simple solution to delete the nodes having value T is to traverse over the linked list and just remove the next pointers to the node having value as T. Now, usually in deletion problem of linked list, there can be multiple cases where node to be deleted is either a head node or other node in rest of list. We usually make use of a dummy node at the start or sentinel node to avoid handling multiple edge cases and write a clean uniform solution.So, the algorithm we are using can be summarised as -Initialize a dummy/sentinel node having its next pointer pointing to the head of linked list and another node pointer prev pointing to this dummy node.
Start iterating over head of linked list
If current node's value is not equal to T, we can just move to next node without deleting current node. In this case,
We first update prev pointer and point it to current head
Then move head to next node.
Otherwise, if head -> val == T, we know that this node needs to be deleted. In this case,
We can just update the next pointer of previous node to the next pointer of current node. This will basically remove the current node from list.
Then, we update head to its next node just as in previous case.
Finally, ignore the dummy node created at start and return its next node.
Solution1 迭代
Solution2 递归
ListNode *removeElements(ListNode *head, int val)
{if (!head)return head;head->next = removeElements(head->next, val);return head->val == val ? head->next : head;
}
复杂度分析
时间复杂度:O(n)O(n),其中 nn 是链表的长度。需要遍历链表一次。
空间复杂度:O(1)O(1)。
215. 数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
This problem is well known and quite often can be found in various text books.You can take a couple of approaches to actually solve it:O(N lg K) running time + O(K) memory
Other possibility is to use a min oriented priority queue that will store the K-th largest values. The algorithm iterates over the whole input and maintains the size of priority queue.O(N) best case / O(N^2) worst case running time + O(1) memory
The smart approach for this problem is to use the selection algorithm (based on the partion method - the same one as used in quicksort).O(N) guaranteed running time + O(1) spaceSo how can we improve the above solution and make it O(N) guaranteed? The answer is quite simple, we can randomize the input, so that even when the worst case input would be provided the algorithm wouldn't be affected. So all what it is needed to be done is to shuffle the input.
Solution1 堆排序
复杂度分析
时间复杂度:O(n \log n)O(nlogn),建堆的时间代价是 O(n)O(n),删除的总代价是 O(k \log n)O(klogn),因为 k < nk<n,故渐进时间复杂为 O(n + k \log n) = O(n \log n)O(n+klogn)=O(nlogn)。
空间复杂度:O(\log n)O(logn),即递归使用栈空间的空间代价。
217. 存在重复元素
给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
输入:nums = [1,2,3,1]
输出:true
示例 2:
输入:nums = [1,2,3,4]
输出:false
示例 3:
输入:nums = [1,1,1,3,3,4,3,2,4,2]
输出:true
1 <= nums.length <= 105
-109 <= nums[i] <= 109
This problem seems trivial, so lets try different approaches to solve it:Starting from worst time complexity to the best one:Time complexity: O(N^2), memory: O(1)The naive approach would be to run a iteration for each element and see whether a duplicate value can be found: this results in O(N^2) time complexity.Time complexity: O(N lg N), memory: O(1) - not counting the memory used by sortSince it is trivial task to find duplicates in sorted array, we can sort it as the first step of the algorithm and then search for consecutive duplicates.Time complexity: O(N), memory: O(N)Finally we can used a well known data structure hash table that will help us to identify whether an element has been previously encountered in the array.This is trivial but quite nice example of space-time tradeoff.
Solution1 排序
Solution2
225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空
Solution1 两个队列
class MyStack {
public:queue<int> queue1;queue<int> queue2;/** Initialize your data structure here. */MyStack() {}/** Push element x onto stack. */void push(int x) {queue2.push(x);while (!queue1.empty()) {queue2.push(queue1.front());queue1.pop();}swap(queue1, queue2);}/** Removes the element on top of the stack and returns that element. */int pop() {int r = queue1.front();queue1.pop();return r;}/** Get the top element. */int top() {int r = queue1.front();return r;}/** Returns whether the stack is empty. */bool empty() {return queue1.empty();}
};
Solution2 单个队列
227. 基本计算器 II
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
Solution1 栈
方法一:栈
思路
由于乘除优先于加减计算,因此不妨考虑先进行所有乘除运算,并将这些乘除运算后的整数值放回原表达式的相应位置,则随后整个表达式的值,就等于一系列整数加减后的值。
基于此,我们可以用一个栈,保存这些(进行乘除运算后的)整数的值。对于加减号后的数字,将其直接压入栈中;对于乘除号后的数字,可以直接与栈顶元素计算,并替换栈顶元素为计算后的结果。
具体来说,遍历字符串 ss,并用变量 \textit{preSign}preSign 记录每个数字之前的运算符,对于第一个数字,其之前的运算符视为加号。每次遍历到数字末尾时,根据 \textit{preSign}preSign 来决定计算方式:
加号:将数字压入栈;
减号:将数字的相反数压入栈;
乘除号:计算数字与栈顶元素,并将栈顶元素替换为计算结果。
代码实现中,若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到了数字末尾。处理完该数字后,更新 \textit{preSign}preSign 为当前遍历的字符。
遍历完字符串 ss 后,将栈中元素累加,即为该字符串表达式的值。
class Solution {
public:int calculate(string s) {vector<int> stk;char preSign = '+';int num = 0;int n = s.length();for (int i = 0; i < n; ++i) {if (isdigit(s[i])) {num = num * 10 + int(s[i] - '0');}if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1) {switch (preSign) {case '+':stk.push_back(num);break;case '-':stk.push_back(-num);break;case '*':stk.back() *= num;break;default:stk.back() /= num;}preSign = s[i];num = 0;}}return accumulate(stk.begin(), stk.end(), 0);}
};
复杂度分析
时间复杂度:O(n)O(n),其中 nn 为字符串 ss 的长度。需要遍历字符串 ss 一次,计算表达式的值。
空间复杂度:O(n)O(n),其中 nn 为字符串 ss 的长度。空间复杂度主要取决于栈的空间,栈的元素个数不超过 nn。
232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
Solution1
思路
将一个栈当作输入栈,用于压入 \texttt{push}push 传入的数据;另一个栈当作输出栈,用于 \texttt{pop}pop 和 \texttt{peek}peek 操作。
每次 \texttt{pop}pop 或 \texttt{peek}peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
class MyQueue {
private:stack<int> inStack, outStack;void in2out() {while (!inStack.empty()) {outStack.push(inStack.top());inStack.pop();}}public:MyQueue() {}void push(int x) {inStack.push(x);}int pop() {if (outStack.empty()) {in2out();}int x = outStack.top();outStack.pop();return x;}int peek() {if (outStack.empty()) {in2out();}return outStack.top();}bool empty() {return inStack.empty() && outStack.empty();}
};
234. 回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
输入:head = [1,2,2,1]
输出:true
示例 2:
链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
Functions used below are HEART n SOUL of linkedlist questions. Usually, any LinkedList question can be broken down to these functions:-Reverse ===> Used for space optimization Find Mid ===> Slow-Fast Pointer Iteration : normal iter, recursive iter, adjacent node 2-vars, slow-fast Insert : start , mid, last delete : start, mid, last THIS QUESTION: find Mid of linkedlist --------> do see cases of even/odd length on paper reverse second half from mid pointer ----> see how prev gets changed Now compare first and second half Easy huh!
Leetcode Solutions - Part 2相关推荐
- Leetcode Solutions - Part 1
回溯: 字符串的排列 回溯:78. 子集 给你一个整数数组 nums ,数组中的元素 互不相同 .返回该数组所有可能的子集(幂集). 解集 不能 包含重复的子集.你可以按 任意顺序 返回解集. 示例 ...
- LeetCode Solutions : Reorder List
Given a singly linked list L: L0→L1→-→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→- You must do th ...
- LeetCode数据库题目1-123
LeetCode数据库题目1-123 175. 组合两个表 难度简单 SQL架构 表1: Person +-------------+---------+ | 列名 | 类型 | +--------- ...
- LeetCode数据库SQL题目记录(难度:简单)
难度:简单 目录 175. 组合两个表 176. 第二高的薪水 181. 超过经理收入的员工 182. 查找重复的电子邮箱 183. 从不订购的客户 196. 删除重复的电子邮箱 197. 上升的温度 ...
- LeetCode刷SQL题
https://leetcode-cn.com/problemset/database/ 题目都是leetcode 上了可以点击题目会有相应的链接 由于个人比较喜欢用开窗函数,所以都优先用了开窗 ,当 ...
- leetcode数据库题目1-123题(20-08-14)(1)
难度简单 SQL架构 表1: Person +-------------+---------+ | 列名 | 类型 | +-------------+---------+ | PersonId | i ...
- 来自北大算法课的Leetcode题解:43. 字符串相乘
代码仓库:Github | Leetcode solutions @doubleZ0108 from Peking University. 解法1(T28% S77%):第一次做大数乘法,才意识到好久 ...
- 来自北大算法课的Leetcode题解:696. 计算二进制子串
代码仓库:Github | Leetcode solutions @doubleZ0108 from Peking University. 解法1(可能超时):代码写起来还是有点绕的,当我站在一个位置 ...
- 来自北大算法课的Leetcode题解:16. 最接近的三数之和
代码仓库:Github | Leetcode solutions @doubleZ0108 from Peking University. 最直观的想法是先排序,然后一次遍历找到相邻三个数加起来跟ta ...
最新文章
- WPF ValidationRule的特点(默认目标-源才校验)
- 【bootstrap组件】几个常用的好用bs组件
- 进化计算在深度学习中的应用 | 附多篇论文解读
- leetcode 312. Burst Balloons | 312. 戳气球(暴力递归->DP)
- PHP MySQL基础知识
- intent 系统设置界面
- 戴尔服务器安装centos7修改网卡名,Centos7.6修改网卡名称为之前的eth形式
- 《Android 第1行代码》读后感—第12章【最佳的UI体验——Material Design实战】
- Git安装配置与GitHub注册及简单使用
- linux安装vmware没有网络,关于无桌面的linux安装VMWare Tools配置的教程
- html左侧浮动广告代码,网站侧边栏广告固定浮动效果的实现
- 腾讯云 obs 推流一直断开连接无法进行直播
- 将Ubuntu的引导写入自己所在分区——变色龙引导Linux,Windows,Mac OS(苹果系统)攻略之一
- Android官方模拟器安装
- filtic函数 matlab_matlab求相位函数
- 【TF-Slim使用】
- Effective C++改善程序与设计的55个具体的做法
- 携程mysql架构_携程数据库高可用架构实践
- 苹果新掌门库克:两次临危受命的实干家
- 全球与中国糖粉市场深度研究分析报告
热门文章