【C++复习】经典笔试题

news2024/11/25 11:20:43

文章目录

  • 八大排序
    • 快排过程
  • 卡特兰数
  • 反转链表
  • 链表的回文结构
  • 左叶子之和
  • 另一棵树的子树
  • 归并排序
  • 类与对象
  • 编程训练
    • 杨辉三角
    • 字符串乘积
    • 二叉树前序遍历成字符串
    • 数组的交集
    • 二叉树的非递归前序遍历
    • 连续子数组的最大乘积

八大排序

插冒归稳定

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

快排过程

以 [3,4,6,1,2,4,7] 为例,以第一个元素3为base,定义左右两个指针(小熊l,小熊r),分别从两端开始扫描。从右向左找比3小的数,替换l所在位置的元素。再从左往右找比3大的数,然后替换r所在位置的元素。重复此过程直至两个小熊重合(两个指针指向同一元素),base替换此元素,此时第一轮结束。再递归排序base左右两部分的元素。

卡特兰数

在这里插入图片描述

C0 = 1,         
C1 = 1,         C2 = 2,          C3 = 5,          C4 = 14,          C5 = 42,
C6 = 132,       C7 = 429,        C8 = 1430,       C9 = 4862,        C10 = 16796,
C11 = 58786,    C12 = 208012,    C13 = 742900,    C14 = 2674440,    C15 = 9694845,
C16 = 35357670, C17 = 129644790, C18 = 477638700, C19 = 1767263190, C20 = 6564120420, ...

反转链表

💡 采用翻指针法,将每个节点的next指针翻转一下,让其指向前一个节点即可

struct ListNode* ReverseList(struct ListNode* head )
{
    struct ListNode* prev=NULL;//prev指针初始为为NULL,考虑链表本身为空的情况
    struct ListNode* cur=head;
    while(cur)
    {
        struct ListNode* next=cur->next;//先保存下一个节点,防止下面翻指针过程中丢失next节点
        cur->next=prev;
        prev=cur;
        cur=next;
    }
    return prev;//prev最终指向原链表最后一个节点,即翻转后的第一个节点
}

💡 头插法,遍历原链表每个节点,将其头插到初始为NULL的新链表上即可

struct ListNode* ReverseList(struct ListNode* head )
{
    struct ListNode* cur=NULL;//cur指针初始化为NULL,考虑链表本身为空的情况
    while(head)
    {
        struct ListNode* next=head->next;//先保存下一个节点,防止下面翻指针过程中丢失next节点
        //头插
        head->next=cur;
        cur=head;
        head=next;
    }
    return cur;//cur最终指向原链表最后一个节点,即翻转后的第一个节点
}

💡 递归法,先递归到最后链表的最后一个节点处,然后在回退的时候进行翻指针

struct ListNode* ReverseList(struct ListNode* head ) 
{
    if(head==NULL||head->next==NULL)
    { 
        return head; 
    }
    struct ListNode* cur = ReverseList(head->next);// 这里的cur就是最后一个节点
    head->next->next = head;
    // 防止链表循环,需要将head.next设置为空
    head->next = NULL;
    // 每层递归函数都返回cur,也就是最后一个节点
    return cur;//这里就是将最后一个节点不断返回回去
}

💡 三指针法,就是用三个指针定位三个节点,做到翻指针的效果

struct ListNode *ReverseList(struct ListNode *head)
{
    if (head == NULL)
    {
        return NULL;
    }
    struct ListNode *prv = NULL, *cur = head, *nxt = cur->next;
    while (cur)
    {
        cur->next = prv;
        prv = cur;
        cur = nxt;

        if (nxt)
            nxt = nxt->next;
    }
    return prv;
}

链表的回文结构

  1. 先通过快慢指针的方式,找到链表中间节点。
  2. 再将后半部分链表进行反转。
  3. 最后将链表前半部分和反转后的后半部分链表逐节点进行比较判断。
class PalindromeList {
  public:
    bool chkPalindrome(ListNode* A) {
        if (A == nullptr || A->next == nullptr)
            return true;

        ListNode* slow, *fast;
        slow = fast = A;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
        }

