LeetCode - 1 双指针+读写优化
耗时
runtime 4 ms, beats 99.95% of cpp submissions.
题意
给定一个有序数组 nums 和一个目标值 target ,在数组 nums 中找出两个不同位置的数 a 和b,使得 a + b == target,返回a 和 b 在数组 nums 中的下标。
题解
首先用pair 将数组中的值和下标组合在一起,放在 vector 中,对vector进行排序。然后开两个指针,分别指向数组头部和尾部。相加两个指针所指元素并和 target 进行比较,如果大于 target,说明两数之和过大,将尾部指针前移,反之将首部指针后移。
特别要说明的是,使用下面这段代码,可以大大提升 cin,cout 的读写速度。
static auto z = [](){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
return 0;
}();
std::ios::sync_with_stdio(false);
是关闭C 和 C++标准IO流的同步。也就是说默认情况下,允许 C 和 C++标准IO流的混用,共用缓冲区。关闭后就使用各自缓冲区,可以加快程序速度,但是此时混用两种风格输入输出是很危险的。
默认情况下,cin 和 cout 是绑定,在 cin 获取用户输入内容时,会先刷新 cout 缓冲区将内容输出,再阻塞程序获取输入。而std::cin.tie(0);
则是解除 cin 和 cout 的绑定,能加快程序执行,但副作用是会导致 cin 先于 cout 执行,比如程序可以已经开始阻塞来接收用户输入,但是“请输入”提示符还没打印出来。
由于此时我们此时的代码主要用于解题,而不是实际生产,所以一般情况下使用这两个语句没有问题,还能够加快程序运行速度。
代码
static auto z = [](){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
return 0;
}();
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<pair<int,int>>v;
int len = nums.size();
for(int i=0;i<len;++i){
v.push_back(pair<int,int>(nums[i],i));
}
sort(v.begin(),v.end());
int i = 0,j = len - 1;
vector<int>ans;
while(i < j){
if(v[i].first + v[j].first == target){
ans.push_back(v[i].second);
ans.push_back(v[j].second);
break;
}
else if(v[i].first + v[j].first < target){
i++;
}
else{
j--;
}
}
return ans;
}
};
LeetCode - 2 链表模拟加法
耗时
runtime 16 ms, beats 100% of cpp submissions.
题意
给两个非空链表,链表每个节点代表一个数字,每个链表倒叙表示一个十进制的数,将两链表按十进制进行相加后返回一个新的链表。
题解
模拟加法算式运算。
需要注意的地方主要是两个点:
- 两个链表长度不一致。
- 进位,尤其是链表末尾的进位,需要新建节点。
最后就是操作上的一些小 trick,用得好的话,可以节省不少运行时间,具体的可以直接看代码。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
static const auto io_sync_off = []()
{
// turn off sync
std::ios::sync_with_stdio(false);
// untie in/out streams
std::cin.tie(nullptr);
return nullptr;
}();
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2){
ListNode* tmp = new ListNode(0);
int carrybit = 0;
ListNode* ans = tmp;
while(l1 && l2){
tmp = tmp -> next = new ListNode(0);
tmp -> val = l1 -> val + l2 -> val + carrybit;
carrybit = (tmp -> val) / 10;
tmp -> val %= 10;
l1 = l1 -> next;
l2 = l2 -> next;
}
if(l1){
tmp -> next = l1;
}
else{
tmp -> next = l2;
}
while(carrybit){
if(tmp -> next == NULL){
tmp -> next = new ListNode(0);
}
tmp = tmp -> next;
tmp -> val ++;
carrybit = (tmp -> val) / 10;
tmp -> val %= 10;
}
return ans -> next;
}
};
LeetCode - 3 滑动窗口
Longest Substring Without Repeating Characters
耗时
runtime 20ms, beats 79.98% of cpp submissions.
题意
给一个字符串,求一个最长子串的长度,该子串需要满足其中所有元素都不相同。
题解
遍历字符串,开一个hash_map记录元素在当前位置之前,在字符串中出现的最后位置。
开一个指针 pre 记录使[pre,i]子串满足条件的 pre 的最小位置。
代码
class Solution {
public:
int lengthOfLongestSubstring(string s){
unordered_map<char,int>hash_map;
int mx = 0;
int pre = -1;
size_t len = s.size();
for(int i=0;i<len;++i){
if(hash_map.find(s[i]) != hash_map.end() && pre < hash_map[s[i]]){
pre = hash_map[s[i]];
}
else{
mx = max(mx,i - pre);
}
hash_map[s[i]] = i;
}
return mx;
}
};
LeetCode - 4 二分查找
耗时
runtime 20ms, beats 98.46% of cpp submissions.
题意
给定两个已排序的数组,其长度分别为 m 和n,找出两个数组中的中间数,时间复杂度为 log(m+n)。
题解
首先时间复杂度是 log(m+n),就很容易想到构造一个能在两个数组之间进行二分查找的函数。
然后需要分两种情况:
- m+n 为偶数,这种中间数就是两个数组中的两个元素的平均值。这两个元素的位置又有四种组合,分别求出来进行判断,找出最终解。
- m+n 为奇数,这种中间数就是两个数组的某一个元素。这个元素的位置只有两种情况,分别求出判断,得出答案。
这题的难点就在于构造跨两个数组的二分查找了,细节需要考虑仔细才能一次 AC。
我的这个解法虽然速度上还可以,但是写法过于繁杂。leetcode 官方题解有一些非常简洁的写法,很值得学习。姿势水平还不够高,继续努力。
代码
static auto z = [](){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
return 0;
}();
class Solution {
public:
int solve(vector<int>& num, int len, int val, int need_l, int need_r){
int res = 0;
if(need_l < 0){
res = 1;
}
else if(need_r < 0){
res = -1;
}
else if(need_l > 0 && num[need_l-1] > val){
res = -1;//too small
}
else if(need_l < len && num[need_l] < val){
res = 1;//too big
}
else if(need_r > 0 && num[len - need_r] < val){
res = 1;
}
else if(need_r < len && num[len - 1 - need_r] > val){
res = -1;
}
return res;
}
int binarySearch(vector<int>& nums1, int len1, vector<int>& nums2, int len2, int total_l, int total_r){
int l = 0, r = len1, res = len1;
while(l < r){
// cout << l << ' ' << r << endl;
int mid = (l + r) >> 1;
int tmp = solve(nums2, len2, nums1[mid], total_l - mid, total_r - (len1 - 1 - mid) );
if(tmp == 0){
res = mid;
break;
}
else if(tmp == 1){
r = mid;
}
else{
l = mid + 1;
}
}
return res;
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1 = nums1.size();
int len2 = nums2.size();
int total = (len1 + len2 - 1) >> 1;
if((len1 + len2) % 2 == 0){
int a1 = binarySearch(nums1, len1, nums2, len2, total, total+1);
// cout << a1 << endl;
int a2 = binarySearch(nums1, len1, nums2, len2, total+1, total);
// cout << a2 << endl;
int b1 = binarySearch(nums2, len2, nums1, len1, total, total+1);
// cout << b1 << endl;
int b2 = binarySearch(nums2, len2, nums1, len1, total+1, total);
// cout << b2 << endl;
if(a1 < len1 && a2 < len1){
return (nums1[a1] + nums1[a2]) / 2.0;
}
else if(a1 < len1 && b2 < len2){
return (nums1[a1] + nums2[b2]) / 2.0;
}
else if(b1 < len2 && b2 < len2){
return (nums2[b1] + nums2[b2]) / 2.0;
}
else if(a2 < len1 && b1 < len2){
return (nums1[a2] + nums2[b1]) / 2.0;
}
}
else{
int a = binarySearch(nums1,len1,nums2,len2,total,total);
// cout << a << endl;
if(a < len1){
return nums1[a];
}
int b = binarySearch(nums2,len2,nums1,len1,total,total);
// cout << b << endl;
if(b < len2){
return nums2[b];
}
}
return 0;
}
};
LeetCode - 5 Manachar
耗时
runtime 4ms, beats %99.73 of cpp submissions.
题意
求字符串中的最长回文子串。
题解
可以参考我之前的文章,上面有对求最长回文串的详细讲解。
《求最长回文串的几种做法》
代码
static auto x = [](){
// turn off sync
std::ios::sync_with_stdio(false);
// untie in/out streams
std::cin.tie(NULL);
return 0;
}();
class Solution {
public:
string longestPalindrome(string s) {
if(s.length()==0) return "";
string strTemp{"$#"};
for(int i=0;i<s.length();++i){
strTemp+=s[i];
strTemp+='#';
}
vector<int> nTemp(strTemp.size(),1);
int TempMaxIndex=0,TempMaxMid=0,ansCount=0,ansIndex=0;
for(int i=0;i<strTemp.size();++i){
if(i<TempMaxIndex)
nTemp[i]=min(nTemp[2*TempMaxMid-i],TempMaxIndex-i);
while(strTemp[i-nTemp[i]]==strTemp[i+nTemp[i]])
nTemp[i]++;
if(nTemp[i]+i>TempMaxIndex)
{
TempMaxIndex=nTemp[i]+i;
TempMaxMid=i;
}
if(nTemp[i]>ansCount){
ansCount=nTemp[i];
ansIndex=i;
}
}
return s.substr((ansIndex-ansCount)/2,ansCount-1);
}
};