备战蓝桥杯第四节

下面是我学习的一些知识点和做的题、每个题都要分析和解释

知识点:

C++的类:(public、protected、private)

1.封装(public、private): 用户代码(类外)可以访问public成员而不能访问private成员;private成员只能由类成员(类内)和友元访问,不能被派生类访问;protected成员可以被派生类对象访问,不能被用户代码(类外)访问。

2.类的另一个特征就是继承,例如:class B : protected A、class B : private A和class B : public A public继承,子类继承原来的父类的成员等级不变。 protected继承,除了public降为protected之外、其余的成员等级不变。 private继承,所有成员的等级都为private。

这里只是简单了解一下;

unordered_map函数、map函数 

头文件#include < unordered_map >、#include < map >

特点:

map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率,效率高、但是占用内存大。

unordered_map: unordered_map内部实现了一个哈希表,因此其元素的排列顺序是杂乱的,无序的。耗时间 unordered_map: 首先unordered_map是一个将key和value关联起来的容器,它可以高效的根据单个key值查找对应的value. key值应该是唯一的,key和value的数据类型可以不相同。

一、map函数会通过红黑树实现键的从小到大排列

1.定义:

    map mp;

2.访问:map mp;

    mp['c'] = 20;

例子:map可以通过定义map::iterator it来查看key和value;

int main()

{

    map mp;

   

    mp['m'] = 20;

    mp['r'] = 30;

    mp['a'] = 40;

   

    //it -> first 是当前映射的键

    //it -> second是当前映射的值

    for(map::iterator it = mp.begin(); it != mp.end(); it++)

    {

        cout << it -> first << " " << it -> second << endl;

    }

   

    return 0;

}

map除了begin()、end()函数,还有:

1.find(key)返回键为key的映射的迭代器,时间复杂度为O(logN),N为map中映射的个数。

例子:map::iterator it = mp.find('b');

2.erase()有两种用法:删除单个元素,删除一个区间内的所有元素

    2.1例如:删除key为‘b’的:

        map::iterator it = mp.find('b');

        mp.erase(it);

    2.2例如:mp.erase(first,last),其中first为需要删除的区间的起始迭代器,而last则为需要删除的区间的末尾迭代器的下一个地址,也即为删除左闭右开的区间[first,last)

        map::iterator it = mp.find('b'); //令it指向键为b的映射

        mp.erase(it,mp.end());//删除从key为b的到最后的所有map映射

       

3.size()用来获取map中映射的对数,时间复杂度为O(1)

4.clear()用来清空map中的所有元素,复杂度为O(N),其中N为map中元素的个数

二、unordered_map存储元素时没有进行排序,只是根据key的哈希值,将元素存在指定位置,所以根据key查找单个value时非常高效,平均可以在常数时间内完成。

unordered_map查询单个key的时候效率比map高,但是要查询某一范围内的key值时比map效率低。

可以使用[]操作符来访问key值对应的value值。

例如:

1.定义

    string key="123";

    int value=4;

    unordered_map unomap;//创建一个key为string类型,value为int类型的unordered_map

    unomap.emplace(key, value);//使用变量方式,插入一个元素

    unomap.emplace("456", 7);//也可以直接写上key和value的值

    cout<

2.遍历

    for(auto x:unomap)//遍历整个map,输出key及其对应的value值

        cout<

    for(auto x:unomap)//遍历整个map,并根据其key值,查看对应的value值

        cout<

函数:

除了begin()、end()、empty()、size()、find(key)、erase()、clear()。

还有:

1.count(key),在容器中查找以key为键的键值对的个数

2.insert()容器中添加新键值对。

3.swap(),交换2个容器存储的键值对,前提是必须保证这2个容器的类型完全向相等,例如: m1.swap(m2);交换m1和m2

下面是我做的一些题:

 原题:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

一、暴力解法

