双指针篇
简单
验证回文串
class Solution {
public:
bool isPalindrome(string s) {
int left = 0;
int right = s.size() - 1;
while (left <= right) {
while (!isalnum(s[left]) && left<right) {
left++;
}
while (!isalnum(s[right]) && left<right) {
right--;
}
if (s[left] != s[right] && toupper(s[left]) != toupper(s[right])) {
return false;
}
left++;
right--;
}
return true;
}
};
成绩:86.71 64.39
思路:
采用双指针
left指针从左向右遍历,right指针从右向左遍历,当left大于right时跳出循环
当left所指字符非字母或数字时,往后遍历,直到找到字母或数字;
right同理,不同的地方只有往前遍历;
当left和right均找到字母或数字时,对两者进行比较,若符合要求,继续遍历;反之,跳出并返回false。
只出现一次的数字
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i : nums) {
res = res ^ i;
}
return res;
}
};
成绩:94.17 69.79
思路:
这题吧,想到用异或就很简单,想不到异或感觉用哈希表也不难。
能想到异或主要是看了提示:位运算 数组。
反正按照我一开始的思路,遍历数组所有元素,用哈希表存储并统计(数组元素作为键,个数作为值),最后找到值为1的键输出即可。
还有一种就是用位运算的异或:两个位相同为0,相异为1;这意味着什么,意味着异或能够消去两个相同的数字。那和这题不就对上了么。除了需要得到的只出现一次的数字,其余出现两次的数字都会被消去。
环形链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head) return false;
ListNode *fast = head;
ListNode *slow = head;
while(fast->next && fast->next->next){
if(fast->next->next == slow->next){
return true;
}
else{
fast=fast->next->next;
slow=slow->next;
}
}
return false;
}
};
成绩:94 28
思路:
想象一下你们学校开运动会,当碰到3000米这种项目时,是不是就会有套圈的现象出现。究其原因时套圈的跑得快,被套圈的跑得慢。这题就用了这样的思想。
设置快慢两个指针。快指针一次走两个节点,慢指针一次走一个节点。
如果该链表没有环,那么快指针会走到头。
如果有环,那么快指针和慢指针都会进入环(跑道),在跑几次后,慢指针会被快指针追上,此时就能说明该链表有环。
至于为什么要选取走一个节点和走两个节点,因为比较简单。其他选取方案可能会更快,到时候想想。
相交链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int getLength(ListNode *head){
int length = 0;
while(head){
length++;
head=head->next;
}
return length;
}
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lengthA = getLength(headA);
int lengthB = getLength(headB);
int distract = abs(lengthA-lengthB);
if(lengthA>lengthB){
while(distract--){
headA = headA->next;
}
}
else{
while(distract--){
headB = headB->next;
}
}
while(headA && headA!=headB){
headA = headA->next;
headB = headB->next;
}
return headA;
}
};
成绩:58 95
思路:
这个题就很难受,也没用到啥技巧,就是经过一遍遍历,统计两条链表的长度,长的那条先走差值个节点,使两条链表的头节点在统一逻辑位置,然后往后遍历就可以。
当两个头节点遍历到同一节点(说明有交叉)或遍历到结尾(说明无节点)时,就返回其中一个头节点即可。
明天看看官方题解。
两数之和 II - 输入有序数组
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int left = 0;
int right = numbers.size()-1;
while(left<right){
if(numbers[left]+numbers[right]<target){
left++;
}
else if(numbers[left]+numbers[right]>target){
right--;
}
else{
return {left+1,right+1};
}
}
return {};
}
};
成绩:
思路:
按照leetcode第一题那么做肯定也可以,但是没有用到非降数组的性质。
一左一右两个指针;
若当前所指两数大于target,右指针左移;
若当前所指两数小于target,左指针右移。
直至找到(题中所示为必定找到)和为target的两数。
回文链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
int count = 0;
stack<int> stack;
ListNode* cur = head;
while(head){ //统计总节点数
count++;
head = head->next;
}
int half = count/2;
while(half--){ //前半进栈
stack.push(cur->val);
cur = cur->next;
}
if(count%2 == 1) cur = cur->next; //奇数位就跳过中间位
while(cur){
if(cur->val != stack.top()) return false;
else{
stack.pop();
cur = cur->next;
}
}
return true;
}
};
成绩:43.48 43.82
思路:
把前一半节点先进栈
若为奇数个节点,则跳过中间节点
对后半节点进行遍历,若当前节点和栈顶元素不相等,则说明非回文链表,返回false;
若遍历完所有节点,说明为回文链表,返回true。
上述方法是用栈做的,但是毕竟最近在集中做双指针的题,所以用双指针再做一遍
移动零
leetcode直达
思路一:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int right = 0;
int left = 0;
while(right<nums.size()){
if(nums[right]){
nums[left++] = nums[right];
}
right++;
}
while(left<nums.size()){
nums[left++] = 0;
}
}
};
成绩:43 85.8
思路:
快慢指针:慢指针指向已完成排列字段末尾(即待插入的位置),快指针指向下一个非零元素(即需要往前插入的元素)。
当快指针便利到末尾时,说明往前插值已经完成了。开始移动慢指针,将最后元素均变为零。
思路二:
思路:不同于思路一的往前插值,思路二直接将非零元素和零元素互换(其实就是将快指针指向的元素直接赋0),这样能省去最后换0的过程,能快一些。
反转字符串
class Solution {
public:
void reverseString(vector<char>& s) {
int left = 0;
int right = s.size()-1;
while(left<right){
swap(s[left++], s[right--]);
}
}
};
成绩:87.4 18.4
思路:
这个就很简单,左右指针换就行,这么简单的题放这么后面。
反转字符串中的元音字母
class Solution {
public:
bool judge(char s){
string w = "aeiouAEIOU";
if(w.find(s) != string::npos) return true; //找到了,说明是元音
else return false;
}
string reverseVowels(string s) {
int left = 0;
int right = s.length()-1;
while(left<right){
if(judge(s[left]) && judge(s[right])){
swap(s[left++], s[right--]);
}
else if(!judge(s[left])) left++;
else if(!judge(s[right])) right--;
}
return s;
}
};
成绩:5.8 100
思路:
和上一题夜没差,无非多了个找元音字母的步骤。
这里想讲讲的是判断元音的judge函数中的下述代码
string w = "aeiouAEIOU";
if(w.find(s) != string::npos) return true; //找到了,说明是元音
else return false;
这里面的string::npos指的是string的结束位置。和find一起用的作用就是检查给定字符s是否在给定字符串w里。
类似于python里的in操作。
两个数组的交集
思路一
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> hash;
for(int num : nums1){
hash.insert(num);
}
vector<int> res;
for(int num : nums2){
if(hash.count(num) > 0){
if(!count(res.begin(),res.end(),num)){
res.push_back(num);
}
}
}
return res;
}
};
成绩:89.69 80.58
思路:
拿哈希表做就很简单,把其中一个数组存入哈希表,然后遍历另一数组,检查当前元素是否在哈希表中。若在,则说明为相交元素,存入res数组;反之,则说明非相交数组,继续遍历。
有一点要说明,从实例中可以看出,相同相交元素只存一个,所以在存入res数组前还需要检查是否已存在。
思路二:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int key1 = 0;
int key2 = 0;
vector<int> res;
while(key1<nums1.size()&&key2<nums2.size()){
if(nums1[key1] == nums2[key2]){
if(res.size() == 0 || nums1[key1] != res.back()){
res.push_back(nums1[key1]);
}
key1++;
key2++;
}
else if(nums1[key1] > nums2[key2]){
key2++;
}
else if(nums1[key1] < nums2[key2]){
key1++;
}
}
return res;
}
};
成绩:51.24 89.41
思路:
双指针,key1指nums1,key2指nums2;
先对两数组进行排序;
若当前所指两数相同且为存入res,则存入res;
若不相同,所指数较小的指针后移。
两个数组的交集
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int key1 = 0;
int key2 = 0;
vector<int> res;
while(key1<nums1.size()&&key2<nums2.size()){
if(nums1[key1] == nums2[key2]){
res.push_back(nums1[key1]);
key1++;
key2++;
}
else if(nums1[key1] > nums2[key2]){
key2++;
}
else if(nums1[key1] < nums2[key2]){
key1++;
}
}
return res;
}
};
成绩:88.27 95.41
思路:
这题用先排序再双指针遍历的办法会好做很多,因为同时遍历的情况下,能够被对应上的相交元素一定是按照数量较少的数组来对应的,超出的部分是对应不上的。
判断子序列
class Solution {
public:
bool isSubsequence(string s, string t) {
int keys = 0;
int keyt = 0;
int slen = s.size();
int tlen = t.size();
while(keyt < tlen){
if(s[keys] == t[keyt]){
keys++;
}
keyt++;
}
return keys == slen;
}
};
成绩:100 90.57
思路:
双指针keys,keyt
对应比较,若两指针所指的字符相同,则一起向后遍历;
反之,则向后遍历主串。
当主串遍历完时,若模式串指针指向模式串末,则说明为子序列;反之,则非子序列。
反转字符串Ⅱ
class Solution {
public:
string reverseStr(string s, int k) {
int n = s.length();
for(int slow = 0;slow<n;slow+=2*k){
reverse(s.begin()+slow, s.begin() + min(slow+k, n));
}
return s;
}
};
成绩:9 42.19
思路:
主要是正确定位需要反转字符串的首末位置,其中末位置又因字符长度会有不同(不同情况题里也写了)。其他的就不多说了。
反转字符中的单词Ⅲ
class Solution {
public:
string reverseWords(string s) {
int n = s.length();
int slow = 0;
int fast = 0;
while(fast<n){
while(s[fast] != ' ' && fast < n) fast++;
reverse(s.begin()+slow, s.begin()+fast);
fast++;
slow = fast;
}
return s;
}
};
成绩:70.80 33
思路:快慢指针,抓住单词之间均用空格隔开的特点即可。
慢指针指单词头,快指针找单词结尾后的空格,两指针之间的即为需要反转的单词。
反转其中一个单词后,快指针+1即为下一指针头,慢指针指向快指针位置。重复即可。
验证回文字符串Ⅱ
class Solution {
public:
bool judge(string s, int left, int right){
while(left<right){
if(s[left] != s[right]){
return false;
}
left++;
right--;
}
return 1;
}
bool validPalindrome(string s) {
int left = 0;
int right = s.length()-1;
while(left < right){
if(s[left] != s[right]) break;
left++;
right--;
}
return judge(s, left+1, right) || judge(s, left, right-1);
}
};
成绩:94.81 8.69
思路:
核心步骤就是跳过第一个不匹配的字符,然后就和最简单的判断回文串步骤一样了;
那么问题就在于怎么跳过第一个不匹配的字符,有两种跳过的情况:
1、多余字符在左半边,则左指针往右跳;
eg.aebcba
2、多余字符在右半边,则右指针往左跳。
跳完这一次之后,若剩余字符串为回文串,则符合题意;反之则说明跳一次不够,不符合题意。
字符的最短距离
class Solution {
public:
vector<int> shortestToChar(string s, char c) {
int len = s.length();
vector<int> res;
for(int i = 0;i<len;i++){
int left = i;
int right = i;
while(s[left]!=c && s[right]!=c){
if(left>0) left--;
if(right<len) right++;
}
res.push_back(max(abs(i-left),abs(i-right)));
}
return res;
}
};
成绩:100 21
思路:
遍历每一个字符,对当前字符设左右双指针,返回较近c和当前字符距离。
有一点需要注意,可能左指针或右指针在向左向右遍历时,会遍历到字符头和尾,此时就需要处理距离异常的问题。通过下面这行代码就能确保得到最短且正确(某一指针遍历到边界但另一指针仍在找c这一情况)的距离:
res.push_back(max(abs(i-left),abs(i-right)));
翻转图像
class Solution {
public:
void reserve(vector<int>& vec){
int left = 0;
int right = vec.size()-1;
while(left<right) swap(vec[left++],vec[right--]);
}
vector<vector<int>> flipAndInvertImage(vector<vector<int>>& image) {
int len = image.size();
for(int i=0;i<len;i++){
reserve(image[i]);
for(int j=0;j<len;j++){
image[i][j] ^= 1;
}
}
return image;
}
};
成绩:88 40
思路:
这题就没啥可说的,就是模拟,题目有啥要求直接实现就完事儿了。
唯一一点要说的就是“0” 和“1”的替换可以用^(异或)1来完成。
比较含退格的字符串
class Solution {
public:
bool backspaceCompare(string s, string t) {
stack<int> res1;
stack<int> res2;
int scur = 0;
int slen = s.length();
int tcur = 0;
int tlen = t.length();
while(scur<slen){
if(!res1.empty() && s[scur] == '#'){
res1.pop();
}
else if(s[scur] != '#') res1.push(s[scur]);
scur++;
}
while(tcur<tlen){
if(!res2.empty() && t[tcur] == '#'){
res2.pop();
}
else if(t[tcur] != '#') res2.push(t[tcur]);
tcur++;
}
return res1 == res2;
}
};
成绩:100 15
思路:
两个栈,遇到’#‘且栈非空就pop,非’#'就push。
最后比较两个栈即可。
链表的中间节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* slow;
ListNode* fast;
slow = fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
成绩:34.46 30.58
思路:
就挺简单的,快慢指针,慢指针一次走一个节点,快指针一次走一个节点。
对于奇数个节点来说,判断条件是一样的,即
while(!fast->next) break;
但如果是偶数个节点,应该有两个中间节点,取第一个和取第二个的判断条件是不一样的;
//取第一个:
while(!fast->next->next) break;
//取第二个
while(!fast->next) break;
按奇偶排列数组
class Solution {
public:
vector<int> sortArrayByParity(vector<int>& nums) {
int slow = 0;
int fast = 0;
int numslen = nums.size();
while(fast<numslen){
if(nums[fast]%2 == 0){
swap(nums[slow], nums[fast]);
slow++;
}
fast++;
}
return nums;
}
};
成绩:83.62 49.24
思路:
就是我感觉,我做这类需要按条件前后换的题目,总是下意识的会有这种想法:左指针(慢指针)找到符合条件的那个节点;接着右指针(快指针)找到符合条件的节点;然后两指针所指节点互换。乍一看好像没毛病,但是仔细一琢磨肯定是错了。
这类题,像这题和移动零,其实两题是一个道理,无非前者是换奇偶,后者是换零一。那其实我们不需要去管指针符合什么条件才能换,而是关注应该把谁换上来。比如这题就是把偶数换上来,不需要去管左指针(慢指针)指向的是奇数还是偶数,只需要确保右指针(快指针)当前所指的是偶数即可。这样能达到一种什么效果:左指针左边都是偶数,没有奇数;左指针到右指针之间都是奇数。这就是我们想要的效果。
仅仅反转字母
class Solution {
public:
string reverseOnlyLetters(string s) {
int left = 0;
int right = s.length()-1;
while(left<right){
if(isalpha(s[left]) && isalpha(s[right])){
swap(s[left++], s[right--]);
}
else if(!isalpha(s[left])) left++;
else if(!isalpha(s[right])) right--;
}
return s;
}
};
成绩:100 72.92
思路:很简单,不说了
按奇偶排序数组
class Solution {
public:
vector<int> sortArrayByParityII(vector<int>& nums) {
int slow = 0;
int fast = 0;
int len = nums.size();
while(fast < len){
if(slow%2 == 0){
if(nums[fast]% 2 == 0){
swap(nums[slow++], nums[fast]);
fast = slow;
}
}
else{
if(nums[fast]% 2 == 1){
swap(nums[slow++], nums[fast]);
fast = slow;
}
}
fast++;
}
return nums;
}
};
成绩:9.76 40
思路:
快慢指针,慢指针遍历数组,快指针找对应就数,然后进行替换。其实核心和按奇偶排列数组是一样的,无非是多了两个点(体现在我的代码里):
1、奇偶交替
2、快指针需要被拉回来
但是这样肯定很慢,从成绩里也体现出来了,想想怎么优化。
长按键入
class Solution {
public:
bool isLongPressedName(string name, string typed) {
int slow = 0;
int fast = 0;
int namelen = name.length();
int typedlen = typed.length();
while(slow<namelen || fast<typedlen){
if(name[slow] != typed[fast]){
slow--;
if(slow == -1 || name[slow] != typed[fast]) return false;
}
slow++;
fast++;
}
if(slow == namelen && fast == typedlen) return true;
return false;
}
};
成绩:35 76.4
思路:
快慢指针,这是没毛病的
一开始我想的是,用slow指name,用fast之指typed;
若匹配,则fast向右走,直到找到不匹配的位置;
当不匹配时,fast和slow的下一个匹配,直到fast遍历到末尾。
匹配失败的条件就是,当fast找到的下一个不匹配的项和slow的下一个不匹配时,return false。但是当实例3时就会发现不对了,按照这种思路,实例3就是对的。没有考虑到name中又连续相同的两个字符。
说明动fast肯定是不行,那就动slow。两指针均从头开始遍历,若匹配,则均向右走。若不匹配,则slow向左移。为什么要这样,因为长按输出的一定是name里的对应字符,不匹配的的情况要么就是碰到长按输入了;要么就是干脆错了。
为什么第二种可以,因为第二种规避了当name里有连续相同字符,typed有大于连续字符个数的输入时,第一种方法认为typed中的连续字符都是name中对应的第一个字符长按输入造成的。
有序数组的平方
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0;i<nums.size();i++){
nums[i] = nums[i]*nums[i];
}
sort(nums.begin(),nums.end());
return nums;
}
};
成绩:87 71
思路:
这题就没啥好说的,平方后排序但是可以借此机会复习一下各种排序。
复写零
leetcode直达
思路1:利用vector的插入和删除操作
class Solution {
public:
void duplicateZeros(vector<int>& arr){
int len = arr.size();
int cur = 0;
while(cur<len){
if(!arr[cur]){
arr.pop_back();
arr.insert(arr.begin()+cur+1, 0);
cur++;
}
cur++;
}
}
};
成绩:29 64
思路:
cur指针遍历数组,当当前指向的数字为0时,就删除最后一个元素,并在cur位置插入0;
这样当然和这道题的考察点没关系了,但是能做出来。
思路二:双指针
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int len = arr.size();
int extend = 0; //需要插入的0的个数
int cur = 0; //当前指针
while(extend+cur<len){
if(!arr[cur]){
extend++;
}
cur++;
}
//跳出,统计结束,从len-extend开始往后du
int left = cur-1;
int right = len-1;
if(cur+extend == len+1){
arr[right--] = 0;
}
while(left>=0){
arr[right] = arr[left];
if(!arr[left]) arr[--right] = arr[left];
left--;
right--;
}
}
};
成绩:92 30
思路:
两次遍历
第一次遍历计算需要插入的0的个数。我的做法是定义一个遍历指针cur和需要插入的0的个数extend,当cur遍历到0时, extend加1;这时候就会遇到一个问题,什么时候停止遍历。停止遍历的点对第二次遍历来说非常重要。我的做法是当extend+cur>=len时,就跳出循环。那么这个时候会有两种情况:
情况一:插入0后长度刚好等于原数组长度
例如10230450
情况二:插入0后长度比原数组长度大1
例如84500007,那么这种情况就需要特殊处理
第二次遍历复制零。从后往前复制,这样就不会出现从前往后复制出现的覆盖问题。设置双指针left,right。left从右往左遍历数组,起始位置为cur。right指向需要插入的位置,对应第一次遍历出现的两种情况,right的起始位置有两种情况:
情况一:从末尾开始;
情况二:将末尾设为0,从倒数第二位开始。
将left指向的数复制到right位置上,两指针同时向前继续遍历。当left所指数为0时,多进行一次复制,即right多移动一次,达成复写0的效果。
删除回文子序列
对这题表示严厉谴责
leetcode直达
class Solution {
public:
bool palindriom(string s){
int left = 0;
int right = s.length()-1;
while(left<right){
if(s[left] != s[right]) return false;
left++;
right--;
}
return true;
}
int removePalindromeSub(string s) {
if(s.length() == 0) return 0;
if(palindriom(s)) return 1;
else return 2;
}
};
成绩:100 90.09
思路:
别思路了,太扯了,脑筋急转弯家人们。
检查整数及其两倍数是否存在
class Solution {
public:
bool checkIfExist(vector<int>& arr) {
for(int num:arr){
if(num != 0 && count(arr.begin(), arr.end(), num*2)>0) return true;
else if(num == 0 && count(arr.begin(), arr.end(), 0)>1) return true;
}
return false;
}
};
成绩:94 91
思路:
遍历一遍就行,找是否有对应两倍数就行;
这种方法有一个需要特殊处理的情况就是0,0的两倍还是0,会找到自己。把0单独拿出来,当遍历到0时,若数组中0的个数大于1,则返回true;
两个数组间的距离值
leetcode直达
思路一:暴力但不完全暴力
class Solution {
public:
int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {
int count = arr1.size();
for (int num1 : arr1) {
for (int num2 : arr2) {
if (abs(num1 - num2) <= d) {
count--;
break;
}
}
}
return count;
}
};
成绩:90 28
思路:
如果两个数组中的所有元素都符合题意,那返回的数就是arr1的size。那当arr1中有几个不符合的,就减几个即可。
那按照这个思路,两个for嵌套,最里面if判断;当出现距离不大于给定d的情况,减1。最后返回return即可。
思路2:优化了个寂寞
class Solution {
public:
int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {
int max = *max_element(arr2.begin(), arr2.end());
int min = *min_element(arr2.begin(), arr2.end());
int count = 0;
for (int num : arr1) {
if (num > max) {
if (abs(num - max) <= d) continue;
}
else if (num < min) {
if (abs(num - min) <= d) continue;
}
else {
sort(arr2.begin(), arr2.end());
int p = 0;
while (p < arr2.size() - 1) {
if (arr2[p] <= num && arr2[p + 1] >= num) {
break;
}
p++;
}
if (abs(num - arr2[p]) <= d || abs(num - arr2[p + 1]) <= d) continue;
}
count++;
}
return count;
}
};
成绩:不写了,就很低
思路:
加入了比较的环节。
情况一当arr1中当前数比arr2中最大值大时,只需要比较前者与最大值的距离;若两者距离大于d,则arr2中其余数比符合。若两者距离小于d,则当前数不符合要求。
情况二当arr1中当前数比arr2中最小值小时,只需要比较前者与最小值的距离。原因同理;
情况三当arr1中当前数介于arr2最大值和最小值之间时,首先对arr2排序,只要比较arr1当前数与arr2中最靠近当前数的两个数的距离;若均符合距离条件,则其余也符合;反之则不符合,continue。
交替合并字符串
leetcode直达
思路一
class Solution {
public:
string mergeAlternately(string word1, string word2) {
string res;
int len1 = word1.length();
int len2 = word2.length();
res.resize(len1+len2);
int key1 = 0;
int key2 = 0;
int key = 0;
while(key1<len1 && key2<len2){
res[key++] = word1[key1++];
res[key++] = word2[key2++];
}
while(key1<len1){
res[key++] = word1[key1++];
}
while(key2<len2){
res[key++] = word2[key2++];
}
return res;
}
};
成绩:100 8.19
思路:
创建一个数组res,将word1和word2两两字符串中的字符依次写入;若其中一字符串遍历完了,就把另一字符串接在后面即可。
这种方法需要额外创建一个长度为word1+word2长度的空间,可以试试在其中一个数组上进行操作。
思路二
class Solution {
public:
string mergeAlternately(string word1, string word2) {
int len1 = word1.length();
int len2 = word2.length();
word1.resize(len1 + len2);
int key1 = len1 - 1;
int key2 = len2 - 1;
int cur = len1 + len2 - 1;
while (key1 != key2) {
if (key1 > key2) word1[cur--] = word1[key1--];
else word1[cur--] = word2[key2--];
}
while (key1 >= 0) {
word1[cur--] = word2[key2--];
word1[cur--] = word1[key1--];
}
return word1;
}
};
成绩:100 10.92
思路:
在word1上操作,从后往前依次将word1和word2中的字符交叉写入word1对应位置。
这还只有10.92,到时候去看看解析看看还能怎么优化。
反转单词前缀
class Solution {
public:
string reverse(string word, int left, int right){
while(left<right){
swap(word[left++], word[right--]);
}
return word;
}
string reversePrefix(string word, char ch) {
int cur = 0;
int len = word.length();
while(cur<len){
if(ch == word[cur]) break;
cur++;
}
if(cur == len) return word;
return reverse(word, 0, cur);
}
成绩:100 20.13
思路:
先去找word中第一次出现ch的位置;如果能找到,那就替换包括它之前的字符串;
若找不到,说明name中没有ch字符,就原原本本输出即可。
快乐数
class Solution {
public:
int func(int n){
int sum = 0;
while(n>0){
sum+=pow(n%10, 2);
n = n/10;
}
return sum;
}
bool isHappy(int n) {
//vector<int> re s = {1, 10, 13, 31, 23, 32, 100, 68, 86, 82, 28, 19, 91 };
int count = 50;
int temp = n;
while(--count){
temp = func(temp);
if(temp == 1) break;
}
if(count) return true;
return false;
}
};
成绩:好久之前写的,时间成绩找不到了,反正不低就是了。
思路:
这题可以去演演算算几个数,可以发现,每位平方和导致的结果是每个数梯度下降(不要在意)的速度非常快。把int范围内9最多的数拿出来,第一次进行各位平方和就下降到了2000左右。再算几个也是这样。我就是抓住了这一点,直接循环各位平方和50此,在这50次里,如果能到1,那说明是快乐数;反之,则不是。其实根本不用50次,可以循环更小的次数。
还有一点,如果一个树不是快乐数,反复进行各位平方和实惠出现循环的,不知道是不是所有非快乐数都是这样,我去查一查。