C++面经知识点总结
1. C++中的new和构造函数的关系(对象的实例化有几种形式,分别如何实现?)
答: 有两种,分别为静态建立类对象和动态建立类对象,且两种方式内存分配的位置以及构造函数调用也都是不同的。A a;这一种是静态的,空间的开辟以及调用构造函数进行变量初始化是有编译器完成,以及后面的调用析构函数进行释放也是有编译器完成,所以静态的过程是由编译器完成的。A* ptr=new A;这种是动态的,是使用new运算符将对象建立在堆空间中。这个过程分为两步,第一步是执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。
附比较详细的链接: linkhttps://blog.csdn.net/tkp2014/article/details/48846715
2. 常用函数默写
1)类的四个构造函数
一定好好理解,每一个功能函数的作用,以及他们之间的区别。特别是复制构造和赋值函数的区别。
//类的原型为:
class String
{
public:
//构造函数
String(const char *str=NULL);
//析构函数
~String(void);
//复制构造函数
String(const String &other);
//赋值函数
String & operate = (const String &other);
private:
//用于保存字符串
char *m_data;
};
String::String(const char *str) //构造函数
{
if (str == NULL)
{
m_data = new char[1];
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length + 1];
strcpy(m_data, str);
}
}
String::~String() //析构函数
{
delete[]m_data;
}
String::String(const String &other) //复制构造函数
{
if (other.m_data == NULL)
{
m_data = new char[1];
*m_data = '\0';
}
else
{
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
}
}
String & String::operate = (const String &other) //赋值函数
{
//检查是否是自赋值
if (this = &other)
return *this;
//释放原有的内存资源
delete [] m_data;
//分配新的内存
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
return *this;
}
2)字符串中的几个函数
a. atoi函数,字符串转整型数字
int atoi(char *str)
{
if (str == NULL)
return -1;
int result = 0;
bool flag = false;
if ((*str >= '0'&&*str <= '9') || (*str == '-' || *str == '+'))
{
if (*str == '-')
flag = true;
str++;
}
while (*str != '\0')
{
if (*str<'0' || *str>'9')
break;
else
result = result * 10 + (*str++ - '0');
}
if (*str != '\0')
return -2;
return (flag ? -result : result);
}
b. itoa函数,整型数字转字符串
几种不同的输入形式,气质实质是一样的,当输入形式不同时,经过稍微修改即可。
void itoa(int number, char *string)
{
int num = number, power = 0;
//避免number%10+'0';number/=10这种形式还需要进行翻转
for (power = 1; num > 10; num /= 10)
power *= 10;
for (; power > 0; power /= 10)
{
*string++ = number / power + '0';
number %= power;
}
*string = '\0';
}
string itoa(int number)
{
string str = "";
int num = number, power = 0;
//避免number%10+'0';number/=10这种形式还需要进行翻转
for (power = 1; num > 10; num /= 10)
power *= 10;
for (int i=0; power > 0; power /= 10)
{
str[i++] = number / power + '0';
number %= power;
}
int len = str.size();
str[len] = '\0';
return str;
}
char* itoa(int number)
{
char* str =NULL;
int num = number, power = 0;
//避免number%10+'0';number/=10这种形式还需要进行翻转
for (power = 1; num > 10; num /= 10)
power *= 10;
for (int i = 0; power > 0; power /= 10)
{
*str++ = number / power + '0';
number %= power;
}
*str = '\0';
return str;
}
c. 手写strcpy函数
如果考虑重叠情况的话,就是下边的两种情况,如果不考虑的话,就只有else里面的内容就可以,挺简单的。
char *strcpy(char* dst, char* src)
{
assert((dst != NULL) && (src != NULL));
char *ret = dst;
int len = strlen(src)+1;
//考虑两种情况--是否有重叠 ?这个重叠问题,指针不能直接解决吗?
if (dst > src || dst < src + len) //有重叠,需要从后向前赋值
{
dst = dst + len - 1;
src = src + len - 1;
while (len--)
{
*dst-- = *src--;
}
}
else //没有重叠,可以从前向后正常进行
{
while (len--)
{
*dst++ = *src++;
}
}
return ret;
}
3)两个排序算法
a. 归并排序
具体的写法可能会有很多种,但是思想都是一样的:归并=递归+合并。
void merge(int *num, int *res, int low, int mid, int high)
{
int k = low;
int left = low, right = mid + 1;
while (left <= mid && right <= high)
{
if (num[left] <= num[right])
res[k++] = num[left++];
else
res[k++] = num[right++];
}
while (left <= mid)
res[k++] = num[left++];
while (right <= high)
res[k++] = num[right++];
}
void merge_sort(int *num, int low, int high, int *res)
{
if (low >= high)
return;
int mid = low + (high - low) / 2;
merge_sort(num, low, mid, res);
merge_sort(num, mid + 1, high, res);
merge(num, res, low, mid, high);
for (int i = low; i <= high; i++) //这一个很关键呀,要更新原数组的值
num[i] = res[i];
}
b.快速排序
有一点很是奇怪!!
int Partition(int *data, int low, int high)
{
int pivot = data[low];
while (low < high)
{
while (low < high&&data[high] >= pivot) //出while循环的情况可能有两个
high--; //1)找到小的数;2)low=high
//low=high时,没有必要进行任何操作
if(low<high) //判断是情况1)吗,是的话进行操作
data[low++]=data[high];
while (low < high&&data[low] <= pivot) //同上
low++;
if(low<high)
data[high--]=data[low];
}
data[low] = pivot;
return low;
}
void Qsort(int *data, int low, int high)
{
if (low < high)
{
int mid = Partition(data, low, high);
Qsort(data, low, mid-1);
Qsort(data, mid + 1, high);
}
}
4)树的两个操作
a. 求解树的高度/宽度
用队列这种数据结构可以比较容易的遍历的树的层数和每一层的宽度------即树的高度和宽度就都有了。
int DepthTree(TreeNode* root)
{
if (root == NULL)
return 0;
int level = 0; //统计树的层数(深度)
int width = 1; //统计树的宽度
int cur_width = 0;
int pre_width = 1;
queue<TreeNode*>que;
que.push(root);
TreeNode* cur = NULL;
while (!que.empty())
{
level++;
while (pre_width)
{
cur = que.front();
que.pop();
if (cur->lchild)
que.push(cur->lchild);
if (cur->rchild)
que.push(cur->rchild);
pre_width--;
}
cur_width = que.size();
width = (cur_width > width) ? cur_width : width;
pre_width = cur_width;
}
return level; //这是返回的层数(树的高度)
//return width;//这是返回的树的最大宽度(树的宽度)
}
b. 非递归判定一棵树是否是平衡二叉树
分析:后序遍历的思想可以避免重复的操作(结点高度的计算)--遍历到NULL为止它会递归到树的叶子结点(NULL为止),然后自下向上的进行结点度的计算判断
//后序遍历的思想可以避免重复的操作(结点高度的计算)--遍历到NULL为止
//它会递归到树的叶子结点(NULL为止),然后自下向上的进行结点度的计算判断
bool Is_Balenced_Tree(TreeNode* root, int *depth)
{
if (root == NULL)
{
*depth = 0;
return true;
}
int l_depth, r_depth;
bool l_flag = Is_Balenced_Tree(root->lchild, &l_depth);
bool r_flag = Is_Balenced_Tree(root->rchild, &r_depth);
if (l_flag&&r_flag)
{
int diff = l_depth - r_depth;
if (diff >= -1 && diff <= 1)
{
*depth = l_depth > r_depth ? l_depth + 1 : r_depth + 1;
return true;
}
}
return false;
}
bool IsBalanced(TreeNode* root)
{
int depth = 0;
return Is_Balenced_Tree(root, &depth);
}