class Solution {

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {

        ListNode prehead = new ListNode(-1);//建立一个新的节点

        ListNode prev = prehead;//定义一个prev指向该节点

        while (l1 != null && l2 != null) {//只要l1和l2链表的值存在就继续循环、最后一个

            if (l1.val <= l2.val) {

                prev.next = l1;

                l1 = l1.next;

            } else {

                prev.next = l2;

                l2 = l2.next;

            }

            prev = prev.next;//上面给prev指向了下一位后、pre移动到下一位

        }

        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可

        prev.next = l1 == null ? l2 : l1;//l1 == null ? l2 : l1;为判断语句,返回l1或者l2;

        return prehead.next;//返回prehead节点的下一位,也就是我们合并后的链表

    }

}

//思路:新建立一个prehead节点、一个prev指针。指针指向节点,在用while循环用l1和l2遍历两个链表、直到一个链表为空、同时在这个过程中把两个链表的最小的元素当做prev指针的下一位(哪个链表被遍历了,它对应的指针就前进一位),然后prev在前面得到了值,移动到下一位不断前进。最后我们一定会遍历完一个链表、然后此时prev的下一位指向没有遍历完的链表,这样两个链表就合并了;

二、递归

class Solution {

public:

    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {//返回合并的链表

        if (l1 == nullptr) {

            return l2;

        } else if (l2 == nullptr) {

            return l1;

        } else if (l1->val < l2->val) {//返回当前链表指针最小的值的地址,同时小的那位的下一位是另外一个链表的地址,最后用递归得到整个合并的链表、返回最开始比较最小的链表的头节点。

            l1->next = mergeTwoLists(l1->next, l2);

            return l1;

        } else {

            l2->next = mergeTwoLists(l1, l2->next);

            return l2;

        }

    }

};

//特点:没有移动指针,只是给出当前链表的值与另外一个链表的值,就可以合并链表,最后通过递归得到整个合并的链表

//思路:把小的链表的下一位地址作为与另外一个链表的值进行下一次比较、通过依次的返回最小的链表的值的地址,我们用返回的链表的地址作为当前的下一位;最后递归回来,我们就自然合并了链表。

原题: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

1.数组

class Solution {

public:

    ListNode* middleNode(ListNode* head) {

        vector A = {head};//定义一个数组用来存放结构体指针

        while (A.back()->next != NULL)//A.back()表示当前数组的最后一位,由于现在A的只有一个元素,while循环的条件是最后一位也就是现在的第一位的下一位地址不为空,则在while循环里:我们不断向while循环放入元素,while的条件语句也会改变,最后整个链表的地址都被放入了数组A。

            A.push_back(A.back()->next);

        return A[A.size() / 2];//返回数组的中间元素,也就是链表的中间元素

    }

};

//思路:定义一个数组A,用于存放链表的地址信息,最后取数组的中间元素作为返回值,就返回了链表的中间元素。

//核心代码(把链表放入数组):

            while (A.back()->next != NULL)//A.back()表示当前数组的最后一位,由于现在A的只有一个元素,while循环的条件是最后一位也就是现在的第一位的下一位地址不为空,则在while循环里:我们不断向while循环放入元素,while的条件语句也会改变,最后整个链表的地址都被放入了数组A。

            A.push_back(A.back()->next);

二、迭代

class Solution {

public:

    ListNode* middleNode(ListNode* head) {

        int n = 0;//定义一个n用来记录链表的大小

        ListNode* cur = head;//定义一个指针,用于遍历数组

        while (cur != nullptr) {//遍历数组

            ++n;

            cur = cur->next;

        }

        int k = 0;//定义一个k用于记录我们遍历到链表中间时,返回链表的元素

        cur = head;

        while (k < n / 2) {

            ++k;

            cur = cur->next;

        }

        return cur;//返回cur指针指向的中间元素

    }

};

//思路:通过一个指针,遍历同一个链表,得到该链表的大小后,在遍历到链表的中间值返回就可以了

//核心代码:

while (cur != nullptr) {//遍历数组

            ++n;

            cur = cur->next;

        }

        int k = 0;//定义一个k用于记录我们遍历到链表中间时,返回链表的元素

        cur = head;

        while (k < n / 2) {

            ++k;

            cur = cur->next;

        }

三、快慢指针

class Solution {

public:

    ListNode* middleNode(ListNode* head) {

        ListNode* slow = head;

        ListNode* fast = head;//定义快慢指针:fast、slow

        while (fast != NULL && fast->next != NULL) {

            slow = slow->next;

            fast = fast->next->next;

        }

        return slow;

    }

};

