C++高频知识点(二十九)
文章目录
- 141. 说说归并排序的思路
- 基本介绍
- 排序思路
- 代码实现
- 自己考研时候写的归并排序代码
- 142. 智能指针 weak_ptr有什么用,可以提升为shared_ptr吗?
- weak_ptr 的作用(解决循环引用问题)
- 使用 weak_ptr 解决循环引用
- weak_ptr可以提升为 shared_ptr 吗?
- 143. 用C++实现二分查找
- 1. 递归实现:
- 2. 迭代实现:
- 144. 静态链接和动态链接有什么区别?(高频)
- 静态编译:
- 动态链接:
- 145. volatile关键字有什么作用?
- 例子1:硬件寄存器
- 如果使用 volatile:
- 如果不使用 volatile:
- 例子2:多线程编程
- 如果使用 volatile:
- 如果不使用 volatile:
141. 说说归并排序的思路
基本介绍
排序思路
代码实现
#include <iostream>
using namespace std;// 合并两个有序数组
void merge(int arr[], int l, int m, int r) {int n1 = m - l + 1;int n2 = r - m;// 创建临时数组int L[n1], R[n2];// 拷贝数据到临时数组 L[] 和 R[]for (int i = 0; i < n1; i++)L[i] = arr[l + i];for (int j = 0; j < n2; j++)R[j] = arr[m + 1 + j];// 合并临时数组到 arr[l..r]int i = 0; // 初始化左边索引int j = 0; // 初始化右边索引int k = l; // 初始化合并子数组索引while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k] = L[i];i++;} else {arr[k] = R[j];j++;}k++;}// 拷贝剩余元素while (i < n1) {arr[k] = L[i];i++;k++;}while (j < n2) {arr[k] = R[j];j++;k++;}
}// 归并排序函数
void mergeSort(int arr[], int l, int r) {if (l < r) {// 找到中间点int m = l + (r - l) / 2;// 递归排序左半部分mergeSort(arr, l, m);// 递归排序右半部分mergeSort(arr, m + 1, r);// 合并两部分merge(arr, l, m, r);}
}// 打印数组函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {cout << arr[i] << " ";}cout << endl;
}int main() {// 初始化一个数组int arr[] = {12, 11, 13, 5, 6, 7};int arr_size = sizeof(arr) / sizeof(arr[0]);// 输出未排序的数组cout << "未排序数组: \n";printArray(arr, arr_size);// 进行归并排序mergeSort(arr, 0, arr_size - 1);// 输出排序后的数组cout << "排序后的数组: \n";printArray(arr, arr_size);return 0;
}
自己考研时候写的归并排序代码
#include <bits/stdc++.h>using namespace std;void merge(vector<int>& arr, int low, int mid, int high) {vector<int> t(high - low + 1, 0);int k = 0;int i = low, j = mid + 1;while(i <= mid && j <= high) {if(arr[i] <= arr[j]) {t[k++] = arr[i++];} else {t[k++] = arr[j++];}}while(i <= mid) {t[k++] = arr[i++];}while(j <= high) {t[k++] = arr[j++];}for(int i = low, k = 0; i <= high; ++i) {arr[i] = t[k++];}
}void MergeSort(vector<int>& arr, int left, int right) {if(left < right) {int mid = left + (right - left) / 2;MergeSort(arr, left, mid);MergeSort(arr, mid + 1, right);merge(arr, left, mid, right);}
}int main() {vector<int> arr = {38, 27, 43, 3, 9, 82, 10};const int n = arr.size();MergeSort(arr, 0, n - 1);for(const int& num: arr) {cout << num<< " ";}cout << endl;return 0;
}
142. 智能指针 weak_ptr有什么用,可以提升为shared_ptr吗?
weak_ptr 的作用(解决循环引用问题)
class A {
public:std::shared_ptr<B> b;
};class B {
public:std::shared_ptr<A> a;
};
在这个例子中,A 和 B 互相持有对方的 shared_ptr,这就会导致它们的引用计数永远不会变为 0,进而造成内存泄漏。
使用 weak_ptr 解决循环引用
为了打破这种循环引用,可以使用 std::weak_ptr。例如,在类 B 中,将 shared_ptr<A> 改为 weak_ptr<A>:
class A {
public:std::shared_ptr<B> b;
};class B {
public:std::weak_ptr<A> a; // 使用 weak_ptr,避免循环引用
};
weak_ptr 不增加引用计数,因此,B 类的 a 指针不会阻止 A 对象被销毁。
weak_ptr可以提升为 shared_ptr 吗?
weak_ptr 可以 提升为 shared_ptr。这通过 lock() 方法实现:
std::shared_ptr<A> sharedA = weakA.lock();
#include <iostream>
#include <memory>class A {
public:void hello() { std::cout << "Hello, World!" << std::endl; }
};int main() {std::shared_ptr<A> sharedA = std::make_shared<A>();std::weak_ptr<A> weakA = sharedA; // weak_ptr 不增加引用计数// 提升 weak_ptr 为 shared_ptrstd::shared_ptr<A> sharedB = weakA.lock();if (sharedB) {sharedB->hello(); // 输出 "Hello, World!"} else {std::cout << "Object is already destroyed!" << std::endl;}return 0;
}
143. 用C++实现二分查找
二分查找是一个经典的算法,通常用来在有序数组中查找某个元素。二分查找的时间复杂度为 O(log n),其中 n 是数组的大小。
以下是 C++ 实现二分查找的代码示例:
1. 递归实现:
#include <iostream>
#include <vector>
int binarySearchRecursive(const std::vector<int>& arr, int target, int left, int right) {if (left > right) {return -1; // 元素未找到}int mid = left + (right - left) / 2; // 避免整数溢出if (arr[mid] == target) {return mid; // 找到目标元素,返回其下标} else if (arr[mid] > target) {return binarySearchRecursive(arr, target, left, mid - 1); // 在左半部分继续查找} else {return binarySearchRecursive(arr, target, mid + 1, right); // 在右半部分继续查找}
}
int main() {std::vector<int> arr = {1, 3, 5, 7, 9, 11, 13, 15};int target = 7;int result = binarySearchRecursive(arr, target, 0, arr.size() - 1);if (result != -1) {std::cout << "Element found at index: " << result << std::endl;} else {std::cout << "Element not found." << std::endl;}return 0;
}
2. 迭代实现:
#include <iostream>
#include <vector>
int binarySearchIterative(const std::vector<int>& arr, int target) {int left = 0;int right = arr.size() - 1;while (left <= right) {int mid = left + (right - left) / 2; // 避免整数溢出if (arr[mid] == target) {return mid; // 找到目标元素,返回其下标} else if (arr[mid] > target) {right = mid - 1; // 在左半部分继续查找} else {left = mid + 1; // 在右半部分继续查找}}return -1; // 元素未找到
}
int main() {std::vector<int> arr = {1, 3, 5, 7, 9, 11, 13, 15};int target = 7;int result = binarySearchIterative(arr, target);if (result != -1) {std::cout << "Element found at index: " << result << std::endl;} else {std::cout << "Element not found." << std::endl;}return 0;
}
144. 静态链接和动态链接有什么区别?(高频)
在 C++ 中,静态链接和动态链接是两种不同的程序链接方式。
它们在编译、链接、运行时的行为和性能上有所不同。
静态编译:
动态链接:
假设你在 Linux 上有一个程序需要使用数学函数库。
145. volatile关键字有什么作用?
例子1:硬件寄存器
假设你在编程时有一个控制硬件的寄存器,比如某个硬件设备通过内存映射的方式提供一个状态寄存器,你的程序每次需要检查这个寄存器的值来了解设备的状态。
如果使用 volatile:
volatile int statusRegister; // 硬件状态寄存器
if (statusRegister == 1) {// 做一些处理
}
- 编译器不会对 statusRegister 做任何优化。每次访问时,都会从内存中读取这个变量的值,确保读取到最新的状态。这是因为硬件的状态可能在程序运行时被硬件或外部事件改变,编译器不能假设它的值会保持不变。
如果不使用 volatile:
int statusRegister;
if (statusRegister == 1) {// 做一些处理
}
- 编译器可能会对 statusRegister 进行优化。例如,它可能在程序开始时读取一次变量的值,并将其缓存到寄存器中,之后直接使用缓存的值,而不是每次从内存读取。这样就可能错过硬件改变的状态,导致程序不能及时响应硬件变化。
例子2:多线程编程
假设有两个线程,一个线程负责更新一个标志位,另一个线程负责检查这个标志位是否发生了变化。
如果使用 volatile:
volatile bool flag = false;void thread1() {flag = true; // 线程1将flag设为true
}void thread2() {while (!flag) {// 线程2一直等待,直到flag变为true}std::cout << "Flag is true!" << std::endl;
}
- 使用 volatile 确保线程2每次检查 flag 时,都能从内存中读取最新的值。即使线程1在其他地方修改了 flag,线程2也能实时看到变化。
如果不使用 volatile:
bool flag = false;void thread1() {flag = true; // 线程1将flag设为true
}void thread2() {while (!flag) {// 线程2一直等待,直到flag变为true}std::cout << "Flag is true!" << std::endl;
}
- 编译器可能会优化 flag 的访问,导致线程2在检查 flag 时读取到缓存的旧值,而不是内存中的最新值。这样线程2可能永远不会看到 flag 变为 true,导致死循环。
之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!