扩展3

1、get和post区别

  1. 通常get的数据写在url里,post写在body里
  2. 服务器或浏览器可能会对url长度有限制,那么get的数据也会受限
  3. get会发一个tcp包,post可以发两个,第一次发header,如果是返回100则发送数据;否则则无权限。

2、HTTP部分报文头部

请求头:
accept:诸如text/html application/xml,表示客户端可以接收什么类型
host:服务器域名。一个IP可以有多个域名,使用host字段可以区分一个IP下的不同站点
user-agent:浏览器信息等
accept-encoding:浏览器支持的数据编码方式,gzip
connection:keep-alive
cookie:携带cookie信息

响应头:
content-encoding:文档的编码方法
content-type:text/html,文档的类型
set-cookie:设置cookie
server:Apache,服务器信息
connection:keep-alive

3、链接具体过程

链接是把一些可重定位目标文件(*.o)链接到一起生成可执行文件。

4、红眼睛蓝眼睛问题

岛上有5个红眼睛,95个蓝眼睛。岛民知道红色和蓝色的区别,不能看自己眼睛颜色,不能告诉别人他们眼睛的颜色,如果他知道自己是红眼睛就会挂。一天,一个游客告诉他们所有人“岛上有红眼睛”,那么会发生什么?

5个红眼睛会在第5天一起挂。
假如只有1个红眼睛,那么他马上就知道自己是红眼睛,第1天就会挂。(1)
假如有2个,第1天没事,第2天他们会发现对方没挂,但是他们都只看见1个红眼睛,由(1)可得如果只有1个红眼睛那么第1天就会挂,说明自己也是,那么他们第2天一起挂。
归纳可得,有n个红眼睛,第n天会一起挂。

5、循环右移数组K位

整体反转,翻转前k个,翻转后面。

6、交换两个数

a = a ^ b;
b = a ^ b;
a = a ^ b;

第二行等价b = a ^ b ^ b,那么b = a;第三行等价a = a ^ b ^ a,即a = b
还有一种可能会越界的,但是和上面基本同理:

//可能越界
a = a + b;
b = a - b;
a = a - b;

7、为啥重载不能根据返回类型

因为调用时不能知道需要哪个返回类型。
比如:int add(int a, int b)double max(int a, int b),如果调用时有接收返回值还可以判断,但是如果忽略返回值,那么就不能判断要选择哪个了。

8、股票买卖系列

1.leetcode123:
限制只能买/卖各两次,每次买入前手里不能有股票,问最大收益。
逆序O(n)算出每个从i开始到n的最大差值dis[i],然后解为max{price[i] - min_pre + dis[i]}

2.leetcode309:
买之前手里必须没有持股,卖了之后第二天不能买卖,问最大收益。
dp[i][0]为持股最大收益,dp[i][1]为不持股第二天冻结(当天卖了)最大收益,dp[i][2]为不持股第二天不冻结最大收益。可得状态转移方程如下:

class Solution {
public:
    // 0:持股, 1:不持股第二天冻结, 2:不持股第二天未冻结
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n <= 1)
            return 0;
        vector<vector<int> > dp(n, vector<int>(3));

        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;
        for (int i = 1; i < n; i++) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
            dp[i][1] = dp[i - 1][0] + prices[i];
            dp[i][2] = max(dp[i - 1][2], dp[i - 1][1]);
        }
        return max(dp[n - 1][2], dp[n - 1][1]);
    }
};

3.leetcode188:
限制只能买/卖各k次,每次买入前手里不能有股票,问最大收益。
类似上面做法,dp[i][j][p]表示第i天已经卖了j次是否持股。

class Solution {
public:
    //第i天已经卖k是否持股
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        if(n <= 1) return 0;
        else if(n / 2 <= k){
            int ret = 0;
            for(int i = 1; i < n; i++)
                ret += max(0, prices[i] - prices[i - 1]);
                return ret;
        }
        vector<vector<vector<int> > > dp(n, vector<vector<int> >(k + 1, vector<int>(2, 0)));

