【计划执行报告】Day5 04-04 C++第10章部分面试题(续)&PCA降维原理推导&图像处理初探

Day5 04-04 C++第10章典型面试题(续)&PCA降维原理推导&图像处理初探

这是我:计划执行的第5

今天是灰色的一天,默哀之余仍需努力奋斗!

1.今日感想

  1. 周末相对工作日来说时间可以集中一些,划分相对简单,但是目前的问题是:布置的任务总是不能按时完成,有些任务(比如较复杂的数学推导,机器学习报告)根本不知道要花多久(总不能到时间没做完就不做了吧,毕竟是要展示的);
  2. 饭后休息半个小时真的很有必要,的确会精神很多,提高效率;
  3. 计划一定要在写blog前完成,毕竟后者耗时更长,很可能写完blog后很可能一时半会儿搞不清明天要做啥了;
  4. 线性代数、概率统计真的得好好回顾一下,只可惜书在学校,网上找不到对应版本的电子书(主要是不方便翻阅);
  5. 像竞赛训练这个板块,我只留了1个小时,但是对于陌生且较为复杂的问题(最近今天都是),我1小时内顶多理解原理,没时间编写;为此,我的想法是:今日理解,明日实现(或许这么做比刚看完原理就实现更好);
  6. 关于计划执行报告的写法:①我收回昨天的观点,目前还是决定不把内容单独成篇,一天中学了什么,就把总结写在当天的执行报告中,尽管这会使知识较为零碎,但暂不考虑,也没精力考虑;等到某一个知识体系学习得较为全面时,可以单独写一篇文章(时间充足的前提下),这时候就可以大面积地copy执行报告中的相关内容,从而大量节省时间;②标题突出一天中各个主要事件(不超过三个);③关于笔记:大多数情况下我会边学边记(Win10自带sticky Notes挺好用的),部分也会在写blog时记录下来

2.计划执行报告

2.1近期计划(03-31-04-12)

1.准备4月10日的机器学习最终报告——《畅想无监督学习》;查找文献与知识补充:《机器学习——算法视角》、数学知识
2.完成专业课的作业(流体机械能转化、生物质能,新能源热利用可以往后稍稍);
3.备战蓝桥杯,为此:①每天1h左右的刷题或者典型题攻克;②知识补充:《程序员的面试笔记:C/C++、算法、数据结构篇》

2.2今日计划表

04-04

2.3实际时间分配

今天洗漱时间超了些时(上厕所+多记了10min),明天注意!

图1 时间分配
图2 目标达成情况

3.C++程序设计基础知识梳理&典型面试题(续)

还剩下几个典型面试题(位运算与内存分配相关),不多了(如果在明早能完成的话就补到)

3.1 程序的编译和执行

【知识梳理】

  • 过程:源程序–>预处理–>编译–>汇编–>链接–>可执行文件
  • 预处理器除了处理#开头的代码行外,还做了:①处理预定义的宏:例如__DATE__、__FILE__;②处理预注释:用空格替代注释;③处理三元符:如??=替换成"#",??/替换成""(历史遗留问题,老键盘上没有”#“或"^")
  • 编译器对预处理过的代码进行词法分析、语法分析和语义分析,将符合规则的程序转换成等价的汇编代码
  • 汇编器将汇编代码翻译成可识别的机器指令
  • 链接器标准库文件、目标代码和其他目标文件链接到一起

【典型面试题】

  1. 简述#include<>和#include""的区别
  2. 简述#与##在define中的作用
  3. 简述assert断言的概念

3.2 变量

