拍案叫绝的逻辑

开贴记录刷题过程中,遇到的令我拍案叫绝的解法。

1、

1071. Greatest Common Divisor of Strings求给定的两个字符串的最大的公共“公约数”。

class Solution {
    
    
public:
    inline int gcd(int a, int b) {
    
    return b == 0? a : gcd(b , a % b);}
    string gcdOfStrings(string str1, string str2) {
    
    
        if (str1 + str2 != str2 + str1) return "";
        return str1.substr(0, gcd(str1.size(), str2.size()));
    }
};

这个代码,最牛的地方在于,如何判断给定的两个串是否有相同的“公约数”,直接将str1+str2与str2+str1相比较,如果不同,则肯定没有相同的公约数。
2021/1/22更新

2、

面试题 08.03. Magic Index LCCI
题中给出一个有序的数组,元素可以重复,即非严格递增有序数组,求第一个使得i==nums[i]的下标。例如nums[]={2,3,4,4,5,5},答案既是5,因为nums[5]=5。
题目标注是二分查找,不过属实没有头绪,因为比如说nums【】={0,1,2,3,4,5,6,7,8},答案是0。使用二分时,初始L=0,R=8,第一轮迭代时mid=4,但是nums【4】=4,此时肯定是排除右侧区间,即令R=mid没有问题。
但是如果当nums=【-1,-1,2,4,4,9,10】时,第一次迭代时,L=0,R=6,此时mid=3,而nums【3】=4,当前nums【mid】>mid,应该往左侧搜索。但是如果nums=【-1,-1,3,4,4,9,10】时,还是有mid=3,此时nums【mid】=mid,但是应该往右侧搜索,也即出现了同样地情况nums【mid】>mid时,对于这两个例子,处理是不同的,这肯定不可以使用普通的二分。
在评论区看到一个解,

class Solution {
    
    
public:
    int findMagicIndex(vector<int>& nums) {
    
    
        for(int i=0;i<nums.size();){
    
    //注意点1
            if(i==nums[i]) return i;
            i=max(i+1,nums[i]);//注意点2
        }
        return -1;
    }
};

注意点1,在for循环中不写变量自增语句
注意点2,变量下一步的取值。
太强了,虽然和拍案1一样适用情况单一,但是仍值得记录下来,
本质上仍是数组的遍历,但是在含有重复元素时,是会快一些。
比如说nums【】={-1,3,5,5,5,5},走一遍程序:
一、i=0时,不走if语句,i=max(i+1,-1),此时i变为1;
二、i=1时,不走if语句,i=max(i+1,3),此时i变为3;
三、i=3时,不走if语句,i=max(i+1,5),此时i变为5;
四、i=5时,走if语句,找到了第一个符合条件的下标。
发现跳过了i=2与i=4时的情况,这在数据量巨大时候,有很大的的优势。
其内在逻辑是,当num【i】>i+1时,即当前位置i上的元素大于i+1时,比如说i=3时,nums【3】=5,此时第三个位置是5,已经大于3+1了,所以,不用去判断nums【4】了,答案肯定不是4的位置,即nums【4】!=4,因为nums【3】=5>4,位置4处,可以直接跳过。

3

2021/1/23更新
力扣面试题 10.01. 合并排序的数组
题目不难,但是,评论区的思路还是有点惊艳到我。题目要求将两个数组归并成一个有序的数组,放到数组a中,其中数组a已经开好了足够大的空间可以用来放置归并之后的数组。一开始想着如何从两个数组的开始位置即各自的0号位置开始遍历,找当前最小的放,但是找到两者之间较小的值之后,如何放到数组中呢?
评论区有个思路很好,就是,不要从各个数组的0号位置开始遍历,而是从各个数组的有数值的最后一个位置开始遍历,这样,找出当前两个指针的所指的最大值,放到数组a的最后面。数组仍然是按照递增顺序进行排列的。

class Solution {
    
    
public:
    void merge(vector<int>& a, int m, vector<int>& b, int n) {
    
    
        int i=m-1,j=n-1,k=m+n-1;
        while(i>=0&&j>=0){
    
    
            if(a[i]<b[j]) a[k--]=b[j--];
            else a[k--]=a[i--];
        }
        while(i>=0) a[k--]=a[i--];
        while(j>=0) a[k--]=b[j--];//注意这里是,如果数组b还没有将全部的元素加入进去,那么,剩余元素进去
    }
};

这种逆着的思路真的值得好好体会

4

2021/2/3更新
力扣876. 链表的中间结点,给定一个链表,返回其中间的结点。
热评第一:

思路:快指针q每次走2步,慢指针p每次走1步,当q走到末尾时p正好走到中间

代码如下

class Solution {
    
    
    public ListNode middleNode(ListNode head) {
    
    
        ListNode p = head, q = head;
        while (q != null && q.next != null) {
    
    
            q = q.next.next;//注意走两步的写法
            p = p.next;
        }
        return p;
    }
}

思路很好,快慢指针,适合出408

猜你喜欢

转载自blog.csdn.net/weixin_44321570/article/details/112854051