总结一下最近遇到的题,无论难易,查漏补缺吧
- 完全二叉树下求取叶子节点:
A:
已知完全二叉树的节点总个数为sum,则树的深度是多少?叶子节点的个数是多少?
最后一层的叶子节点是多少?
解法1:便于求取最后一层的叶子节点个数
Q:
等比数列:前n项和:Sn = 2^n - 1
Sn <= sum 求出层数x,之后剩下的就是最后1层的叶子节点。
则树的深度是:n+1
举例:完全二叉树有2018个节点,求相关问题
2^n - 1 <= 2018
2^n <= 2019
n = 10
则2^10 = 1024 ,剩余995是最后一层的叶子节点个数,树的深度:11
第10层的节点个数:a1*q^(n-1) = 2^9 = 512
第10层剩余的叶子节点个数:512 - (995/2 + 1) = 14
总的叶子节点个数:995 + 14 = 1009
解法2:
利用节点的度来求取
度为0的节点为:n0; 也就是叶子节点的个数
度为1的节点为:n1;
度为2的节点为:n2;
总节点个数 n = n0 + n1 + n2
n0和n2的关系 n0 = n2 + 1;
所以 n = n0 + n1 + n2 = 2*n0 + n1 - 1;
而n1在完全二叉树下非0即1
所以n0 = (n+1)/2
上一个例子的总的叶子节点个数:2019/2 = 1009
-
C++STL容器:vector
vector是动态数组,在堆中分配连续的内存空间,有保留内存,如果减少大小后内存也不会释放,内存是在析构函数中释放的。由于vector拥有一段连续的内存空间,并且起始地址不变,因此它支持随机存取,利用[]操作符。
(1)新增元素:由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝。当给定的内存已满,则需要重新申请一块足够大的内存将原来的数据拷贝到新申请的内存,并且释放之前的旧内存。这些都大大影响了vector的效率。注意:插入新的数据分在最后插入push_back和通过迭代器在任何位置插入, 这里说一下通过迭代器插入,通过迭代器与第一个元素的距离知道要插入的位置, 即int index=iter-begin()。 这个元素后面的所有元素都向后移动一个位置,在空出来的位置上存入新增的元素
(2)删除元素:删除和新增差不多,也分两种,删除最后一个元素pop_back和通过迭代器删除任意一个元素erase(iter)。通过迭代器删除还是先找到要删除元素的位置,即int index=iter-begin();这个位置后面的每个元素都想前移动一个元素的位置。同时我们知道erase不释放内存只初始化成默认值。
删除全部元素clear:只是循环调用了erase,所以删除全部元素的时候,不释放内存。内存是在析构函数中释放的。
ps: 对vector的任何操作,一旦引起空间重新配置 指向原vector的所有迭代器就都失效了。
参考链接:
STL源码剖析—vector
《STL系列》之vector原理及实现(1)size()和capacity()的区别
size():指容器当前拥有的元素个数; capacity():指容器在必须分配存储空间之前可以存储的元素总数。
(2)为什么vector是2倍扩容
因为每次扩容都涉及到原来元素的copy。空间和时间的权衡。
简单来说, 空间分配的多,平摊时间复杂度低,但浪费空间也多。
详细的可以看我的这篇博客:
STL中vector 扩容为什么要以1.5倍或者2倍扩容?
(3)简述可能执行的结果?若出错简述出错原因。vector<int> v; v.push_back(0); auto it = v.begin(); for(int i = 1; i < 100; ++i) v.push_back(i); cout<<*it<<endl;
这就是上面说到的空间重新配置引起的问题
执行的结果就是输出一个不确定的数。
原因:vector是自动扩容的,数据被重新拷贝到新开辟的内存,之前的内存会被释放掉,迭代器已经失效 -
如何判断两个链表相交并求出第一个相交点?
详见:两个单链表相交问题 -
括号匹配问题
详见:括号匹配问题(栈) -
在二叉树中寻找两个节点的最近公共祖先(不存在指向父亲节点的指针)
详见: -
全排列问题(C++ STL中含有函数next_permutation)
详见: -
含有头尾节点的双向链表的节点插入和删除(ps:头尾节点中数值是0)
详见:双向链表的节点插入和删除 -
宏定义和优先级问题
#define num(x) (x*x)
printf("%d %d\n",num(1+2),num(2==2));
我的答案:5 1
正确答案:5 0
宏定义很简单:不存在类型检测,直接等价替换
优先级当时疏忽了。。。
num(1+2) = 1+2*1+2 = 1+2+2 = 5
num(2 == 2) = 2 == 2*2 == 2
*号的优先级要高于==
所以结果是2 == 4 == 2 即 0 == 2 结果是0
优先级
- GetMemory问题
问程序执行完毕会发生什么?存在哪些问题?
void GetMemory(char* p)
{
p = (char*)malloc(100*sizeof(char));
}
int main()
{
char *str = NULL;
GetMemory(str);
strcpy(str,"hello");
cout<<str<<endl;
return 0;
}
1.str还是NULL,传入的是一个指针,p的值虽然发生了改变,
但是str的值并没有发生改变
2.内存泄漏,动态申请的内存并没有释放掉
3.程序直接崩溃
结果如图
详见:GetMemory()问题
- 数组指针和指针数组
已知整型数组a[2][3]={1,8,3,5,6,9},使用下列表达式正确的是:ACE
A int *p = a[0];
B int *p = a;
C int (*p)[3] = a;
D int *p[3] = a;
E int (*p)[3](a);
数组指针:像C中int (*p)[3]是一个数组指针,指向的是一个数组,
所以C、E正确。E中的(a)是赋值的意思,平时用的很少
指针数组:是一个存放指针的数组,像D中 int *p[3] ,[]的优先级要高于*
选项A、B
int *p=a[0]
a[0]是第一行元素的首地址 *a[0] = 1
a[1]是第二行元素的首地址 *a[1] = 5
*p是地址里的内容,p代表地址
a[0]代表地址里的内容,a代表地址,
要形成地址对地址,内容对内容