文章目录
strlen() 与 sizeof() 的区别
- 在进行字符(数组)串的长度求取问题时,strlen()和sizeof()都可以作为选择,当时两者之间的区别还是值得考究的。
(1)strlen()是函数,sizeof()是运算符;
(2)对于定义字符数组大小的字符数组,sizeof 的值在声明时已经确定了,而 strlen()则根据数组中实际的字符计算大小;如:
char test2[100] = "hello world";
cout << "sizeof test2: " << sizeof(test2) << endl;
cout << "strlen test2: " << strlen(test2) << endl;
(3)对于为给定大小的字符数组,sizeof()计算时会将数组末尾的 ’\0 ‘ 也算上,而strlen()则不进行计算;如:
char test1[] = "hello world";
cout << "sizeof test1: " << sizeof(test1) << endl;
cout << "strlen test1: " << strlen(test1) << endl;
计算二进制中0的个数和1的个数
- 求二进制中1的个数:(当然方法很多,就是写下来选择题容易出现这个)
int sumOfNum1(int n)
{
int count = 0;
while(n != 0)
{
n &= (n-1);
count++;
}
return count;
}
- 求二进制中0的个数:
int fun(int value)
{
int count = 0;
while(!value)
{
count++;
value |= (value+1);
}
}
大小端问题
- 大小端区别以及各自优点,什么时候用
大端优点:符号位在所表示的数据的内容的第一个字节中,便于快速判断数据的正负和大小;
小端优点:低地址放低字节,所以在强制转换时不需要调整字节的内容。而CPU做数值运算时从内存中依次从低到高取数据进行运算,直到最后刷新最高位的符号位,这样的方式会更高效。
用一个例子来说明:
- 上述题目:
(1)如果按照大端模式存储:从低地址到高地址:20 15 08 10
存放时高字节放低地址,输出从低地址到高地址:20 15 08 10,因此结果就是:2015 810
(2)如果按照小端模式存储:从低地址到高地址:10 08 15 20
存放时高字节放高地址,输出从高地址到低地址:08 10 20 15,因此结果就是:810 2015
二叉树的最近公共祖先
在leetcode上看的一道题,觉得不错,记录一下
- 题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
- 题解:主要思想是递归
当我们用递归去做这个题时不要被题目误导,应该要明确一点,这个函数的功能有三个:给定两个节点 p 和 q
(1)如果 p 和 q 都存在,则返回它们的公共祖先;
(2)如果只存在一个,则返回存在的一个;
(3)如果 pp 和 qq 都不存在,则返回NULL. - 具体思路
(1)如果当前节点 root 等于 NULL,则直接返回 NULL;
(2) 如果 root 等于 p 或者 q ,那这棵树一定返回 p 或者 q;
(3)然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left 和 right 表示;
(4)此时若 left 为空,那最终结果只要看 right;若 right 为空,那最终结果只要看 left;
(5)如果 left 和 right 都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root 是他们的最近公共祖先;
(6)如果 left 和 right 都为空,则返回空(其实已经包含在前面的情况中了). - 时间复杂度是 O(n):每个结点最多遍历一次或用主定理,空间复杂度是 O(n):需要系统栈空间
- C++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
class Solution {
public:
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
if(root == NULL)
return NULL;
if(root == p || root == q)
return root;
struct TreeNode* left = lowestCommonAncestor(root->left, p, q);
struct TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left == NULL)
return right;
if(right == NULL)
return left;
if(left && right) // p和q在两侧
return root;
return NULL; // 必须有返回值
}
};
strcpy函数返回值为什么要用char *
- 已知 strcpy 的函数原型:char *strcpy(char *strDest,char *strSrc) 其中 strDest 是目的字符串,strSrc 是源字符串;strcpy 能把 strSrc 的内容复制到 strDest ,为什么还要 char * 类型作为返回值?
char *strcpy(char *strDest, const char *strSrc)
{
if ( strDest == NULL || strSrc == NULL)
return NULL ;
if ( strDest == strSrc)
return strDest ;
char *tempptr = strDest ;
while( (*strDest++ = *strSrc++) != ‘\0’)
;
return tempptr ;
}
- 这是因为返回 char * 原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。
链式表达式如:
int iLength=strlen(strcpy(strA,strB));
C++中重写(覆盖),重载,隐藏之间的区别
- 重写(覆盖):是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。
- 重载:是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
- 重写和重载的区别:
(1)范围的区别:被重写和重写的函数位于两个类中,而重载和被重载的函数位于同一个类中;
(2)参数的区别:被重写函数和重写函数的参数列表一定相同,而被重载函数和重载函数的参数列表一定不同。
(3)virtual 的区别:重写的基类中被重写的函数必须要有 virtual 修饰,而重载函数和被重载函数可以被 virtual 修饰,也可以没有。 - 隐藏和重写、重载有以下几点不同:
(1)隐藏与重载的范围不同:和重写一样,隐藏函数和被隐藏函数不在同一个类中;
(2)参数区别:隐藏函数和被隐藏函数的参数列表可以相同,也可以不同,但是函数名肯定要相同。当参数不相同时,无论基类中的函数是否被 virtual 修饰,基类的函数都是被隐藏的,而不是被重写。
- 牛客上总结的一个解答,感觉不错:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)