【典型面试题】

  1. 写出下面代码执行后i、j、m、n的值

    int i=10,j=10;
    int m=(i++)+(i++)+(i++);
    int n=(++j)+(++j)+(++j);
    

    考察了i++与++j的区别,答案:

    VS2005下:i=13;j=13;m=30;n=39
    GCC下:i=13;j=13;m=30;n=37

    这说明编译器不同,对同一个表达式的处理顺序可能会有差异。

  2. 简述C++的类型转换操作符
    答:
    static_cast:①基本类型转换②在对象指针之间的类型转换时,可以将父类指针转变为子类指针(当父类指针指向父类对象时转换不安全),也可以将子类指针转换为父类指针;③不相关的类的指针无法相互转换

    dynamic_cast:①只能用于对象指针之间的类型转换;②父类指针–>子类指针过程中会对其背后的对象类型进行检查以确保类型完全匹配,static_cast则不会;③当父类指针指向子类对象,且父类中包含虚函数时,转换才能成功,否则返回NULL(对引用则是抛出异常)

    const_cast:一般情况下,无法将常量指针直接赋值给普通指针,因此通过它可以移除常量指针的const属性,实现const指针–>非const指针转换

    reinterpret_cast:①将一个类型的指针转换为另一个类型的指针,不论两者是否有继承关系;②可以把一个指针转换为一个整数,或把一个整数转换为一个指针;③常用于不同函数指针之间的转换

  3. 简述静态全局变量的概念
    答:
    静态全局变量通过在全局变量前加个static形成。与普通全局变量的区别:
    ①静态全局变量通常在源文件中声明和定义,不能使用extern关键字将其导出,作用域仅限定于定义静态全局变量所在的文件内部;普通全局变量的作用域为整个工程,在头文件中用extern声明,并在源文件中定义;
    ②头文件中声明静态全局变量的同时会初始化(无论是否显式初始化),相当于在头文件中同时完成声明和定义;普通全局变量不能在头文件中定义;
    ③若多个源文件都是用#include包含了定义静态全局变量的头文件,那么静态全局变量在各个源文件中都有一份单独的copy,且初始值相同,copy之间相互独立

3.3 条件语句与循环语句

【典型面试题】 巧打乘法口诀表
描述:编写一个函数,接收一个整形参数n表示输出规模,要求只用一重循环输出乘法口诀表的全部内容,并且程序中不能使用任何条件语句。

思路 除法与取余的妙用

#include<iostream>
using namespace std;
//一重循环、禁用条件语句 
void print(int n){
	int row=1,col=1;
	char flag[3]=" \n"; //注意转义字符只占1个字节
	while(row<=n){
		cout<<row<<"*"<<col<<"="<<row*col<<flag[col/row];
		int tmp=col%row+1;
		row+=col/row;
		col=tmp;	
	}
}

int main(){
	print(9);
	return 0;
}

3.4 宏定义与内联

【知识梳理】

  1. 对于宏定义:
    1) 所谓的宏函数实际上不是函数,只是形式与用起来像函数而已
    2)宏函数的机理是在预处理阶段将宏定义原原本本地按照字符串进行替换,因此,与普通函数相比,宏函数省去了函数调用过程,节省了函数调用的开销
    3)在宏定义中,最好将参数加上括号,这样在替换时保证括号内的表达式优先运算
    4)使用宏定义时尽量不要将++x这类表达式作为参数

  2. 对于内联函数:
    1)inline不能出现在函数的声明前
    2) 确保内联函数的函数体十分简单
    3)不建议将构造函数定义为内联函数,因为构造函数会隐式调用基类的构造函数,并初始化成员变量,使得代码展开比想象中大得多

【典型面试题】 简述内联函数与宏定义的区别
答:
相同处
①目的相同:都能够节省频繁的函数调用过程中的时空开销,提高程序执行效率;
②实现类似:都是通过将函数调用替换成完整的函数体;
区别:
①宏定义仅仅是字符串的替换,而内联函数是个函数,具有函数的基本性质,因此内联函数可以像普通函数一样调试,而宏定义不能;
②内联函数和宏定义的代码展开发生在不同阶段(分别是编译阶段和预处理阶段),因此许多编译阶段的工作只对内联函数有效,例如类型安全检查和自动类型转换;
③内联函数作为类的成员函数时,可以访问类的所有成员,this指针也会被隐式地正确使用,而宏定义实现不了;

3.5 sizeof的使用

【知识梳理】

  1. sizeof不是一个函数,而是一个单目运算符
  2. sizeof的操作数可以是类型名(如sizeof(int)),也可以是表达式
  3. 将数组名作为sizeof的操作数可以获得整个数组所占的空间,但如果将数组名作为实参传递给子函数,那么子函数的形参对应的参数已变为指针,此时sizeof获取的只是指针所占的字节数
  4. 不要用sizeof计算字符串的长度!用strlen()来算!
    int main(){
    	char s[20];
    	cin>>s; 
    	cout<<strlen(s)<<endl;//字符串长度(除去'\0') 
    	cout<<sizeof(s)<<endl; //永远为20  
    	return 0;
    }
    
  5. 结构体sizeof的计算结果必须是结构体中占用空间最多的成员所占空间的整数倍
  6. 存在结构体嵌套时的数据对齐,要以结构体中最深层的基本数据类型为准

