LeetCode刷题-35.搜索插入位置
题目链接:35.搜索插入位置
¶题解:
同样是二分!
¶题目简述:
给一个有序数组,查找目标值的位置,若数组中存在,返回该下标,否则返回目标值应该插入的位置!
¶题解:
继续二分:
找到二分条件:x >= target,右端满足,左端不满足!
如果最终该目标值越界,即nums[r] < target,直接返回r + 1或nums.size()。
会发现:其实直接使用右端点r = nums.size()二分即可!最后也可以不用判断是否越界!
¶AC代码:
123456789101112131415161718192021222324252627class Solution {public: int searchInsert(vector<int>& nums, int target) { int l = 0, r = nums.size() - 1; while(l < r){ int mid = l + r >> 1; if(nums[mid] > ...
LeetCode刷题-34.在排序数组中查找元素的第一个和最后一个位置
题目链接:34.在排序数组中查找元素的第一个和最后一个位置
¶题解:
同样是二分!
¶题目简述:
给定一个有序数组,找到目标值出现的开始和结束位置!
¶题解:
同样是二分:目的在于找到两个端点,如下图:
使用 x >= target可以找到满足该条件的最后一个数,即左端点!
使用x <= target可以找到满足该条件的最后一个数, 即右端点!
具体情况:
若数组为空,直接返回-1
若左端点不存在,即不存在target直接返回-1
否则,重新二分该数组找到右端点,最后返回。
注意: 二分左端点时,r的选取可以是nums.size() - 1,也可以是上次二分的结果。复杂度不影响!
¶AC代码:
123456789101112131415161718192021class Solution {public: vector<int> searchRange(vector<int>& nums, int target) { if(nums.empty()) return {-1, -1}; int l ...
LeetCode刷题-33.搜索旋转排序数组
题目链接:33.搜索旋转排序数组
¶题解:
开始进入二分的时代,二分模板要掌握并理解。
¶题目简述:
一个升序序列在某一点反转形成两个升序序列,从其中找到一个目标值,不存在返回 -1,要求时间复杂度为O(log n)级别。
¶题解:
自然而然的想到要使用二分了!
二分的二段性:一段满足,另一段不满足,则可以将分界点二分出来!
二分可以找到满足条件的最后一个数!最后(指的是按照区间缩小的趋势最后满足条件的数)
二分模板:点击这里!
注意:关于mid加不加一的问题,若 l= mid就得加,否则只有两数会造成死循环;若r = mid就不能加,否则只有两数也会造成死循环。
‘
本题分为了两段,根据二分的二段性,当 x >= nums[0]的时候,会发现左边一段都符合条件,右边一段都不符合条件,可以使用二分,二分到分界点。
如下图:
找到分界点后,判断当前目标值在哪一段,继续进行二分即可!
0 ~ mid
mid + 1 ~ nums.size() - 1
¶AC代码:
1234567891011121314151617181920212223class Solution ...
数据库教程之事务及隔离
¶首先来首歌曲来放松一下吧!
¶一、事务
把多条语句作为一个整体进行操作的功能,被称为数据库事务。
数据库事务可以确保该事务范围内的所有操作都可以全部成功或者全部失败。如果事务失败,那么效果就和没有执行这些SQL一样,不会对数据库数据有任何改动。
与多条语句分别执行而不使用事务的区别:
不使用事务,若某一句执行出错,则执行成功的语句将会生效
使用事务,若某一句执行出错,当我们关掉窗口再次打开则会自动触发回滚操作ROLLBACK
事务(transaction):一组SQL语句
回退(rollback):撤销指定SQL语句
提交(commit):指将未存储的SQL语句结果写入数据库表
保留点(savepoint):指事务处理中设置的临时占位符,你可以对它发布回退(与回退整个事务处理不同)
¶1、数据库事务的ACID四个特性
A:Atomic,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
C:Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
I:Isolation,隔离 ...
LeetCode刷题-32.最长有效括号
题目链接:32.最长有效括号
¶题解:
又是动态规划的题,需要多加理解与按步骤走;同时使用栈也可以巧妙的解决!
¶题目简述:
给定一个包含左右括号的字符串,需要求出最长的有效匹配括号的子串的长度!
¶题解一:使用栈
首先,先回忆一下括号匹配的两个条件?
上次的括号问题有提到的,点击这里!
左右括号相等
任意前缀左括号数量大于等于右括号数量
接下来,我们可以将该括号序列分为若干段:
从前向后找,找到右括号大于左括号的位置,此时就是一段合法区间
从所有合法区间中找到一个最大匹配有效长度即可
参考下面的图:
具体操作:
使用start标记可匹配区间的上一个元素,即每次出现的右大于左的位置!
遇到左括号直接进栈
遇到右括号:
若栈不空,说明没走到右大于左的位置,此时栈顶元素与之对应的左括号出栈
如果出栈后栈空,说明从起点start到当前位置i是一组有效括号序列,更新最大值
如果出栈后不空,说明栈顶到当前位置i是一组有效括号序列,更新最大值
若栈为空,说明已经走到右大于左的位置,此时更新start为当前位置i
¶AC代码一:
123456789101112131 ...
LeetCode刷题-31.下一个排列
题目链接:31.下一个排列
¶题解:
手动实现全排列函数,有点巧妙!
¶题目简述:
给定一个序列,计算出按照字典序的下一组更大的排列!
¶题解一:
直接调用algorithm头文件里的next_permutation即可!
当然,本题意在让你自己实现,而不是调用库函数,题解二将进行手动实现。
¶AC代码一:
123456class Solution {public: void nextPermutation(vector<int>& nums) { next_permutation(nums.begin(), nums.end()); }};
¶题解二:
思路:
从后向前找一个降序序列,该序列的第一个元素为最大值,找到当前序列的上一个元素,即非降序位置。
从后面的降序序列找一个比当前非降序位置值大的最小元素,交换二者位置。
将降序序列倒序
简图如下:
我的简单理解与解释:
后面是一个降序序列,要想找到下一个字典序,必须找到降序的上一个非降序的位置,因为降序序列的位置是不能动的,该降序序列已经到了字典序的最大值,要动也是前一个位 ...
LeetCode刷题-30.串联所有单词的子串
题目链接:30.串联所有单词的子串
¶题解:
嗯,有点难度,使用哈希表和分类的思想!
¶题目简述:
给定一个字符串,一个字符串数组,从字符串中找出可以包含字符串数组左右元素的起始位置!
¶题解:
暴力:不推荐
这里直接采用高效率的算法:
预先规定:
n = s.size(), m = words.size(), w = words[0].size();
使用分类思想:
本题可以划分为w类,如下图所示:
可以发现,可以把起点分为0 - w-1的w组,你会惊奇的发现,起点从w-1往后,都已经被前面的情况所包括了!
而且,我们扫描时按单词的长度w往后走,即每个单词一定落在坑里!
具体做法:
使用一个哈希表tot记录words数组中单词出现的数量,使用另个一个哈希表wd 动态 记录选定区间的单词数量。
当两个哈希表单词和数量对应相等时即匹配了一组,继续后续匹配。
怎样处理两个哈希表是否一致,暴力,复杂度太高,所以使用一个 动态 变量cnt,用来统计两个容器对应的个数!
当cnt == m时,即为找到了一组合法区间。
如何动态维护wd哈希表?
如果当前处理个数不到m,即j < ...
LeetCode刷题-29.两数相除
题目链接:29.两数相除
¶题解:
嗯,倍增思想的应用!
¶题目简述:
给定两个int范围的数相除,要求不使用乘除法和取余计算得到结果!
¶题解一:暴力
不能使用乘除就使用减法,一直减去除数直到减到负数即可!
当然这样会超时,例如输入:2147483647 和 1 ,此时的数量级是10的9次方的,妥妥超时!
超出int范围,统一返回INT_MAX
正确做法:请看题解二,使用倍增思想!
¶TLE代码:
12345678910111213class Solution {public: int divide(int dividend, int divisor) { long long res = 0, k = 1; if((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) k = -1; long long dividend1 = abs(dividend), divisor1 = abs(divisor) ...
LeetCode刷题-28.实现strStr()
题目链接:28.实现strStr()
¶题解:
嗯,使用暴力就是简单题!
提高效率使用KMP,复杂了,好好理解,多实现,最好记住模板!本题就是一个模板。
¶题目简述:
给定两个字符串,从第一个中找第二个串,能找到返回起始下标,找不到返回-1,第二个串为空,直接返回0。
¶题解一:暴力
直接扫描一遍原串即可:
先判断当前位置是否够子串的长度,不够直接返回-1,因为肯定没有可以匹配的。
如果位置长度够,就从当前位置截取和子串长度相同,查看是否相等,相等直接返回当前下标i,否则继续下次循环
时间复杂度:substr(pos, m):复杂度为O(len),总时间复杂度为O(n*m)
¶AC代码一:
123456789101112131415161718192021222324class Solution {public: int strStr(string haystack, string needle) { if(needle.empty()) return 0; for(int i = 0; i < haystack.size(); i+ ...
LeetCode刷题-27.移除元素
题目链接:27.移除元素
¶题解:
简单题,两行代码!
¶题目简述:
去除数组中值等于val的数,并返回去掉后的长度。
¶题解:
同样使用双指针,一个指针i指向数组当前扫描位置,一个指针k指向当前新数组的下一个元素,遇到和val值相等的,直接跳过即可!
¶AC代码:
123456789class Solution {public: int removeElement(vector<int>& nums, int val) { int k = 0; for(int i = 0; i < nums.size(); i++) if (nums[i] != val) nums[k++] = nums[i]; return k; }};
document.querySelectorAll('.github-emoji')
.forEach(el => {
if (!el.dataset.src) { return; }
...
LeetCode刷题-26.删除排序数组中的重复项
题目链接:26.删除排序数组中的重复项
¶题解:
嗯,简单题。
¶题目简述:
删除有序数组长度重复项,返回不重复元素的长度!
¶题解:
用两个指针一个指针t指向不重复元素的末尾,一个指针i指向当前扫描的位置,如果当前扫描位置和不重复元素的末尾相同,则继续后移,找到不一样的插到不重复元素的下一个位置。
最后返回长度,此处t指向下标,长度为t + 1.
注意:这样写需要特判nums为空的情况!
¶AC代码:
12345678910111213141516171819202122232425class Solution {public: int removeDuplicates(vector<int>& nums) { if(nums.size() == 0) return 0; int t = 0; for(int i = 0; i < nums.size();){ while(i < nums.size() && nums[i] == nums[t]) i++; ...
LeetCode刷题-25.K个一组翻转链表
题目链接:25.K个一组翻转链表
¶题解:
两两一组升级为 k 个一组!
参考上一篇两两一组的解法,基本一致!
¶题目简述:
给定一个链表,k个一组进行倒序反转!
¶题解一:我的 --> 乱
嗯,不推荐看这个,虽然我自己写的也AC了,但是着实有点乱,有点多,不条理。非常建议直接看题解二:更加清晰!
接下来介绍一下我的乱乱的思路:
特判一下k为1的情况,直接返回即可
先通过pt指针循环找到第k个节点。用b指针指向第k个节点。用a指针指向第一个节点。(如果存在)
如果不够第k个节点,直接返回。
用t指向第二个节点, p指向第k个节点b
使用循环将第二个节点到第k个节点全部反向指一下。
使用s指针指向第k个节点到第二个节点。
使用b来反向连接
t->next = a:第二个指向第一个
p = a:p指向a
。。。。其实画个图还是很明白的,我懒得画了,毕竟有点乱!
强烈建议看题解二!
¶AC代码一:(复杂,不调理,易错)
123456789101112131415161718192021222324252627282930313233 ...