        ListNode* prev, *cur, *nxt;
        prev = nullptr;
        cur = slow;
        while (cur) {
            nxt = cur->next;
            cur->next = prev;
            prev = cur;
            cur = nxt;
        }

        while (A && prev) {
            if (A->val != prev->val)
                return false;
            
            A = A->next;
            prev = prev->next;
        }
        return true;
    }

};

左叶子之和

左叶子之和_牛客题霸_牛客网 (nowcoder.com)

class Solution {
  public:
    void _helper(TreeNode* root, int& sum) {
        if (root == nullptr)
            return;
        if (root->left && !root->left->left && !root->left->right)
            sum += root->left->val;
            
        _helper(root->left, sum);
        _helper(root->right, sum);
    }
    int sumOfLeftLeaves(TreeNode* root) {
        int sum = 0;
        _helper(root, sum);
        return sum;
    }
};

另一棵树的子树

572. 另一棵树的子树 - 力扣(LeetCode)

class Solution {
public:
    bool isSubtree(TreeNode* s, TreeNode* t) {
        if (!s && !t)
            return true;

        // 代码走到这里 已经保证s t不全空
        if (!s || !t)
            return false;

        return isSameTree(s, t) || isSubtree(s->left, t) ||
               isSubtree(s->right, t);
    }

    bool isSameTree(TreeNode* s, TreeNode* t) {
        if (!s && !t)
            return true; 
        if (!s || !t)
            return false; 
        return (s->val == t->val) && isSameTree(s->left, t->left) &&
               isSameTree(s->right, t->right);
    }
};

归并排序

递归

#include <iostream>
#include <vector>

void merge(std::vector<int>& arr, int left, int mid, int right) {
    std::vector<int> leftArr(arr.begin() + left, arr.begin() + mid + 1);
    std::vector<int> rightArr(arr.begin() + mid + 1, arr.begin() + right + 1);

    int i = 0, j = 0, k = left;

    while (i < leftArr.size() && j < rightArr.size()) {
        if (leftArr[i] <= rightArr[j]) {
            arr[k++] = leftArr[i++];
        } else {
            arr[k++] = rightArr[j++];
        }
    }

    while (i < leftArr.size()) {
        arr[k++] = leftArr[i++];
    }

    while (j < rightArr.size()) {
        arr[k++] = rightArr[j++];
    }
}