        for(int i = 0; i <= k; i++){
            dp[0][i][0] = 0;
            dp[0][i][1] = -prices[0];
        }
        for(int i = 1; i < n; i++){
            for(int j = 0; j <= k; j++){
                if(j - 1 >= 0) dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j - 1][1] + prices[i]);
                else dp[i][j][0] = dp[i - 1][j][0];
                dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j][0] - prices[i]);
            }
        }
        int Max = 0;
        for(int i = 0; i <= k; i++)
            Max = max(Max, dp[n - 1][i][0]);
    
        return Max;
    }
};

9、this指针

调用类的非静态成员函数时,编译器会自动传入一个隐含的参数this,它指向这个对象的地址。

10、LCA倍增

dp[i][k]表示i的第\(2^k\)辈节点(比如dp[i][0]为父节点,dp[i][1]为爷爷节点),所以i的\(2^{k-1}\)辈节点的\(2^{k-1}\)辈节点是i的\(2^k\)辈节点,状态转移方程dp[i][k] = dp[dp[i][k-1]][k-1],初始化dp数组复杂度O(nlogn)。
然后先让u和v跳到同一深度,然后从\(2^{max}\)一直一起往上跳跳到\(2^0\),这时保证了dp[u][0] == dp[v][0]

int lca(int u, int v){
	if(deep[u] < deep[v]){
		swap(u, v);	
	}
	int d = deep[u] - deep[v];
	for(int i = 20; i >= 0; i--){
		if(d & (1 << i))
			u  = dp[u][i];
	}
	if(u == v) return u;
	for(int i = 20; i >= 0; i--){
		if(dp[u][i] != dp[v][i]){
			u = dp[u][i];
			v = dp[v][i];
		}
	}
	return dp[u][0];
}

11、shared_ptr的线程安全

shared_ptr的引用计数是原子的,所以是线程安全的,但是读写指针是不安全的,所以读写时建议加锁。

12、单例模式线程安全

懒汉式:使用时才创建,(如果同时创建会内存泄漏)需考虑线程安全。
饿汉式:直接创建实例,本来就线程安全,因为静态属性初始化在主函数执行之前。

懒汉式:
C++11及以后局部静态变量只会初始化一次,并且是线程安全的

/***
*局部静态变量只会初始化一次,并且是线程安全的
***/
class Single{
public:
    static Single& get(){ //返回的是引用
        static Single instance;
        return instance;
    }
private:
    Single(){}
    ~Single(){}
    Single(const Single& obj){}
    Single& operator = (Single &obj){}
};

直接加锁。

class Single{
public:
    static Single* get(){
	    lock
        if(instance == nullptr){
            instance = new Single;
        }
        return instance;
    }
private:
    static Single* instance;
    Single(){}
    ~Single(){}
    Single(const Single& obj){}
    Single& operator = (Single &obj){}
};

双检查锁DCL可能会失效,因为编译器会重排顺序。m_instance = new Singleton()有三步:开辟对象的内存,初始化构造对象,把内存地址分配给指针。如果乱序,那么当二三两步乱序,那么先分配指针但是还没构造,就切换了,那么m_instance != nullptr但是对象并未构造。

if(m_instance==nullptr){
	Lock lock; //伪代码
	if (m_instance == nullptr) {
	    m_instance = new Singleton();//可能失效
	}
}

饿汉式:

class Single{
public:
    static Single* instance;
    static Single* get(){
        return instance;
    }
    static void del(){
        delete instance;
    }
private:
    Single(){}
    ~Single(){}
    Single(const Single& obj){}
    Single& operator = (Single &obj){}
};
//静态属性初始化在main之前
Single* Single::instance = new Single;

参考资料

一道好玩的逻辑题之蓝眼睛红眼睛

posted @ 2020-07-19 23:00  KirinSB  阅读(151)  评论(0编辑  收藏  举报