Leetcode—双指针

双指针篇

简单

验证回文串

leetcode直达

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。

只出现一次的数字

leetcode直达

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;这意味着什么,意味着异或能够消去两个相同的数字。那和这题不就对上了么。除了需要得到的只出现一次的数字,其余出现两次的数字都会被消去。

环形链表

leetcode直达

/**
 * 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米这种项目时,是不是就会有套圈的现象出现。究其原因时套圈的跑得快,被套圈的跑得慢。这题就用了这样的思想。
设置快慢两个指针。快指针一次走两个节点,慢指针一次走一个节点。
如果该链表没有环,那么快指针会走到头。
如果有环,那么快指针和慢指针都会进入环(跑道),在跑几次后,慢指针会被快指针追上,此时就能说明该链表有环。
至于为什么要选取走一个节点和走两个节点,因为比较简单。其他选取方案可能会更快,到时候想想。

相交链表

leetcode直达

/**
 * 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 - 输入有序数组

leetcode直达

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的两数。

回文链表

leetcode直达

/**
 * 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的过程,能快一些。

反转字符串

leetcode直达

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
思路:
这个就很简单,左右指针换就行,这么简单的题放这么后面。

反转字符串中的元音字母

leetcode直达

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操作。

两个数组的交集

leetcode直达

思路一

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
思路:
这题用先排序再双指针遍历的办法会好做很多,因为同时遍历的情况下,能够被对应上的相交元素一定是按照数量较少的数组来对应的,超出的部分是对应不上的。

判断子序列

leetcode直达

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
对应比较,若两指针所指的字符相同,则一起向后遍历;
反之,则向后遍历主串。
当主串遍历完时,若模式串指针指向模式串末,则说明为子序列;反之,则非子序列。

反转字符串Ⅱ

leetcode直达

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
思路:
主要是正确定位需要反转字符串的首末位置,其中末位置又因字符长度会有不同(不同情况题里也写了)。其他的就不多说了。

反转字符中的单词Ⅲ

leetcode直达

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即为下一指针头,慢指针指向快指针位置。重复即可。

验证回文字符串Ⅱ

leetcode直达

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、多余字符在右半边,则右指针往左跳。
跳完这一次之后,若剩余字符串为回文串,则符合题意;反之则说明跳一次不够,不符合题意。

字符的最短距离

leetcode直达

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)));

翻转图像

leetcode直达

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来完成。

比较含退格的字符串

leetcode直达

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;

按奇偶排列数组

leetcode直达

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
思路:
就是我感觉,我做这类需要按条件前后换的题目,总是下意识的会有这种想法:左指针(慢指针)找到符合条件的那个节点;接着右指针(快指针)找到符合条件的节点;然后两指针所指节点互换。乍一看好像没毛病,但是仔细一琢磨肯定是错了。
这类题,像这题和移动零,其实两题是一个道理,无非前者是换奇偶,后者是换零一。那其实我们不需要去管指针符合什么条件才能换,而是关注应该把谁换上来。比如这题就是把偶数换上来,不需要去管左指针(慢指针)指向的是奇数还是偶数,只需要确保右指针(快指针)当前所指的是偶数即可。这样能达到一种什么效果:左指针左边都是偶数,没有奇数;左指针到右指针之间都是奇数。这就是我们想要的效果。

仅仅反转字母

leetcode直达

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
思路:很简单,不说了

按奇偶排序数组

leetcode直达

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、快指针需要被拉回来
但是这样肯定很慢,从成绩里也体现出来了,想想怎么优化。

长按键入

leetcode直达

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中对应的第一个字符长按输入造成的

有序数组的平方

leetcode直达

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
思路:
别思路了,太扯了,脑筋急转弯家人们。

检查整数及其两倍数是否存在

leetcode直达

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,到时候去看看解析看看还能怎么优化。

反转单词前缀

leetcode直达

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字符,就原原本本输出即可。

快乐数

leetcode直达

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次,可以循环更小的次数。
还有一点,如果一个树不是快乐数,反复进行各位平方和实惠出现循环的,不知道是不是所有非快乐数都是这样,我去查一查。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值