LeetCode(CPP):一月刷完热题100(3)

LeetCode(CPP):一月刷完热题100(3),第1张

11. 盛水最多的容器(mid)

思路:

在左右两侧分别维护一个指针,每次计算两个指针之间可以容纳水的容积,这个计算满足”木桶效应“,即以短边为准。

指针移动的依据是,哪边短就移动哪边。

维护一个ans来记录此过程中的容积最大值。

//双指针
class Solution{
public:
    int maxArea(vector& height){
        int l=0,r=height.size()-1;//分别从左右开始的两个指针
        int ans=0;
        while(l

 

15. 三数之和(mid)

 思路:

a+b+c=0,首先想到三层循环(wssb),变换一下思路b+c=-a=target,就减掉了一层循环!

但是这个target的值也是随a实时变化的,不能定义为全局变量,并且为了target有规律,先对数组进行sort,这样左侧的负数+右侧的正数结果才为0,特殊情况是abc全为0。

默认a

首先枚举a (也就是-target),遇见重复的数字可跳过,以保证不出现重复三元组;具体细节在注释中标识。

class Solution {
public:
    vector> threeSum(vector& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b
17. 电话号码的字母组合(mid)

 思路:

一个数字对应几个母,就想到了前面自动机里采用的哈希表。

那又怎么样得到排列组合呢,排列组合->回溯算法

那么回溯算法也是可以总结一下的:

我理解的回溯就是 递归+状态退回上一步

1.确定回溯(递归)函数的参数,一般比较多,包括要传入的原始字符串数组,容器,起始位置,边界大小等;并且这些参数如起始位置会随每次递归不断变化

2.确定终止条件,即何种状态下得到最终结果

3.遍历逻辑,怎么挨个进行

4.退回到上一状态,即回溯

code:   递归函数(参数)

                {

                   终止条件判断;

                   for(num:nums){

                                 递归调用(传参,num[i]  or  i   or 下一位置 ?);

                                 退到上一步;

                }}

class Solution {
public:
    vector letterCombinations(string digits) {
        vector combinations;//创建用于接收最终结果的容器
        if(digits.empty()){//判断输入的digits是否为空
            return combinations;//为空返回空容器
        }
        unordered_map phoneMap={//创建HashMap,可以通过键值来查找元素
            {'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},
            {'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}
        };
        string combination;//创建string对象来接受字符结合后的单次结果
        backtrack(combinations,phoneMap,digits,0,combination);//回溯算法
        return combinations;
    }
    void backtrack(vector& combinations,const unordered_map& phoneMap,const string& digits,int index,string& combination){
            //终止条件:
            if(index==digits.length()){//当索引次数==digits长度时,尾插到combinations容器中
            combinations.push_back(combination);
        }else{//未索引完成时
            char digit = digits[index];//挨个取出digits中的数字字符
            const string& letters = phoneMap.at(digit);//string成员函数at(),返回指定的下标位置的字符
            //循环
            //取出phoneMap中单个数字digit对应下标的字符串
            for(const char& letter:letters){//遍历letters字符串中的字符letter
                combination.push_back(letter);//将letter尾插到combination中
                //递归
                backtrack(combinations,phoneMap,digits,index+1,combination);//索引+1,再次回溯           
                //回溯
                combination.pop_back();
            }
        }
    }
};
19. 删除链表的倒数第N个结点(mid)

 思路:

next=next->next即可以完成删除 *** 作

这里用的双指针有点有点先创建一个窗口,再把窗口滑动到最后的意思,这样就不用从头去找目标节点,节省了一次遍历。

注意的点:建立一个哨兵节点,来返回最终链表

//双指针
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* p = new ListNode(0, head);//链表问题总是要创建一个哨兵结点,用来返回整个链表
        ListNode* first = head;
        ListNode* second = p;
        for (int i = 0; i < n; ++i) {//first先走n步,这样当first到链表尾时,second正好指向倒数第n个节点前面的结点,然后直接second->next = second->next->next,跳过该结点
            first = first->next;
        }
        while (first) {
            first = first->next;
            second = second->next;
        }
//删除+连接
        second->next = second->next->next;
        ListNode* q = p->next;//新链表的哨兵节点
        delete p;
        return q;
    }
};
20. 有效的括号(easy)

思路:

看到这个题我首先想的是”( ( [ ] ) )"算不算闭合?我想应该是的,要不然一次遍历就够了

之前好像遇到过这种题是采用栈的方式,左括号入栈,遇到右括号就出战,最后栈为空则有效;这道题是同样的方法。

基本思想就是上面所说,只是需要在创建一个哈希表,来做到右括号为key,左括号为value的作用。这样拿到一个括号先判断是不是右括号,右括号就配对,左括号放入栈。

只要栈不为空,就需要将下一个括号与栈顶元素配对。

class Solution {
public:
    bool isValid(string s) {
        int n = s.size();
        if (n % 2 == 1) {//奇数不成立
            return false;
        }

        unordered_map pairs = {//哈希表,一个左括号对应一个右括号
            {')', '('},
            {']', '['},
            {'}', '{'}
        };
        stack stk;//创建字符栈
        for (char ch: s) {//遍历s中的字符,放入ch
            if (pairs.count(ch)) {//count查找的是key
//如果哈希表中左侧键值ch的数量不为0,就是当前ch为右括号,就要进行判断
                if (stk.empty() || stk.top() != pairs[ch]) {
                    //栈中为空,或者栈顶元素不等于ch,返回错误
                    return false;
                }
                stk.pop();//配对成功就出栈,无需再次配对
            }
            else {
                stk.push(ch);//ch为左括号就入栈
            }
        }
        return stk.empty();//栈为空则为成立,返回true
    }
};

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/728015.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)