//定义快慢指针,由于快的速度是慢的两倍,因此慢指针的路程是快指针的一半,当快指针指向末尾时,慢指针指向链表的中间元素

//核心代码:

while (fast != NULL && fast->next != NULL) {

            slow = slow->next;

            fast = fast->next->next;

        }

原题:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

一、栈

class Solution {

public:

    vector reverseBookList(ListNode* head) {

        stack stk;//定义一个栈stk,用于存放链表的值

        while(head != nullptr) {

            stk.push(head->val);

            head = head->next;

        }//向栈中放入链表的元素

        vector res;//定义一个数组作为返回值

        while(!stk.empty()) {//把栈里面的元素全部放入数组

            res.push_back(stk.top());//依次取出栈的值放入数组中

            stk.pop();

        }

        return res;

    }

};

//利用栈的性质,做到反转链表的值,并用数组返回。(还可以反转链表的、反转数组、字符串等等)

二、递归

class Solution {

public:

    vector reverseBookList(ListNode* head) {

        recur(head);//取head作为链表的头结点,把链表的值反转放入数组中

        return res;

    }

    vector res;//定义一个数组、作为返回值

    void recur(ListNode* head) {//定义一个修改res数组的值的没有返回值的数组,作用是通过递归放入链表的值到数组。

        if(head == nullptr) return ;

        recur(head->next);//取链表的下一个地址开始作为头结点

        res.push_back(head->val);//取链表的值放入res数组中

    }

};

//定义一个全局的数组、一个函数、最后主程序调用函数解决问题。

//函数recur:通过递归,从最后面把链表的值放入数组中。

//上面很好的解释了递归的本质是栈

原题:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {

public:

    bool isValid(string s) {

        int n = s.size();

        if (n % 2 == 1) {

            return false;

        }//如果只有一个字符,那么肯定是错误的

        unordered_map pairs = {//定义key和value都为char类型,方便把它们放入栈时进行比较判断

            {')', '('},

            {']', '['},

            {'}', '{'}//根据栈的性质,我们把后括号作为key、前括号作为value

        };

        stack stk;

        for (char ch: s) {//for的增强循环、作用是把字符串前括号放入栈中,如果检测到unordered_map有对应的后括号,就判断现在栈的top的值是否是对应的前括号

            if (pairs.count(ch)) {//判断当前字符是否存在于栈中

                if (stk.empty() || stk.top() != pairs[ch]) {//判断栈为空、或者栈顶端的value与当前后括号是否相等、相等就对应,则说明则对括号是正确的

                    return false;

                }

                stk.pop();

            }

            else {

                stk.push(ch);//把字符串的值依次放入栈,但是只放入了前括号

            }

        }

        return stk.empty();//如果给的是空就返回True、括号全部对应,则栈为空

    }

};

//思路:建立一个映射(unordered_map)pairs,来当做我们对括号是否对应的标准,根据栈的性质、key为后括号、value为前括号,我们通过循环把字符串放入栈中、判断是否是正确括号:在这个过程中、我们只放入前括号、一旦检测到后括号、我们就拿映射去做对比,因为根据括号的正确顺序和栈的性质、如果检测到后括号,那么此时栈顶的字符一定是对应的前括号。最后在栈中,不断放入、删除字符。最后栈为空,就说明括号的顺序正确。

//核心代码:

for (char ch: s) {//for的增强循环、作用是把字符串前括号放入栈中,如果检测到unordered_map有对应的后括号,就判断现在栈的top的值是否是对应的前括号

            if (pairs.count(ch)) {//判断当前字符是否存在于栈中

                if (stk.empty() || stk.top() != pairs[ch]) {//判断栈为空、或者栈顶端的value与当前后括号是否相等、相等就对应,则说明则对括号是正确的

                    return false;

                }

                stk.pop();

            }

            else {

                stk.push(ch);//把字符串的值依次放入栈,但是只放入了前括号

            }

路很漫长、愿我坚持不懈。

你可能感兴趣的:(蓝桥杯,职场和发展)