【C++上岸】C++常见面试题目--数据结构篇(第十五期)
欢迎来到C++面试题系列的第15期!😊 数据结构是面试中的高频考点,掌握它们能帮你轻松上岸。今天,我们聚焦数据结构篇,详细解答10个常见问题。用简单易懂的语言和代码示例(C++实现)帮你理解核心概念🚀。
上岸必备!一次性搞懂八大高频考点+代码实战 🌟
文章目录
- 🔍 1. 直接选择排序算法
- 💦 2. 冒泡排序算法
- ⚡ 3. 快速排序算法
- 🌳 4. 堆排序算法
- 🌀 5. 希尔排序算法
- 🌿 6. 二叉树后序遍历的从后往前输出
- 🔄 7. 自底向上的二叉树层序遍历
- 👉 8. 二叉树的右视图
- 🧹 9. 原地移除指定元素
- 🔢 10. 有序数组的平方排序
- 💎 结语
🔍 1. 直接选择排序算法
核心思想:每次从未排序序列中选最小元素,放到已排序序列末尾。
时间复杂度:O(n2)O(n^2)O(n2),空间复杂度:O(1)O(1)O(1)
void selectionSort(vector<int>& arr) {for (int i = 0; i < arr.size() - 1; i++) {int minIndex = i;for (int j = i + 1; j < arr.size(); j++) {if (arr[j] < arr[minIndex]) minIndex = j; // 找最小值下标}swap(arr[i], arr[minIndex]); // 交换到有序区末尾}
}
举个栗子🌰:[5,2,9,1]
→ 第一轮选最小1
交换 → [1,2,9,5]
→ 第二轮选2
(已在位)→ 第三轮选5
交换 → [1,2,5,9]
💦 2. 冒泡排序算法
核心思想:相邻元素两两比较,将较大元素"冒泡"到右侧。
优化点:若某轮无交换,说明已有序可提前终止!
void bubbleSort(vector<int>& arr) {for (int i = 0; i < arr.size() - 1; i++) {bool swapped = false;for (int j = 0; j < arr.size() - 1 - i; j++) {if (arr[j] > arr[j + 1]) {swap(arr[j], arr[j + 1]);swapped = true;}}if (!swapped) break; // 无交换则提前退出}
}
动图联想🎞️:像气泡从水底上升,大的数慢慢浮到右侧~
⚡ 3. 快速排序算法
分治思想:选基准(pivot),划分小于/大于基准的子数组,递归排序。
平均时间复杂度:O(nlogn)O(n \log n)O(nlogn)
void quickSort(vector<int>& arr, int l, int r) {if (l >= r) return;int i = l, j = r, pivot = arr[(l + r) / 2]; // 选中间为基准while (i <= j) {while (arr[i] < pivot) i++;while (arr[j] > pivot) j--;if (i <= j) swap(arr[i++], arr[j--]);}quickSort(arr, l, j); // 递归左子数组quickSort(arr, i, r); // 递归右子数组
}
面试注意❗:手写时需处理边界条件,避免死循环!
🌳 4. 堆排序算法
核心步骤:
- 建最大堆(父节点 ≥ 子节点)
- 交换堆顶与末尾元素,堆大小-1
- 调整剩余元素为新堆
void heapify(vector<int>& arr, int n, int i) {int largest = i, l = 2 * i + 1, r = 2 * i + 2;if (l < n && arr[l] > arr[largest]) largest = l;if (r < n && arr[r] > arr[largest]) largest = r;if (largest != i) {swap(arr[i], arr[largest]);heapify(arr, n, largest);}
}void heapSort(vector<int>& arr) {for (int i = arr.size() / 2 - 1; i >= 0; i--) // 建堆heapify(arr, arr.size(), i);for (int i = arr.size() - 1; i > 0; i--) {swap(arr[0], arr[i]); // 移堆顶到末尾heapify(arr, i, 0); // 调整剩余堆}
}
口诀🧠:父节点i
,左子2i+1
,右子2i+2
🌀 5. 希尔排序算法
改进插入排序:按增量分组插入排序,增量逐轮缩小至1。
时间复杂度:O(n1.3)O(n^{1.3})O(n1.3) ~ O(n2)O(n^2)O(n2)
void shellSort(vector<int>& arr) {for (int gap = arr.size() / 2; gap > 0; gap /= 2) { // 增量序列for (int i = gap; i < arr.size(); i++) {int temp = arr[i], j;for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {arr[j] = arr[j - gap]; // 组内插入排序}arr[j] = temp;}}
}
增量选择🔧:常用gap = n/2, n/4, ... 1
🌿 6. 二叉树后序遍历的从后往前输出
需求:输出顺序为右子树 → 左子树 → 根(反向后序)
解法:用栈实现根 → 右 → 左遍历,结果反转即可!
vector<int> postorderTraversal(TreeNode* root) {vector<int> res;stack<TreeNode*> stk;if (root) stk.push(root);while (!stk.empty()) {TreeNode* node = stk.top(); stk.pop();res.push_back(node->val);if (node->left) stk.push(node->left); // 左先入栈if (node->right) stk.push(node->right); // 右后入栈(先出)}reverse(res.begin(), res.end()); // 反转得后序return res;
}
逻辑🤔:栈的LIFO
特性使右子树先出,反转后符合后序要求。
🔄 7. 自底向上的二叉树层序遍历
输出要求:从最后一层向根节点逐层输出
技巧:正常BFS层序遍历,结果反转每层
vector<vector<int>> levelOrderBottom(TreeNode* root) {vector<vector<int>> res;queue<TreeNode*> q;if (root) q.push(root);while (!q.empty()) {int size = q.size();vector<int> level;for (int i = 0; i < size; i++) {TreeNode* node = q.front(); q.pop();level.push_back(node->val);if (node->left) q.push(node->left);if (node->right) q.push(node->right);}res.push_back(level);}reverse(res.begin(), res.end()); // 反转层顺序return res;
}
示例🌲:
输入: 3 自底向上输出: [[15,7], [9,20], [3]]/ \9 20/ \15 7
👉 8. 二叉树的右视图
题目:给定根节点root
,返回从右侧能看到的节点值(每层最右节点)
解法:BFS层序遍历时,记录每层最后一个元素
vector<int> rightSideView(TreeNode* root) {vector<int> res;queue<TreeNode*> q;if (root) q.push(root);while (!q.empty()) {int size = q.size();for (int i = 0; i < size; i++) {TreeNode* node = q.front(); q.pop();if (i == size - 1) res.push_back(node->val); // 当前层最右节点if (node->left) q.push(node->left);if (node->right) q.push(node->right);}}return res;
}
关键点🔑:每层循环中,i == size-1
时即为最右节点。
🧹 9. 原地移除指定元素
要求:O(1)O(1)O(1)空间,原地移除nums
中所有val
双指针法:快指针扫描,慢指针标记非val
位置
int removeElement(vector<int>& nums, int val) {int slow = 0;for (int fast = 0; fast < nums.size(); fast++) {if (nums[fast] != val) {nums[slow++] = nums[fast]; // 非val元素前移}}return slow; // 新长度
}
示例📊:
nums = [3,2,2,3], val = 3
→ 快指针跳过3
,慢指针记录[2,2]
→ 返回长度2
🔢 10. 有序数组的平方排序
题目:非递减数组nums
,返回平方后的非递减数组
最优解:双指针从两端向中间扫描,比较绝对值大小!
vector<int> sortedSquares(vector<int>& nums) {int n = nums.size();vector<int> res(n);int left = 0, right = n - 1, pos = n - 1; // 从后往前填充while (left <= right) {if (abs(nums[left]) > abs(nums[right])) {res[pos--] = nums[left] * nums[left];left++;} else {res[pos--] = nums[right] * nums[right];right--;}}return res;
}
为什么从后往前❓:平方后最大值只可能出现在两端(负数的平方可能很大)!
💎 结语
🎉 恭喜你完成本期的学习!数据结构是C++面试的基石,多加练习这些题目,上岸指日可待。如果有疑问,欢迎留言讨论~💪
🌟如果觉得有用,点个赞吧!👍 收藏本文,面试前翻一翻,上岸更轻松! 如果觉得有用,点赞支持一下,你的鼓励是我持续更新的动力!😊 也欢迎关注我,获取更多“C++上岸”系列干货。下期见!🚀