Leetcode 5 15 98 138解题记录


刷top-100的题目,最近做了这四道,原题: 138, 5, 15, 98.

15 3Sum

给你一串整数,让你找里面所有和为0的三元组。

思路: 暴力的话三循环,时间复杂度高。可以考虑先求任意两个数的和a,然后找剩下的数中值为-a的元素,但是复杂度依旧很高。考虑先确定一个数b,那么接下来的任务就是找和为-b的两个数,这个任务可以通过双指针法实现 - 排序然后从头部和尾部向中间靠拢。举个例子

设数组a[9]={-5 0 1 1 2 3 3 4 4},求1 ~ 9中和为5的二元组:

  1. 双指针i=1, j=8,结果集res
    1. a[i]+a[j] == t,则将这个二元组放入res,并将i++, j++
    2. a[i]+a[j] < t,则i++
    3. a[i]+a[j] > t,则j++
    4. 重复上述操作直到i>=j

上述过程会产生重复的元组(1,4),产生的原因是相同的元素多次计算了,那只需在每次执行i++j++时,跳过相同的元素即可。

代码:

vector<vector<int>> threeSum(vector<int> &nums) {
    if (nums.size() < 3) return vector<vector<int>>();
    sort(nums.begin(), nums.end());
    typedef pair<int, int> pii;
    auto findtarget = [](size_t tindex, const vector<int> &nums, vector<pii> &vecpii) -> bool {
        int t = -nums[tindex];
        size_t i = tindex + 1, j = nums.size() - 1;
        while (i < j && i < nums.size() && j < nums.size()) {
            const int tmp = nums[i] + nums[j];
            if (tmp == t) {
                vecpii.push_back(pii(i, j));
                while (i + 1 < nums.size() && nums[i + 1] == nums[i])
                    i++;
                while (j > 0 && nums[j - 1] == nums[j])
                    j--;
                i++, j--;
            } else if (tmp > t) {
                while (j > 0 && nums[j - 1] == nums[j])
                    j--;
                j--;
            } else if (tmp < t) {
                while (i + 1 < nums.size() && nums[i + 1] == nums[i])
                    i++;
                i++;
            }
        }
        return vecpii.size() > 0;
    };
    typedef tuple<int, int, int> tiii;
    vector<vector<int>> resvec;
    for (size_t i = 0; i < nums.size(); i++) {
        if (i > 0 && nums[i] == nums[i - 1])
            continue;
        vector<pii> vecpii;
        if (findtarget(i, nums, vecpii)) {
            for (auto &&p : vecpii)
                resvec.push_back({nums[i], nums[p.first], nums[p.second]});
        }
    }
    return resvec;
}

98 验证二叉搜索树

二叉搜索树: 左儿子比父亲小,右儿子比父亲大。

思路

  1. 递归

    对于父节点root,左子树小于root->val,右子树大于root->val,相当于规定了每个节点的上界与下界。因此对于节点p,假设其上界与下界为(mi, mx),则左子树上下界为(mi, root->val),右子树上下界为(root->val, mx)

  2. 宽搜

    与递归的思想一样,只是使用队列方式迭代,时间复杂度小一丢丢

  3. 中序遍历

    二叉搜索树的中序遍历结果一定有序,基于这个事实,只要在中序遍历的结果中发现了res[i]>=res[j],就说明不是二叉搜索树。可以先遍历在判断,也可以边遍历边判断

5 最长回文子串

回文串: a, aaa, aba, abba, aaaa, babababab

思路: 回文串是由子回文串组成的,基于这个特点,可以考虑递推的方式。

  1. 单独一个字符一定是回文串

  2. 两个相同且相连字符也是回文串

  3. s[i~j]是回文串,且s[i-1]==s[j+1],那么s[i-1 ~ j+1]也是回文串。

    s[i~j]='bbbb', s[i-1]='b',显然s[i-1 ~ i,i+1,i+2,...,j]都是回文串,因此需要考虑的是以s[i]开头的所有回文串。

  4. 若以s[i]开始的所有回文串长度为0 1 l2 l3 ... lk,且对于s[i-1],有s[i-1]==s[i+lj+1],则说明s[i-1 ~ i+lj+1]组成新的长为lj+2的回文串。

即设dp[i]=以s[i]开始的所有回文串长度,则: dp[i-1]={dp[i][k]+2, 若s[i-1]==s[i+dp[i][k]+1]}. 据此即可确定以任意s[i]开头能组成的所有回文串,然后确定最长的即可(可以在上述过程中确定)。另外,dp[i]只与dp[i-1]有关,因此可以使用长为2的dp数组,轮流使用以减少空间复杂度。

代码:

string longestPalindrome(string s) {
    if (s.size() <= 1)
        return s;
    const int sz = s.size();
    vector<int> dp[2] = {{0, 1}, {0, 1}};
    int mxlen = 1, mxpos = 0;
    for (int i = sz - 2; i >= 0; i--) {
        int ia = 0, ib = 1;
        if (i & 1) ia = 1, ib = 0;
        dp[ia] = {0, 1};
        for (auto &&k : dp[ib]) {
            if (i + k + 1 < sz && s[i] == s[i + k + 1]) {
                dp[ia].push_back(k + 2);
                if (k + 2 >= mxlen)
                    mxlen = k + 2, mxpos = i;
            }
        }
    }
    return s.substr(mxpos, mxlen);
}

138 拷贝带有随机指针的链表

拷贝一个链表,该链表的每个节点有一个指针,指向任意一个值。

思路: 拷贝时不考虑随机指针,记录 原指针 -> 新指针 的映射,拷贝之后再次遍历确定随机指针的值,但是这样的空间复杂度较高。如何减小呢?如果原链表可修改,那么可将新的节点插入到原节点后面,即

old: a->b->c->d   
new: a->a1->b->b1->c->c1->d->d1

那么随机指针的值就非常容易确定了。方法很有趣,不过指针的操作往往比较繁琐,需要先理清过程之后再写代码。

代码:

class Solution {
public:
    Node *copyRandomList(Node *head) {
        if (head == nullptr) return nullptr;
        if (head->next == nullptr) {
            Node *node = new Node(head->val, nullptr, nullptr);
            if (head->random == head) node->random = node;
            return node;
        }
        auto append2Node = [](Node *newnode, Node *oldnode) {
            newnode->next = oldnode->next;
            oldnode->next = newnode;
        };
        auto removeFromNode = [](Node *newnode, Node *oldnode) {
            oldnode->next = newnode->next;
            if (newnode->next != nullptr) newnode->next = newnode->next->next;
        };
        Node *p;
        p = head;
        // insert
        while (p != nullptr) {
            append2Node(new Node(p->val, nullptr, nullptr), p);
            p = p->next->next;
        }
        p = head;
        Node *q = p;
        // change random
        while (p != nullptr) {
            q = p->next;
            q->random = (p->random != nullptr ? p->random->next : nullptr);
            p = q->next;
        }
        p = head;
        Node *res = p->next;
        // delete
        while (p != nullptr) {
            Node *q = p->next->next;
            removeFromNode(p->next, p);
            p = q;
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值