【典型面试题】不能使用sizeof计算的表达式
答:
位域声明结构体中的成员;②函数名;③无返回值或返回值为void的函数

struct baby{ //位域
	unsigned int gender:1;
	unsigned int weight:5;
	unsigned int week:7;
	unsigned int blood:3;
	unsigned int height:8;
};
int triple(int num){
	return 3*num;
}
void show(){
	cout<<"hello"<<endl;
}
int main(){
	sizeof(baby);//4
	//sizeof(baby.gender); //CE
	sizeof(triple); //CE
	sizeof(triple(3)); //4
	sizeof(show()); //warning,结果为1 
	return 0;
}

3.6 内存分配

还存在点理解问题

3.7 位运算

【典型面试题】

  1. 不使用临时变量交换两个数(两种解法)

    方法1 加减法实现

    void swap_1(int& a,int& b){
    	a=a-b;
    	b=a+b;
    	a=b-a;
    }
    
    

    方法2 异或操作实现

    
    void swap_2(int& a,int& b){
    	a=a^b;
    	b=a^b; //b=a^b^b=a
    	a=a^b; //a=a^b^a=b
    }
    
    
  2. 计算二进数中1的个数

    知识点:与操作和移位操作
    方法1

    //通过num&1取二进制末位,不断右移 
    int binCount_1(int num){ 
    	int ans=0;
    	while(num){
    		if(num&1)
    			ans++;
    		num=num>>1;
    	}
    	return ans;	
    }
    

    方法2

    //从后往前数1的个数:
    //利用num-1&num除去最后一位1
    //直到num为0 
    int binCount_2(int num){
    	int count=0;
    	while(num){
    		count++;
    		num=num&(num-1);//除去最后一个1 
    	} 
    	return count;
    }
    

    一个具体实例流程帮助理解:

  1. 将二进制数倒数第M位的前N位取反
    思路:准备一个变量x=1–>将x左移N位–>x=x-1–>将x左移M位–>将二进制数和x进行异或运算

    int getNum(int num,int m,int n){
    	int x=1;
    	x=x<<n;
    	x-=1;
    	x=x<<m;
    	return num^x;
    }
    

    一个具体实例流程帮助理解:

  2. 找出人群中唯一的“单身狗”
    描述:一个数组中除了一个数字只出现一次外,其余数字均出现偶数次,要求只扫描数组一次,找出这个只出现一次的数字。
    思路:异或操作去重

    int getSingleDog(int* a,int n){
    	int ans=0;
    	for(int i=0;i<n;i++)
    		ans^=a[i];
    	return ans;		
    }
    
  3. 找出人群中三个“单身狗”中的任意一个
    描述:一个数组中除了三个数字只出现一次外,其余数字均出现偶数次,要求用最小的时间复杂度找出这三个数字中的任意一个。
    思路:将三个数分到不同的两个组中,通过异或操作去重,难点在于如何将三个数分开
    关于pair的使用

    //寻找三个中的任意一个 
    #define BIT 32 
    int getAnySingleDog(int* a,int n){
    	int cluster=1;
    	int m=0;
    	int ans1=0,ans2=0;
    	pair<int,int> p1,p2; //(异或值,元素个数)
    	for(int m=0;m<BIT;cluster=1<<++m){
    		p1.first=0,p1.second=0;
    		p2.first=0,p2.second=0;
    		for(int i=0;i<n;i++){
    			if(a[i]&cluster){
    				p1.first^=a[i];
    				p1.second++;
    			}
    			else{
    				p2.first^=a[i];
    				p2.second++;
    			}
    		}
    		if(p1.first==0||p2.first==0)
    			continue; //未分开 		
    		return p1.second&1?p1.first:p2.first;
    	}
    }
    
    int main(){
    	int a[]={2,3,9,13,3,2,5};
    	int n=sizeof(a)/sizeof(int);
    	cout<<"singleDog is "<<getAnySingleDog(a,n); //9
    	return 0;
    }
    

3.8 main函数

