刷穿LeetCode——Task04

这篇博客记录第4天刷题的思路。

16. 最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4

分析:这道题和15. 三数之和非常接近,一个是和刚好等于target的三元数,一个是最接近target的三数之和。大致思路依然是先排序,然后将三重暴力搜索转换为双指针问题。注意:这里我是对三数之和与target的绝对值之差(即残差res)进行判断,看内部每次指针移动时残差res是否减小。因此要恢复原有的三数之和,需要额外定义一个Ifbig变量,记录残差res减小时三数之和与target相比更大还是更小。也可以直接对残差res判断后将本轮的三数之和赋值变量,最后输出遍历完成后的结果。

class Solution {
    
    
public:
    int threeSumClosest(vector<int>& nums, int target) {
    
    
        int sum = 0;

        if (nums.size() < 3) {
    
    
            for (int i = 0; i < nums.size(); i++) {
    
    
                sum += nums.at(i);     
            }
            return sum;
        }
        sort(nums.begin(), nums.end());
        int res = 1e4, Ifbig = 1;
        bool go_on = true;
        for (int i = 0; i < nums.size() - 2; i++) {
    
    
            int left = i + 1, right = nums.size() - 1;
            while (left < right) {
    
    
                sum = nums[i] + nums[left] + nums[right];
                res = min(res, abs(target - sum));  
                if (abs(target - sum) < res && sum < target)
                    Ifbig = -1;
                if (abs(target - sum) < res && sum > target)
                    Ifbig = 1;
                
                if (sum < target)   left++;
                else if (sum > target)  right--;
                else {
    
    
                    go_on = false;
                    break;
                }
            }
            if (!go_on) break;
        }
        return (target + Ifbig * res);

    }
};

20.有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true

分析:这道题我是完全照搬评论区大佬的思路和代码的。。。用栈的FIFO特性对字符串元素遍历:若当前为左括号元素则入栈,若当前为右括号元素,则正确格式下必定与栈顶的左括号元素对应,否则直接判错。正确格式下,经历了上面的比较后栈顶元素应出栈,不影响后面的入栈与比较。这里使用unordered map作为左右括号对照表,其中key为左括号类型,比较巧妙,详细C++代码如下:

class Solution {
    
    
public:
    bool isValid(string s) {
    
    
        while (s != "") {
    
    
            unordered_map<char, char> m = {
    
    {
    
    '[', ']'}, {
    
    '{', '}'}, {
    
    '(', ')'}};  //key值为左括号类型
            if (m.find(s[0]) == m.end() || s.size() == 1)   //s开头元素错误或仅有1个元素
                return false;
            stack<char> left;   //C++ stack类,存左括号
            for (int i = 0; i < s.size(); i++) {
    
    
                if(m.find(s[i]) != m.end()) left.push(s[i]); //左括号元素,入栈
                else {
    
       //若 s[i] 非左括号,则它之前必有左括号在栈中且栈顶与它对应,若栈空直接判错
                    if(!left.empty()) {
    
    
                        //栈非空,正确格式下栈顶元素必与此时右边类型的括号 s[i] 对应,否则直接判错
                        if(m[left.top()] != s[i])  
                            return false;
                        else
                            left.pop();  //正确格式,则栈顶左括号元素出栈
                    }
                    else
                        return false; 
                }
            }
            //一趟遍历结束,若s 格式正确,则经历了上面 比较后栈顶元素出栈 的操作后,栈应空,否则直接判错
            if(left.empty())
                break;
            else
                return false;
        }
        return true;
    }
};

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
merge_linklist

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = [] 输出:[]

示例 3:

输入:l1 = [], l2 = [0] 输出:[0]

提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

这道题和02.两数之和一样,都是根据输入的两链表进行判断求新链表。我的方法太中规中规,对每次两链表节点的value进行大小排序,然后新建链表节点。代码如下:

class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    
    
        ListNode head(0), *p = &head;
        while (l1 || l2) {
    
    
            int lvalmin = 0, lvalmax = 0;
            bool valmax = true;
            if (l1 != nullptr && l2 != nullptr && l1->val != l2->val) {
    
    
                lvalmin = min(l1->val, l2->val);
                lvalmax = max(l1->val, l2->val);
            }
            else if (l1 != nullptr && l2 != nullptr && l1->val == l2->val) {
    
    
                lvalmin = l1->val; lvalmax = l2->val;
            }    
            else if (l1 != nullptr && l2 == nullptr) {
    
    
                lvalmin = l1->val;
                valmax = false;
            }    
            else if (l1 == nullptr && l2 != nullptr){
    
    
                lvalmin = l2->val;
                valmax = false;
            }
            ListNode* next = new ListNode(lvalmin);
            next->val = lvalmin;
            p->next = next;
            p = p->next;

            if(valmax) {
    
    
                ListNode* next = new ListNode(lvalmax);
                next->val = lvalmax;
                p->next = next;
                p = p->next;
            }
            l1 = l1 ? l1->next : nullptr;
            l2 = l2 ? l2->next : nullptr;    
        }
        return head.next;
    }
};

但是当某个链表节点value比较大的情况,该方法是无法通过的,例如输入为:
[5]
[1,2,4]
该方法输出:
[1,5,2,4]

看到评论区一个解法,非常优雅,利用到了输入链表有序的特点,在对两输入链表节点value比较大小并交换节点排序的过程中,只移动始终指向较小节点的指针到下一个。测试如下:
输入:
[5,7]
[1,2,4,6]
输出:
[1,2,4,5,6,7]
该方法有效!

class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    
    
        ListNode head(0), *p = &head;
        //链表有序,只移动指向较小节点的指针
        while(l1 && l2) {
    
           
            if(l1->val > l2->val) swap(l1, l2);  //地址交换
            p->next = l1;
            l1 = l1->next;
            p = p->next;
        }
        p->next = l1 ? l1 : l2;
        return head.next;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_33007293/article/details/112646133