7.15-7.17 leetcode+数据库

搬家浪费了几天时间,从进行开始继续更新。


首先还是Leetcode:

leetcode:24 两两交换列表

首先虚拟节点是个好东西,可以避免许多头结点的边界问题,应当多用!

思考一下如果需要交换两个节点,比如交换23,则需要知道1,2,3,4四个节点的内存地址。所以非常自然地定义四个ListNode类型的指针变量。分别存储连续的四个地址。交换其实比较简单。2连接4,3链接2,1链接3,最后记得把指针变量2,3交换一下换为原来的顺序,方便下一次交换。

边界问题:需要思考能否移动,在我们这样的算法里面,每一次交换的始终是p2和p3节点,因此只要p3不为null则可以进行交换。下一次交换的条件就是,p3所要到的地方(p4->next)不为空。但是值得注意的是,使用p4->next的时候需要确保p4不是一个野指针,所以应当增加判断。

 ListNode* swapPairs(ListNode* head) {
        if(head == nullptr) return head;
        if(head->next == nullptr) return head;
        ListNode* v_node = new ListNode(-101);

        ListNode* p1 = v_node;
        ListNode* p2 = head;//左边指针
        ListNode* p3 = head->next;//右边指针
        ListNode* p4 = head->next->next;


        while(p3 != nullptr)
        {
            p2->next = p4;
            p1->next = p3;
            p3->next = p2;

            ListNode* tmp;

            tmp = p2;
            p2 = p3;
            p3 = tmp;

            if(p4 == nullptr ||  p4->next == nullptr)
            {
                break;
            }else{
                p1 = p1->next->next;
                p2 = p2->next->next;
                
                p3 = p3->next->next;
                p4 = p4->next->next;   
            }
        }
        return v_node->next;
    }

leetcode 25 k个一组反转链表

这道题目有点困难,主要需要考虑一下几点,首先需要考虑到剩余节点是否够k个节点能够成为一组进行反转,然后反转的过程也就不多说了。每一组之间如何连接,这里我设计了pair对来记录这个信息,然后再对其进行连接。代码如下:

ListNode* reverseKGroup(ListNode* head, int k) {
        vector<pair<ListNode*,ListNode*>> link_together;
        int nums = 0; //已经反转的节点
        //注意最后一组如果节点数不足k则不进行反转
        if(k == 1) return head;
        if(head == nullptr || head->next == nullptr) return head;
        //确保至少有两个元素
        ListNode* p2 = head->next;
        ListNode* p3 = head->next->next;
        //ListNode* v = new ListNode(-101);
        ListNode* p1 = head;
        //需要一个侦察兵
        ListNode* detect = head;
        

        while(detect!=nullptr)
        {
            ListNode* tail = p1;
            nums+=1;
            detect = detect->next;
            if(nums%k == 0)
            {
                
                while(--nums)
                {
                    //每完成一次元素的反转nums--1;
                    p2->next = p1;
                    if(p1->next == p2) p1->next = nullptr;
                    p1 = p2;
                    p2 = p3;
                    if(p3!= nullptr ) p3 = p3->next;
                }
                ListNode* head = p1;
                p1 = p2;
                p2 = p3;
                if(p3!=nullptr) p3 = p3->next;
                link_together.push_back({head,tail});
            }
        }

    for(int i = 0 ; i < link_together.size()-1;i++)
    {
        link_together[i].second->next = link_together[i+1].first;
    }
    link_together[link_together.size()-1].second->next = p1;
    return link_together[0].first;
    }

 官方题解里面有一个很巧妙的递归解法,可以看到这个问题可可以被划分为递归问题,不仅仅是树的结构可以巧妙地运用递归,类似于这样也行,还需要积累。

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode newHead = head.next;
        head.next = swapPairs(newHead.next);
        newHead.next = head;
        return newHead;
    }
}
。

leetcode 138

我的方法过于粗劣了就不展示了,直接进行了模拟,费事费力,在这里默写一下官方的题解

 unordered_map<Node*, Node*> cachedNode;
    Node* copyRandomList(Node* head) {
        if(head == NULL) return NULL;

        //如果
        if(!cachedNode.count(head))
        {
            Node* headNew = new Node(head->val);
            cachedNode[head] = headNew;
            headNew->next = copyRandomList(head->next);
            headNew->random = copyRandomList(head->random);
        }
        return cachedNode[head];
    }

 对某种结构的子结构做某种相同的操作都可以运用这种方法。


今天开始学习极客时间06 MySQL实战45讲。

1. 一条SQL查询语句如何执行的:

这里主要讲讲MySQL服务端发生了什么:

首先是连接器:MySQL是一个Client-Sever架构的软件系统,连接器只要管理和客户端的连接状态和请求。首先是进行密码检查,完事后从库里面读取该用户的权限,值得注意的是一旦连接完成权限会持续到下一次连接,在此期间管理员改变了什么都没用。

长连接和短连接的问题:
这个问题,是由于长时间的长连接使得内存占用过大,可以主动断开连接,或者执行特定语句,初始化连接资源。

查询缓存:顾名思义通过sql语句去缓存中查询是否有相关的结果,有的话直接返回没有再去查询。但是MySQL作为一个业务型数据来讲更新频率很高,缓存使用率不是很高,因此在8.5后缓存已经被弃用。

分析器: 词法分析,挑出关键词,以及比较列名。

优化器:  优化查询路径

执行器:首先是查看当前用户对于该表是否具备权限,调用引擎接口进行查询。

2.一条更新语句如何执行?

首先当一个表有任何更新的时候这个缓存就会失效,这也就注定了MySQL的缓存效率不高。在数据库的更新操作中主要的内容是两个日志:

redo log日志:innoDB独有的日志系统

这个非常经典的图,redo log是先在内存中记录每一次的更新操作,注意他所记录的是物理逻辑上的更新,而不是SQL语句,当内存快满的时候就开始往磁盘写入,或者不忙的时候再写。这个能力叫做crash-safe.

bin log: sever层面的操作日志

日志有以下三点不同。

  1. redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。

  2. redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。

  3. redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志

为了保证两个日志的记录一致性,redolog采用两阶段方式提交,先prepare之后写binlog,binlog写完之后再写redo变成commit状态。

一般用binlog来进行数据库恢复,这是由于Binlog追加写,能储存更长时间的信息。

3. 数据库的事务隔离

三个重要的并行问题:脏读,不可重复读,幻读

四个隔离级别:读未提交(没有任何锁),读已提交(写锁),可重复读(读写锁),串行化(大范围的锁,或者完全串行执行)。

MVCC:多版本并行控制

数据库采用了这样一个多版本控制的策略进行并行管理,具体来说,数据库每一行操作都有多个版本,每一个事务去访问这个版本的时候都会获得一个版本号,然后将自己的版本号和当前活跃的事务版本号进行对比有以下结果。

1.我可以访问当前所有活跃事务的操作,则是读未提交的级别。

2.我只可以访问dead事务的版本,这就是一个读已提交。

3.在2的基础上同时别人不能修改活跃版本的数据,可重复读

4.在3的基础上,加上next-key这样的表锁,串行化

如果当前事务失败了,则回滚回上一事务

一致性原理:大家看到的顺序一样,对于一个顺序,我的最后的操作序没变。

长事务引发的常见危害有:

  1. 数据库连接池被占满,应用无法获取连接资源;

  2. 容易引发数据库死锁;

  3. 数据库回滚时间长;

  4. 在主从架构中会导致主从延时变大。

长事务往往伴随着大颗粒度和对较多资源的占有,就会导致死锁。

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值