【知识梳理】

  1. main函数不是整个程序的入口,实际上在main函数执行前已经做了一些初始化工作,main函数执行之后也有一些扫尾工作
  2. 按照C99标准,main函数有两种形式,且返回值都是int类型,尽管void main()在一些编译器上也行,但这是不标准的
    int main()//形式1
    int main(int argc,char* argv[])//形式2
    
  3. 关于形式2的形参说明:第一个参数argc是argument count的缩写,整数类型,表示通过命令行输入的参数个数;第二个参数argv是argument value的缩写,字符串指针数组类型,其中argv[0]是程序的名字,其余元素为通过命令行输入的参数

【典型面试题】

  1. 简述main函数执行前后都发生了什么?
    答:main函数第一行代码执行之前会调用全局对象和静态对象的构造函数,初始化全局变量和静态变量;main函数最后一行代码执行之后会调用在atexit中注册的函数,并且调用顺序与注册顺序相反(函数注册中使用了栈)。

4.蓝桥杯竞赛模拟赛:竞赛经验收获

  1. 当数据量来到 1 0 5 10^5 以上时,O( N 2 N^2 )的算法就不可行了,复杂度最多为O( N l o g N NlogN );简单的判断方法:题目没有提示时,看最内层循环代码执行的次数是否超过 1 0 9 10^9
  2. 貌似难题的复杂度大多为O( N l o g N NlogN ),而且与动态规划沾点边

5.机器学习PCA原理推导

本来想写完这个类似算法流程图后开始编程的,然后发现自己好多数学概念不清楚(怪我没打好数学基础),导致花了很多时间理解概念,不过基本原理已经推出来了
推导图
参考的一些数学知识

6. Python3图像处理初探

我把这部分时间归为了“创造性开发”中,实际上是为机器学习报告打点技术基础,第一次把图像视作矩阵来进行操作,通过查阅一系列命令用法,做了几个简单的图像处理操作。
先列出我的收获:通过几次调试的失败我认识到了PNG格式的图片颜色标准为RGBA,而JPEG格式的图片的为RGB,因此对于不同类型的图片,每个像素的维度会有所区别(PNG为4,JPEG为3)。

  1. 反色
    我把上面PCA推导的笔记进行了简单的反色处理 O(NM)
    反色处理
  2. 图像等比例放大
    放大图像n倍(n为整数),我的思路就是把每个像素扩展成n*n的阵列,每个小阵列中的像素颜色与原始的像素颜色一致,比如我把壁纸像素放大两倍后的结果:

看到结果时,我开心极了,以为自己找到了让图像更清晰的一种方式(按照我的设想应该不会改变清晰度,也就是说放大之后与放大之前看着一样清晰),但是后面的实验结果让我意识到自己在想peach。。。
我把一个小图标放大了100倍,结果如下:

卧槽,反而不清晰了!!但实际不是,仔细想想,我做的事无非是把原本的一个像素扩大了100倍,那么并没考虑像素与像素之间的颜色过渡,因此才感觉这么的不自然(马赛克画质)。
下一步的优化或许可以考虑像素之间的渐变?
不管怎样,也算有所收获吧~

最后贴上实验所用代码:

from skimage import io,data,data_dir
# from PIL import Image
import  numpy as np

def load_image(path):
    return io.imread(path)

def inv_color(rgb):
    return np.array([255, 255, 255]) - rgb

def img_zoom(data, times):
    height = data.shape[0]
    width = data.shape[1]
    new_img = np.zeros((height*times, width*times, 4), np.uint8)
    for i in range(height):
        for j in range(width):
            rgb = data[i][j]
            ii = times * i
            jj = times * j
            for k in range(times):
                new_img[ii+k][jj:jj+times] = rgb

    # print(new_img.size)
    io.imshow(new_img)
    return new_img


desktop = r'C:\Users\****\Desktop'  # 反正就是桌面路径啦
if __name__ == '__main__':

    img_dir = desktop + r"\timo.ico"
    #  img_dir = data_dir + "\Chelsea.jpg"
    img = load_image(img_dir)
    # print(img_dir)
    # print(np.shape(img))
    # io.imshow(img)
    img = img_zoom(img, 100)
    io.imsave(desktop + "\out1.png", img)
    # io.show()

明天也要加油~

发布了6 篇原创文章 · 获赞 2 · 访问量 144

猜你喜欢

转载自blog.csdn.net/weixin_42430021/article/details/105319369