void merge_sort_recursive(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        merge_sort_recursive(arr, left, mid);
        merge_sort_recursive(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

int main() {
    int N;
    std::cin >> N;
    std::vector<int> data(N);

    for (int i = 0; i < N; ++i) {
        std::cin >> data[i];
    }

    merge_sort_recursive(data, 0, N - 1);

    for (const auto& num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

非递归

#include <iostream>
#include <vector>

void merge(std::vector<int>& arr, int left, int mid, int right) {
    std::vector<int> leftArr(arr.begin() + left, arr.begin() + mid + 1);
    std::vector<int> rightArr(arr.begin() + mid + 1, arr.begin() + right + 1);

    int i = 0, j = 0, k = left;

    while (i < leftArr.size() && j < rightArr.size()) {
        if (leftArr[i] <= rightArr[j]) {
            arr[k++] = leftArr[i++];
        } else {
            arr[k++] = rightArr[j++];
        }
    }

    while (i < leftArr.size()) {
        arr[k++] = leftArr[i++];
    }

    while (j < rightArr.size()) {
        arr[k++] = rightArr[j++];
    }
}

void merge_sort_iterative(std::vector<int>& arr)
{
    int n = arr.size();
    for (int size = 1; size < n; size *= 2) 
    {
        for (int left = 0; left < n; left += 2 * size)
        {
            int mid = std::min(left + size - 1, n - 1);
            int right = std::min((left + 2 * size - 1), (n - 1));
            if (mid < right) 
            {
                merge(arr, left, mid, right);
            }
        }
    }
}

int main() {
    int N;
    std::cin >> N;
    std::vector<int> data(N);

    for (int i = 0; i < N; ++i) {
        std::cin >> data[i];
    }

    merge_sort_iterative(data);

    for (const auto& num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

类与对象

  1. 已知表达式++a中的"++"是作为成员函数重载的运算符,则与++a等效的运算符函数调用形式为:a.operator++();后置++a++ == a.operator++(0)

  2. c++中,ClassA的构造函数和析构函数的执行次数分别为:程序可能崩溃

    ClassA *pclassa=new ClassA[5];
    delete pclassa;
    
  3. resize和reserve

    int main()
    {
        string str("Hello Bit.");
        str.reserve(111);
        str.resize(5);
        str.reserve(50);
        cout << str.size() << ":" << str.capacity() << endl;
        return 0;
    }
    // linux:5:50
    // vs:   5:111
    
  4. 迭代器失效1.0

int main()
{
    int ar[] = { 1,2,3,4,0,5,6,7,8,9 };
    int n = sizeof(ar) / sizeof(int);
    vector<int> v(ar, ar + n);
    vector<int>::iterator it = v.begin();
    while (it != v.end())
    {
        if(*it == 0)
            v.erase(it);
        else
            cout<<*it;
        it++;
    }
    return 0;
}

erase方法从vector容器中删除一个或多个元素,并调整vector的大小。删除元素后,后续元素会被移动以填补删除的空隙,此时it指向5,所有指向被删除元素之后的迭代器都会失效,即此时it失效linux允许执行 it++操作,故输出12346789;vs不允许,故崩溃。

解决

if(*it == 0)
    it = v.erase(it);
else
{
    cout<<*it;
    it++;
}
  1. 迭代器失效2.0
int main()
{
    Container cont = { 1, 2, 3, 4, 5};
    Container::iterator iter, tempIt;
    for (iter = cont.begin(); iter != cont.end();)
    {
        tempIt = iter;
        ++iter;
        cont.erase(tempIt);
    }
}
// 会导致代码片段崩溃的Container类型是(vector和deque)

vector和deque,erase操作会使所有指向被删除元素之后元素的迭代器、引用和指针都失效。

但对于list,erase操作只会使指向被删除元素的迭代器失效,其他迭代器、引用和指针仍然有效。

由题知虽然++iter迭代器了,但用erase(tempit)以后,vector、deque底层是连续空间,删除会挪动数据,最终导致iter意义改变,迭代器就已失效了。但是list,不是连续空间,删除以后tempIt虽然失效了,但是不影响iter。

  1. 引用

引用在定义时必须被初始化,并且必须绑定到一个已经存在的对象。
引用一旦被初始化并绑定到一个对象,就不能改变其绑定。
引用本身不是一个独立的对象,它只是被绑定对象的一个别名,因此不能定义引用的引用
引用本身不是对象,因此不能有指向引用的指针。指针可以指向对象,但不能直接指向引用。

  1. 析构函数与this指针
class A
{
public:
   ~A()
    {
        delete this;
        this = NULL;
    }
};
析构函数内部调用delete -- 无限递归
this的类型`A* const this`,this不能更改指向,编译错误。故该程序在编译时就无法通过。
  1. 私有重写虚函数
class A
{
public:
    virtual void f()
    {
        cout << "A::f()" << endl;
    }
};
class B : public A
{
private:
    virtual void f()
    {
        cout << "B::f()" << endl;
    }
};
int main()
{
    A *pa = (A *)new B;
    pa->f();
    return 0;
}
// B::f()

编译时,不会确定具体调用哪个虚函数;只是进行语法检测,编译器检测到pa是A类型的指针,编译器会确定两件事:A类中有没有f()函数 + 该函数是否是public。如果满足这两个条件,那么编译通过,否则编译报错。显而易见,该代码可以编译通过,在运行时,pa是指向子类的基类指针,回去子类的虚表调用f(),改函数已被B重写故调用B::f()。

静态类型: 声明变量时给的类型A * pa=newB; pa的静态类型就是A * 。编译时看静态类型。

动态类型:实际指向或引用实体的类型pa的动态类型时B* , 运行时看的是动态类型。

  1. 判断题

被virtual修饰的函数称为虚函数 【❌】被virtual修饰的成员函数称为虚函数。友元函数不属于成员函数,不能成为虚函数。

位图可以很方便的进行字符串的映射以及查找【❌】采用位图标记字符串时,必须先将字符串转化为整形的数字,找到位图中对应的
比特位,但是在字符串转整形的过程中,可能会出现不同字符串转化为同一个整形数字,即冲突,因此一般不会直接用位图处理字符串。

set中存<key, key> unordered_set中存< key >

unordered _ map和unordered _ set:它们在进行元素插入时,都得要通过key的比较去找待插入元素的位置。【❌】不需要比较,只需要通过哈希函数,就可以确认元素需要存储的位置。

AVL树是一定需要旋转,红黑树不一定,比如插入的节点的parent节点为红色,uncle节点也为红色,那么只需要将父节点和叔叔节点颜色变黑,将grandfather的颜色变红,该种情况下,红黑树是不需要进行旋转的。AVL树和红黑树中序遍历都可以得到有序序列,因为它们都是二叉搜索树。【✔】

插入时,AVL树最多只需要旋转两次 :新结点插入前树已经是AVL树了,新结点插入后,如果导致某结点平衡因子为2或-2时,AVL树平衡性遭到破坏,以该结点为根的二叉树局部高度增加了一层,旋转完成后,该棵树高度和插入新结点前高度一致,所以旋转后对整棵树没有任何影响,旋转结束后树满足AVL树的特性。该旋转可能是单旋也可能是双旋,因此插入时AVL树最多只需要旋转两次。

删除时,只要某个节点的平衡因子不满足特性时 ,只需要对该棵子树进行旋转,就可以使AVL树再次平衡。错误,可能需要旋转多次,子树旋转后,其高度降低了一层,其上层可能也需要跟着旋转。

AVL树的节点中必须维护平衡因子,因为要依靠其平衡因子是否需要旋转以维护其平衡性 ,平衡因子不是必须要维护的,在操作时也可以直接通过高度函数来算,只不过比。较麻烦

AVL树的双旋转只需要直接使用对应的单旋转即可:不能直接使用单旋转,因为两个单旋转完成后,还需要对部分节点的平衡因子进
行更新

二叉树的后序非递归遍历中,需要的额外空间:需要一个栈模拟递归的过程,一个顺序表保存节点。同时还需要一个prev节点保存刚刚遍历过的结点来解决某个结点有右子树时的死循环问题。

  1. 多态经典代码
class A
{
public:
    A()
        : m_iVal(0)
    {
        test();
    }

    virtual void func()
    {
        std::cout << m_iVal << ' ';
    }

    void test()
    {
        func();
    }

public:
    int m_iVal;
};
class B : public A
{
public:
    B()
    {
        test();
    }
    
    virtual void func()
    {
        ++m_iVal;
        std::cout << m_iVal << ' ';
    }
};
int main(int argc, char *argv[])
{
    A *p = new B;
    p->test();
    return 0;
}
// 0 1 2

编程训练

杨辉三角

class Solution 
{
  public:
    vector<vector<int>> generate(int numRows) 
    {
        vector<vector<int>> t;
        for (int i = 0; i < numRows; i++)
        {
            vector<int> row(i + 1, 1);
            for (int j = 1; j < i; j++) 
            {
               row[j]  = t[i - 1][j - 1] + t[i - 1][j];
            }
            t.push_back(row);
        }

        return t;
    }
};

字符串乘积

在这里插入图片描述

模拟手工计算两个大数相乘,从低位到高位,每次计算一对对应位的乘积,然后加上进位和之前的结果,更新当前位的结果和进位。

class Solution
{
public:
    string solve(string num1, string num2)
    {
        if (num1 == "0" || num2 == "0")
        {
            return "0";
        }
        reverse(num1.begin(), num1.end());
        reverse(num2.begin(), num2.end());

        string ans(num1.size() + num2.size(), '0');
        int carry = 0;

        for (int i = 0; i < num1.size(); ++i)
        {
            for (int j = 0; j < num2.size(); ++j)
            {
                // 当前位的结果等于之前的进位加上两数当前位的乘积和之前计算的结果
                carry += (num1[i] - '0') * (num2[j] - '0') + (ans[i + j] - '0');
                // 计算当前位的结果,保存到结果中
                ans[i + j] = carry % 10 + '0';
                // 更新进位
                carry /= 10;
            }
            // 如果最后还有进位,将进位加到结果中
            if (carry > 0)
            {
                ans[i + num2.size()] = (carry + '0');
                carry = 0;
            }
        }
        // 如果最高位为0(即未被使用),则去掉最高位
        if (ans.size() >= 2 && ans.back() == '0')
        {
            ans.pop_back();
        }
        // 将结果反转回正确的顺序
        reverse(ans.begin(), ans.end());
        // 返回结果
        return ans;
    }
};

逻辑更清晰 用vector暂存每位结果 只加不进

class Solution
{
public:
    string solve(string s, string t)
    {
        // 其中一个数为0
        if (s == "0" || t == "0")
        {
            return "0";
        }
        int sNum = s.size();
        int tNum = t.size();
        vector<int> tmp(sNum + tNum); // save per add
        string ret;
        // reverse s t
        reverse(s.begin(), s.end());
        reverse(t.begin(), t.end());
        // only add not carry
        for (int i = 0; i < sNum; i++)
        {
            for (int j = 0; j < tNum; j++)
            {
                tmp[i + j] += (s[i] - '0') * (t[j] - '0');
            }
        }
        // deal carry
        int carry = 0;
        for (auto per : tmp)
        {
            per += carry;
            ret += per % 10 + '0';
            carry = per / 10;
        }
        // max high carry
        while (carry)
        {
            ret += carry % 10 + '0';
            carry /= 10;
        }
        // prev zero
        while (ret.size() >= 2 && ret.back() == '0')
        {
            ret.pop_back();
        }
        reverse(ret.begin(), ret.end());
        return ret;
    }
};

二叉树前序遍历成字符串

606. 根据二叉树创建字符串 - 力扣(LeetCode)

一棵树只有这五种情况:

只有根节点

只有左子树

只有右子树

左右子树都有

法一

  1. 如果左子树为真,需要加括号
  2. 如果左子树为空,右子树为真,需要给左子树加()不破坏输入与输出一一映射的关系
  3. 如果左右子树都为空,则不需要加括号
  4. 当右子树为空,不需要加括号
class Solution {
    public:
    string tree2str(TreeNode* root) {
        if (root == nullptr)
        {
            return "";
        }
        string ret;
        ret += to_string(root->val);

        //左子树为真,需要加括号。如果没有左子树,但是有右子树为了不破坏输入与输出一一映射的关系,同样需要加括号
        if (root->left || root->right)
        {
            ret+= '(';
            ret += tree2str(root->left);
            ret += ')';
        }
        if (root->right)
        {
            ret+= '(';
            ret += tree2str(root->right);
            ret += ')';
        }
        return ret;
    }
};

法二:

  1. 当节点为叶子节点时不需要加括号。

  2. 当左子树存在,右子树为空时,需要给左子树加上括号。

  3. 当左右子树都为真就去递归加括号或者左子树为空右子树为真,这个时候为了不破坏输入与输出一一映射的关系,需要加左括号

    class Solution {
        public:
        string tree2str(TreeNode *root) {
            // 空树
            if (root == nullptr) {
                return "";
            }
            // 只有根节点
            if (root->left == nullptr && root->right == nullptr) {
                return to_string(root->val);
            }
            // 只有左子树
            if (root->right == nullptr) {
                return to_string(root->val) + "(" + tree2str(root->left) + ")";
            }
            // 只有右子树 + 左右子树都有
            return to_string(root->val) + "(" + tree2str(root->left) + ")(" +
                tree2str(root->right) + ")";
        }
    };
    

法三:迭代

class Solution {
public:
    string tree2str(TreeNode *root) {
        string ans = "";
        stack<TreeNode *> st;
        st.push(root);
        unordered_set<TreeNode *> vis;
        while (!st.empty()) {
            auto node = st.top();
            if (vis.count(node)) {
                if (node != root) {
                    ans += ")";
                }
                st.pop();
            } else {
                vis.insert(node);
                if (node != root) {
                    ans += "(";
                }
                ans += to_string(node->val);
                if (node->left == nullptr && node->right != nullptr) {
                    ans += "()";
                }
                if (node->right != nullptr) {
                    st.push(node->right);
                }
                if (node->left != nullptr) {
                    st.push(node->left);
                }
            }
        }
        return ans;
    }
};

数组的交集

只求交集 不管是否重复

class Solution 
{
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) 
	{
		unordered_set<int> us1, us2;

		for (auto& e : nums1)
			us1.insert(e);

		for (auto& e : nums2)
			us2.insert(e);
        
		return get(us1, us2);
	}

	vector<int> get(unordered_set<int>& us1, unordered_set<int>& us2) 
	{
		if (us1.size() > us2.size())
		return get(us2, us1);

		vector<int> v;
		for (auto& e : us1)
		{
			if (us2.count(e))
				v.push_back(e);
		}
		return v;
	}
};

交集元素有多个

//法一:排序+双指针
//1.1 用multiset排序
class Solution
{
    public:
    vector<int> intersect(vector<int> &nums1, vector<int> &nums2)
    {
        multiset<int> ms1(nums1.begin(), nums1.end());
        multiset<int> ms2(nums2.begin(), nums2.end());
        multiset<int>::iterator it1 = ms1.begin();
        multiset<int>::iterator it2 = ms2.begin();
        vector<int> v;
        while (it1 != ms1.end() && it2 != ms2.end())
        {
            if (*it1 < *it2)
                ++it1;
            else if (*it1 > *it2)
                ++it2;
            else
            {
                v.push_back(*it1);
                ++it1;
                ++it2;
            }
        }
        return v;
    }
};
//1.2 用sort排序
class Solution 
{
    public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2)
    {
        sort(nums1.begin(), nums1.end());
        sort(nums2.begin(), nums2.end());
        int length1 = nums1.size(), length2 = nums2.size();
        vector<int> intersection;
        int index1 = 0, index2 = 0;
        while (index1 < length1 && index2 < length2)
        {
            if (nums1[index1] < nums2[index2]) 
            {
                index1++;
            } 
            else if (nums1[index1] > nums2[index2]) 
            {
                index2++;
            } 
            else 
            {
                intersection.push_back(nums1[index1]);
                index1++;
                index2++;
            }
        }
        return intersection;
    }
};
//法二:哈希表
class Solution 
{
    public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) 
    {
        if (nums1.size() > nums2.size()) 
        {
            return intersect(nums2, nums1);
        }
        unordered_map <int, int> m;
        for (int num : nums1)
        {
            ++m[num];
        }
        vector<int> intersection;
        for (int num : nums2) 
        {
            if (m.count(num)) 
            {
                intersection.push_back(num);
                --m[num];
                if (m[num] == 0)
                {
                    m.erase(num);
                }
            }
        }
        return intersection;
    }
};

二叉树的非递归前序遍历

class Solution {
public:
   vector<int> preorderTraversal(TreeNode* root)
{
	vector<int> v;
	stack<TreeNode*> st;
	TreeNode* cp = root;
	while (cp || !st.empty())
	{
		//左路结点
		while (cp)
		{
			v.push_back(cp->val);
			st.push(cp);
			cp = cp->left;
		}
		//左路结点的右子树
		TreeNode* top = st.top();
		st.pop();
		cp = top->right;
	}
	return v;
}
};

连续子数组的最大乘积

#include<vector>
#include<cstdlib>
#include<climits>
#include<iostream>
using namespace std;
//暴力解法
int maxProductSubarray(vector<int>& nums) 
{
    int n = nums.size();
    int maxnum = INT_MIN;
    for (int start = 0; start < n; ++start) 
    {
        //更新cur 下次重新记录start位置的最大乘积
        int cur = 1;
        for (int end = start; end < n; ++end) 
        {
            //计算从start位置开始向后的乘积
            cur *= nums[end];
            if (cur > maxnum)
            {
                maxnum = cur;
            }
        }
    }
    return maxnum;
}
int main() 
{
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; ++i) 
    {
        cin >> nums[i];
    }
    int result = maxProductSubarray(nums);
    cout << result << endl;
    return 0;
}
class Solution {
  public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n + 1), g(n + 1);
        f[0] = g[0] = 1;
        int ret = INT_MIN;
        for (int i = 1; i <= n; i++) {
            int x = nums[i - 1];
            int y = f[i - 1] * x;
            int z = g[i - 1] * x;
            f[i] = max(x, max(y, z));
            g[i] = min(x, min(y, z));
            ret = max(ret, f[i]);
        }
        return ret;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219918.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计网笔记】以太网

经典以太网 总线拓扑 物理层 Manchester编码 数据链路层 MAC子层 MAC帧 DIX格式与IEEE802.3格式 IEEE802.3格式兼容DIX格式 前导码&#xff08;帧开始定界符SOF&#xff09; 8字节 前7字节均为0xAA第8字节为0xAB前7字节的Manchester编码将产生稳定方波&#xff0c;用于…

steam游戏模拟人生3缺少net framework 3.5安装不成功错误弹窗0x80070422怎么修复

模拟人生3在Steam上运行时提示缺少.NET Framework 3.5并出现错误代码0x80070422&#xff0c;通常意味着.NET Framework 3.5功能没有正确启用&#xff0c;或者安装过程中出现了问题。以下是解决这个问题的步骤&#xff1a; 1.启用Windows功能 按下Win R键&#xff0c;输入opti…

【论文学习与撰写】论文里的Mathtype公式复制粘贴,跨文档复制后错码/错位问题的解决

1、描述 问题&#xff1a;论文的草稿已经写好&#xff0c;里面的公式之类的都已经一个个打上去了 但是把草稿里的正文和公式粘贴在另一个文档里的时候&#xff0c;会出些公式格式错误的情况 那该怎么操作保证复制后的公式保持原格式呢 选中复制的内容&#xff0c;在另一个文…

MySQL【知识改变命运】10

联合查询 0.前言1.联合查询在MySQL里面的原理2.练习一个完整的联合查询2.1.构造练习案例数据2.2 案例&#xff1a;⼀个完整的联合查询的过程2.2.1. 确定参与查询的表&#xff0c;学⽣表和班级表2.2.2. 确定连接条件&#xff0c;student表中的class_id与class表中id列的值相等2.…

【c++篇】:解析c++类--优化编程的关键所在(一)

文章目录 前言一.面向过程和面向对象二.c中的类1.类的引入2.类的定义3.类的封装和访问限定符4.类的作用域5.类的实例化6.类对象模型 三.this指针1.this指针的引出2.this指针的特性3.C语言和c实现栈Stack的对比 前言 在程序设计的广袤宇宙中&#xff0c;C以其强大的功能和灵活性…

[k8s理论知识]6.k8s调度器

k8s默认调度器 k8s调度器的主要职责&#xff0c;就是为一个新创建出来的pod寻找一个适合的节点Node。这包括两个步骤&#xff0c;第一&#xff0c;从所有集群的节点中&#xff0c;根据调度算法挑选出所有可以运行该pod的节点&#xff0c;第二&#xff0c;从第一步的结果中&…

Java项目-基于springboot框架的企业客户信息反馈系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

Windows环境下Qt Creator调试模式下qDebug输出中文乱码问题

尝试修改系统的区域设置的方法&#xff1a; 可以修复问题。但会出现其它问题&#xff1a; 比如某些软件打不开&#xff0c;或者一些软件界面的中文显示乱码&#xff01; 暂时没有找到其它更好的办法。

10-Docker安装Redis

10-Docker安装Redis Docker安装Redis 以 Redis 6.0.8 为例&#xff1a; docker pull redis:6.0.8直接pull会出现以下错误 [rootdocker ~]# docker pull redis:6.0.8 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request can…

[Python学习日记-50] Python 中的序列化模块 —— pickle 和 json

[Python学习日记-50] Python 中的序列化模块 —— pickle 和 json 简介 pickle 模块 json 模块 pickle VS json 简介 什么叫序列化&#xff1f; 序列化指的是将对象转换为可以在网络上传输或者存储到文件系统中的字节流的过程。序列化使得对象可以被保存、传输和恢复&#…

3D Slicer 教程二 ---- 数据集

上一章下载3d slicer的软件,这章从加载数据集来弄清楚3dslicer怎么使用. 一. 加载数据集 如果没有数据集,也可用用样本数据. (1) "File" --> "add Data" 可以添加图片文件夹,(试了MP4不行,内镜的视频估计不支持),添加单个图片的话,会出现一些选项, …

C++贪心

前言 C算法与数据结构 打开打包代码的方法兼述单元测试 简介 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法策略。贪心算…

【设计模式系列】抽象工厂模式

一、什么是抽象工厂模式 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。这种模式允许客户端使用抽象的接口来创建一组…

AUTOSAR_EXP_ARAComAPI的5章笔记(17)

☞返回总目录 相关总结&#xff1a;AutoSar AP CM通信组总结 5.7 通信组 5.7.1 目标 通信组&#xff08;Communication Group&#xff0c;CG&#xff09;是由 AUTOSAR 定义的复合服务模板。它提供了一个通信框架&#xff0c;允许在 AUTOSAR 应用程序之间以对等方式和广播模…

第6章 元素应用CSS作业

1.使用CSS对页面网页元素加以修饰&#xff0c;制作“旅游攻略”网站。 浏览效果如下&#xff1a; HTML代码如下&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title>旅游攻略</title><link type"t…

[JAVAEE] 线程安全问题

目录 一. 什么是线程安全 二. 线程安全问题产生的原因 三. 线程安全问题的解决 3.1 解决修改操作不是原子性的问题 > 加锁 a. 什么是锁 b. 没有加锁时 c. 加锁时 d. 死锁 e. 避免死锁 3.2 解决内存可见性的问题 > volatile关键字 (易变的, 善变的) a. 不加…

【Linux】多线程安全之道:互斥、加锁技术与底层原理

目录 1.线程的互斥 1.1.进程线程间的互斥相关背景概念 1.2.互斥量mutex的基本概念 所以多线程之间为什么要有互斥&#xff1f; 为什么抢票会抢到负数&#xff0c;无法获得正确结果&#xff1f; 为什么--操作不是原子性的呢&#xff1f; 解决方式&#xff1a; 2.三种加锁…

git add操作,文件数量太多卡咋办呢,

git add介绍 Git的add命令是用于将文件或目录添加到暂存区&#xff08;也就是索引库&#xff09;&#xff0c;以便在后续的提交&#xff08;commit&#xff09;操作中一并上传到版本库的。具体来说&#xff0c;git add命令有以下几种常见用法&#xff1a; 添加单个文件&#…

4、.Net 快速开发框架:DncZeus - 开源项目研究文章

DncZeus 是一个基于 ASP.NET Core 和 Vue.js 的前后端分离的通用后台管理系统框架&#xff0c;其愿景是成为一个易于使用且功能丰富的 .NET Core 通用后台权限管理模板系统基础框架。项目名称 "DncZeus" 由 "Dnc"(.NET Core 的缩写)和 "Zeus"(古…

CLion和Qt 联合开发环境配置教程(Windows和Linux版)

需要安装的工具CLion 和Qt CLion下载链接 :https://www.jetbrains.com.cn/clion/ 这个软件属于直接默认安装就行&#xff0c;很简单&#xff0c;不多做介绍了 Qt:https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/ window 直接点exe Linux 先c…