目录
数据结构和算法-左神
03二进制和位运算
04 选择、冒泡、插入排序
// 交换数组中的两个元素
void swap(int arr[], int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
void printarr(int arr[], int n) {
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// 选择排序
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
swap(arr, i, minIndex);
}
}
// 冒泡排序
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
// 插入排序
void insertionSort(int arr[], int n) {
if (arr == nullptr || n < 2) {
return;
}
for (int i = 1; i < n; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
// printarr(arr, n); // 打印当前数组状态
}
}
}
05 对数器
对数器:实验器,对与两种针对同一种问题的算法进行验证,以判断缩写代码是否正确(提出一易一难进行对比)
// 生成随机数组
vector<int> randomArray(int n, int v) {
vector<int> arr(n);
for (int i = 0; i < n; i++) {
arr[i] = rand() % v + 1; // 生成 [1, v] 范围内的随机整数
}
return arr;
}
// 复制数组
vector<int> copyArray(const vector<int>& arr) {
return arr; // 直接返回一个副本
}
// 比较两个数组是否相同
bool sameArray(const vector<int>& arr1, const vector<int>& arr2) {
return arr1 == arr2; // 使用 vector 的比较运算符
}
// 主函数
int main() {
// 随机数组最大长度
int N = 10;
// 随机数组每个值,在1~V之间等概率随机
int V = 10;
// 测试次数
int testTimes = 5;
srand(time(0)); // 初始化随机数种子
cout << "测试开始" << endl;
for (int i = 0; i < testTimes; i++) {
// 随机得到一个长度,长度在[0~N-1]
int n = rand() % N;
// 得到随机数组
vector<int> arr = randomArray(n, V);
vector<int> arr1 = copyArray(arr);
vector<int> arr2 = copyArray(arr);
vector<int> arr3 = copyArray(arr);
// 转换为普通数组
int* arr1_ptr = arr1.data();
int* arr2_ptr = arr2.data();
int* arr3_ptr = arr3.data();
// 排序
selectionSort(arr1_ptr, n);
bubbleSort(arr2_ptr, n);
insertionSort(arr3_ptr, n);
// 比较结果
if (!sameArray(arr1, arr2) || !sameArray(arr1, arr3)) {
cout << "出错了!" << endl;
// 打印出错的数组
cout << "原始数组: ";
for (int num : arr) {
cout << num << " ";
}
cout << endl;
cout << "选择排序结果: ";
for (int num : arr1) {
cout << num << " ";
}
cout << endl;
cout << "冒泡排序结果: ";
for (int num : arr2) {
cout << num << " ";
}
cout << endl;
cout << "插入排序结果: ";
for (int num : arr3) {
cout << num << " ";
}
cout << endl;
return 1; // 退出程序
}
}
cout << "测试结束" << endl;
return 0;
}
上述是对三种排序算法的检测,如果报错可通过调整数组长度和数据大小,选择容易的数组进行测验,以解决报错
06 二分搜索
1)在有序数组中确定num存在还是不存在
2)在有序数组中找>=num的最左位置
3)在有序数组中找<=num的最右位置
4)二分搜索不一定发生在有序数组上(比如寻找峰值问题)
如果数组长度为n,那么二分搜索搜索次数是log n次,以2为底
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
// 生成随机数组
vector<int> randomArray(int n, int v) {
vector<int> arr(n);
for (int i = 0; i < n; i++) {
arr[i] = rand() % v + 1; // 生成 [1, v] 范围内的随机整数
}
return arr;
}
// 暴力解法:遍历数组查找数字
bool right(const vector<int>& sortedArr, int num) {
for (int cur : sortedArr) {// 遍历数组
if (cur == num) {
return true;
}
}
return false;
}
// 二分查找:在有序数组(从小到大)中查找数字
bool exist(const vector<int>& arr, int num) {
if (arr.empty()) {
return false;
}
int l = 0, r = arr.size() - 1, m = 0;
while (l <= r) {
m = (l + r) / 2;
//m=l+(r-l)/2; // 防止溢出
if (arr[m] == num) {
return true;
} else if (arr[m] > num) {
r = m - 1;
} else {
l = m + 1;
}
}
return false;
}
int findleft(const vector<int>& arr, int num) {
int l = 0, r = arr.size() - 1,m=0;
int ans=-1;
while (l <= r) {
m = (l + r) / 2;
if (arr[m] >= num) {
ans=m; // 记录当前的左边界
r = m - 1; // 继续向左查找
}
else {
l = m + 1;
}
}
return ans; // 返回左边界
}
int findright(const vector<int>& arr, int num) {
int l = 0, r = arr.size() - 1,m=0;
int ans=-1;
while (l <= r) {
m = (l + r) / 2;
if (arr[m] <= num) {
ans=m; // 记录当前的右边界
l = m + 1; // 继续向右查找
}
else {
r = m - 1;
}
}
return ans; // 返回右边界
}
int findpeak(const vector<int>& arr) {
int n = arr.size() - 1;
if(arr.size() == 0) {
return -1; // 数组为空
}
if (arr[0] > arr[1]) {
return 0; // 第一个元素是峰值
}
if (arr[n] > arr[n - 1]) {
return n; // 最后一个元素是峰值
}
int l=1,r=n-1,m=0,ans=-1;
while (l <= r) {
m = (l + r) / 2;
if (arr[m] < arr[m + 1]) {
l = m + 1;
} else if (arr[m] < arr[m - 1]) {
r = m - 1;
}else {
ans = m; // 找到峰值
break;
}
}
return ans;
}
// 主函数
int main() {
// 随机数组最大长度
int N = 100;
// 随机数组每个值,在1~V之间等概率随机
int V = 1000;
// 测试次数
int testTime = 500000;
srand(time(0)); // 初始化随机数种子
cout << "测试开始" << endl;
for (int i = 0; i < testTime; i++) {
// 随机得到一个长度,长度在[0~N-1]
int n = rand() % N;
// 得到随机数组
vector<int> arr = randomArray(n, V);
// 排序
sort(arr.begin(), arr.end());
// 随机生成一个数字
int num = rand() % V + 1;
// 比较暴力解法和二分查找的结果
if (right(arr, num) != exist(arr, num)) {
cout << "出错了!" << endl;
// 打印出错的数组和数字
cout << "数组: ";
for (int x : arr) {
cout << x << " ";
}
cout << endl;
cout << "查找数字: " << num << endl;
return 1; // 退出程序
}
}
cout << "测试结束" << endl;
return 0;
}
07 时间复杂度和空间复杂度
时间复杂度:常数操作(固定时间的操作、执行时间和数据量无关)的次数,只关注最高阶项;针对固定流程,计算最差情况,对于随机行为,选择平均或者期望的时间复杂度;仅根据代码结构无法判断时间复杂度,需要通过算法流程进行判断。
08 算法和数据结构
硬计算类算法、软计算类算法:
硬计算类算法:精确求解。但是某些问题使用硬计算类的算法,可能会让计算的复杂度较高(大厂算法和数据结构笔试、面试题、acm比赛或者和acm形式类似的比赛,考察的都是硬计算类算法)
软计算类算法:更注重逼近解决问题,而不是精确求解。计算时间可控(比如:模糊逻辑、神经网络、进化计算、概率理论、混沌理论、支持向量机、群体智能)
硬计算类算法是所有程序员岗位都会考、任何写代码的工作都会用到的。前端、后端、架构、算法所有岗位都要用到。
但是算法工程师除了掌握硬计算类的算法之外,还需要掌握软计算类的算法。
09 单双链表
09.1单双链表及反转
#include <iostream>
using namespace std;
// 单链表节点
class ListNode {
public:
int val;
ListNode* next;
// 构造函数:初始化节点值,下一个节点为 nullptr
ListNode(int val) : val(val), next(nullptr) {}// 构造函数初始化 val 和 next;或如下放在函数体里
// ListNode(int val) {
// this->val = val; // 使用 this 指针访问成员变量
// this->next = nullptr;
// }
ListNode(int val, ListNode* next) : val(val), next(next) {}
};
// 双链表节点
class DoubleListNode {
public:
int value;
DoubleListNode* last;
DoubleListNode* next;
DoubleListNode(int v) : value(v), last(nullptr), next(nullptr) {}
};
// 反转单链表
static ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* next = nullptr;
while (head != nullptr) {
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}
// 反转双链表
static DoubleListNode* reverseDoubleList(DoubleListNode* head) {
DoubleListNode* pre = nullptr;
DoubleListNode* next = nullptr;
while (head != nullptr) {
next = head->next;
head->next = pre;
head->last = next;
pre = head;
head = next;
}
return pre;
}
// 打印链表
void printList(ListNode* head) {
ListNode* current = head;
while (current != nullptr) {
cout << current->val << " -> ";
current = current->next;
}
cout << "nullptr" << endl;
}
// 主函数
int main() {
// 创建链表 1 -> 2 -> 3 -> nullptr
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(3);
// 打印链表
cout << "链表内容: ";
printList(head);
ListNode* reversedHead = reverseList(head);
cout << "反转后的链表内容: ";
printList(reversedHead);
// 释放链表内存
ListNode* current = reversedHead;
while (current != nullptr) {
ListNode* temp = current;
current = current->next;
delete temp;
}
return 0;
}
09.2合并链表
#include <iostream>
// 定义链表节点结构
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
// 合并两个有序链表
ListNode* mergeTwoLists(ListNode* head1, ListNode* head2) {
if (head1 == nullptr || head2 == nullptr) {
return head1 == nullptr ? head2 : head1;
}
ListNode* head = head1->val <= head2->val ? head1 : head2;
ListNode* cur1 = head->next;
ListNode* cur2 = head == head1 ? head2 : head1;
ListNode* pre = head;
while (cur1 != nullptr && cur2 != nullptr) {
if (cur1->val <= cur2->val) {
pre->next = cur1;
cur1 = cur1->next;
} else {
pre->next = cur2;
cur2 = cur2->next;
}
pre = pre->next;
}
pre->next = cur1 != nullptr ? cur1 : cur2;
return head;
}
// 辅助函数:打印链表
void printList(ListNode* head) {
while (head != nullptr) {
std::cout << head->val << " -> ";
head = head->next;
}
std::cout << "nullptr" << std::endl;
}
int main() {
// 创建两个链表
ListNode* head1 = new ListNode(1, new ListNode(3, new ListNode(5)));
ListNode* head2 = new ListNode(2, new ListNode(4, new ListNode(6)));
std::cout << "List 1: ";
printList(head1);
std::cout << "List 2: ";
printList(head2);
// 合并链表
ListNode* mergedHead = mergeTwoLists(head1, head2);
std::cout << "Merged List: ";
printList(mergedHead);
// 释放链表内存(可选)
while (mergedHead != nullptr) {
ListNode* temp = mergedHead;
mergedHead = mergedHead->next;
delete temp;
}
return 0;
}
09.2两数相加
给你两个 非空 的链表,表示两个非负的整数
它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头
#include <iostream>
// 定义链表节点结构
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
// 实现两个链表数字相加
ListNode* addTwoNumbers(ListNode* h1, ListNode* h2) {
ListNode* ans = nullptr; // 用于存储结果链表的头节点
ListNode* cur = nullptr; // 用于构建结果链表的当前节点
int carry = 0; // 进位
for (int sum, val; // 声明变量
h1 != nullptr || h2 != nullptr; // 终止条件
h1 = (h1 == nullptr ? nullptr : h1->next), // 每一步 h1 的跳转
h2 = (h2 == nullptr ? nullptr : h2->next) // 每一步 h2 的跳转
) {
// 计算当前位的和,包括进位
sum = (h1 == nullptr ? 0 : h1->val) + (h2 == nullptr ? 0 : h2->val) + carry;
val = sum % 10; // 当前位的值
carry = sum / 10; // 更新进位
// 构建结果链表
if (ans == nullptr) {
ans = new ListNode(val);
cur = ans;
} else {
cur->next = new ListNode(val);
cur = cur->next;
}
}
// 如果最后还有进位,追加一个新节点
if (carry == 1) {
cur->next = new ListNode(1);
}
return ans;
}
// 辅助函数:打印链表
void printList(ListNode* head) {
while (head != nullptr) {
std::cout << head->val << " -> ";
head = head->next;
}
std::cout << "nullptr" << std::endl;
}
int main() {
// 创建两个链表表示的数字
ListNode* h1 = new ListNode(2, new ListNode(4, new ListNode(3))); // 表示数字 342
ListNode* h2 = new ListNode(5, new ListNode(6, new ListNode(4))); // 表示数字 465
std::cout << "List 1: ";
printList(h1);
std::cout << "List 2: ";
printList(h2);
// 计算两个链表数字的和
ListNode* result = addTwoNumbers(h1, h2);
std::cout << "Sum List: ";
printList(result);
// 释放链表内存(可选)
while (h1 != nullptr) {
ListNode* temp = h1;
h1 = h1->next;
delete temp;
}
while (h2 != nullptr) {
ListNode* temp = h2;
h2 = h2->next;
delete temp;
}
while (result != nullptr) {
ListNode* temp = result;
result = result->next;
delete temp;
}
return 0;
}
09.2分隔链表
给你一个链表的头节点 head 和一个特定值 x
请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置
#include <iostream>
// 定义链表节点结构
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
// 实现链表分区操作
ListNode* partition(ListNode* head, int x) {
ListNode* leftHead = nullptr; // < x 的区域
ListNode* leftTail = nullptr;
ListNode* rightHead = nullptr; // >= x 的区域
ListNode* rightTail = nullptr;
ListNode* next = nullptr;
while (head != nullptr) {
next = head->next; // 保存下一个节点
head->next = nullptr; // 断开当前节点的连接
if (head->val < x) {
if (leftHead == nullptr) {
leftHead = head;
} else {
leftTail->next = head;
}
leftTail = head;
} else {
if (rightHead == nullptr) {
rightHead = head;
} else {
rightTail->next = head;
}
rightTail = head;
}
head = next; // 移动到下一个节点
}
if (leftHead == nullptr) {
return rightHead; // 如果没有小于 x 的节点,直接返回右分区的头节点
}
// 将左分区的尾部连接到右分区的头部
leftTail->next = rightHead;
return leftHead; // 返回左分区的头节点
}
// 辅助函数:打印链表
void printList(ListNode* head) {
while (head != nullptr) {
std::cout << head->val << " -> ";
head = head->next;
}
std::cout << "nullptr" << std::endl;
}
int main() {
// 创建一个链表:1 -> 4 -> 3 -> 2 -> 5 -> 2
ListNode* head = new ListNode(1, new ListNode(4, new ListNode(3, new ListNode(2, new ListNode(5, new ListNode(2))))));
std::cout << "Original List: ";
printList(head);
// 分区操作,x = 3
ListNode* partitionedHead = partition(head, 3);
std::cout << "Partitioned List: ";
printList(partitionedHead);
// 释放链表内存(可选)
while (partitionedHead != nullptr) {
ListNode* temp = partitionedHead;
partitionedHead = partitionedHead->next;
delete temp;
}
return 0;
}
013队列、栈、环形队列
013.1队列
Queue1:使用queue容器进行
#include <queue>
#include <iostream>
class Queue1 {
private:
std::queue<int> queue; // 使用C++ STL中的queue容器
public:
// 判断队列是否为空
bool isEmpty() {
return queue.empty();
}
// 向队列中加入元素,加到队尾
void offer(int num) {
queue.push(num);
}
// 从队列中取出元素,从队头取出
int poll() {
if (queue.empty()) {
throw std::out_of_range("Queue is empty"); // 如果队列为空,抛出异常
}
int front = queue.front(); // 获取队头元素
queue.pop(); // 弹出队头元素
return front;
}
// 返回队列头的元素,但不弹出
int peek() {
if (queue.empty()) {
throw std::out_of_range("Queue is empty"); // 如果队列为空,抛出异常
}
return queue.front();
}
// 返回队列中元素的数量
int size() {
return queue.size();
}
};
int main() {
Queue1 q;
q.offer(1);
q.offer(2);
q.offer(3);
std::cout << "Queue size: " << q.size() << std::endl; // 输出队列大小
std::cout << "Front element: " << q.peek() << std::endl; // 输出队头元素
std::cout << "Poll element: " << q.poll() << std::endl; // 弹出队头元素
std::cout << "Queue size after poll: " << q.size() << std::endl; // 输出队列大小
return 0;
}
queue2:使用数组,但是还是还是利用容器模拟数组
#include <iostream>
#include <vector>
class Queue2 {
public:
std::vector<int> queue;
int l;
int r;
// 构造函数,初始化队列大小
Queue2(int n) : queue(n, 0), l(0), r(0) {}
// 判断队列是否为空
bool isEmpty() {
return l == r;
}
// 向队列中加入元素
void offer(int num) {
if (r >= queue.size()) {
throw std::out_of_range("Queue is full"); // 如果队列已满,抛出异常
}
queue[r++] = num;
}
// 从队列中移除元素
int poll() {
if (isEmpty()) {
throw std::out_of_range("Queue is empty"); // 如果队列为空,抛出异常
}
return queue[l++];
}
// 返回队列头部的元素
int head() {
if (isEmpty()) {
throw std::out_of_range("Queue is empty"); // 如果队列为空,抛出异常
}
return queue[l];
}
// 返回队列尾部的元素
int tail() {
if (isEmpty()) {
throw std::out_of_range("Queue is empty"); // 如果队列为空,抛出异常
}
return queue[r - 1];
}
// 返回队列中元素的数量
int size() {
return r - l;
}
};
int main() {
Queue2 q(5); // 创建一个大小为5的队列
q.offer(1);
q.offer(2);
q.offer(3);
std::cout << "Queue size: " << q.size() << std::endl; // 输出队列大小
std::cout << "Head element: " << q.head() << std::endl; // 输出队头元素
std::cout << "Tail element: " << q.tail() << std::endl; // 输出队尾元素
std::cout << "Poll element: " << q.poll() << std::endl; // 弹出队头元素
std::cout << "Queue size after poll: " << q.size() << std::endl; // 输出队列大小
return 0;
}
013.2栈
stack1:c++中stack容器
#include <iostream>
#include <stack>
class Stack1 {
public:
std::stack<int> stack;
// 判断栈是否为空
bool isEmpty() {
return stack.empty();
}
// 向栈中压入一个元素
void push(int num) {
stack.push(num);
}
// 从栈中弹出一个元素
int pop() {
if (stack.empty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
int top = stack.top(); // 获取栈顶元素
stack.pop(); // 弹出栈顶元素
return top;
}
// 返回栈顶元素但不弹出
int peek() {
if (stack.empty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return stack.top();
}
// 返回栈中元素的数量
int size() {
return stack.size();
}
};
int main() {
Stack1 s;
s.push(1);
s.push(2);
s.push(3);
std::cout << "Stack size: " << s.size() << std::endl; // 输出栈大小
std::cout << "Top element: " << s.peek() << std::endl; // 输出栈顶元素
std::cout << "Pop element: " << s.pop() << std::endl; // 弹出栈顶元素
std::cout << "Stack size after pop: " << s.size() << std::endl; // 输出栈大小
return 0;
}
stack2:
#include <iostream>
#include <vector>
#include <stdexcept> // 用于抛出异常
class Stack2 {
public:
std::vector<int> stack; // 使用 std::vector 来模拟固定大小的数组
int size; // 当前栈中的元素个数
// 构造函数,初始化栈的大小
Stack2(int n) : stack(n, 0), size(0) {}
// 判断栈是否为空
bool isEmpty() {
return size == 0;
}
// 向栈中压入一个元素
void push(int num) {
if (size >= stack.size()) {
throw std::out_of_range("Stack is full"); // 如果栈已满,抛出异常
}
stack[size++] = num;
}
// 从栈中弹出一个元素
int pop() {
if (isEmpty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return stack[--size];
}
// 返回栈顶元素但不弹出
int peek() {
if (isEmpty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return stack[size - 1];
}
// 返回栈中元素的数量
int size() {
return size;
}
};
int main() {
Stack2 s(5); // 创建一个大小为5的栈
s.push(1);
s.push(2);
s.push(3);
std::cout << "Stack size: " << s.size() << std::endl; // 输出栈大小
std::cout << "Top element: " << s.peek() << std::endl; // 输出栈顶元素
std::cout << "Pop element: " << s.pop() << std::endl; // 弹出栈顶元素
std::cout << "Stack size after pop: " << s.size() << std::endl; // 输出栈大小
return 0;
}
013.3循环队列
#include <iostream>
#include <vector>
class MyCircularQueue {
public:
std::vector<int> queue; // 使用 std::vector 来模拟数组
int l, r, size, limit; // l: 队列头部指针,r: 队列尾部指针,size: 当前队列大小,limit: 队列容量
// 构造函数,初始化循环队列的大小
MyCircularQueue(int k) : queue(k, 0), l(0), r(0), size(0), limit(k) {}
// 如果队列满了,什么也不做,返回false
// 如果队列没满,加入value,返回true
bool enQueue(int value) {
if (isFull()) {
return false;
} else {
queue[r] = value;
// r = (r + 1) % limit; // 循环队列的关键操作:尾指针循环移动
r=r==limit-1?0:(r+1);
size++;
return true;
}
}
// 如果队列空了,什么也不做,返回false
// 如果队列没空,弹出头部的数字,返回true
bool deQueue() {
if (isEmpty()) {
return false;
} else {
// l = (l + 1) % limit; // 循环队列的关键操作:头指针循环移动
l=l==limit-1?0:(l+1);
size--;
return true;
}
}
// 返回队列头部的数字(不弹出),如果没有数返回-1
int Front() {
if (isEmpty()) {
return -1;
} else {
return queue[l];
}
}
// 返回队列尾部的数字(不弹出),如果没有数返回-1
int Rear() {
if (isEmpty()) {
return -1;
} else {
int last = (r == 0) ? (limit - 1) : (r - 1); // 循环队列的关键操作:计算尾部索引
return queue[last];
}
}
// 判断队列是否为空
bool isEmpty() {
return size == 0;
}
// 判断队列是否已满
bool isFull() {
return size == limit;
}
};
int main() {
MyCircularQueue q(5); // 创建一个大小为5的循环队列
q.enQueue(1);
q.enQueue(2);
q.enQueue(3);
std::cout << "Front element: " << q.Front() << std::endl; // 输出队头元素
std::cout << "Rear element: " << q.Rear() << std::endl; // 输出队尾元素
std::cout << "Dequeue element: " << q.deQueue() << std::endl; // 弹出队头元素
std::cout << "Front element after dequeue: " << q.Front() << std::endl; // 输出队头元素
return 0;
}
014栈-队列的相互转换
014.1用栈实现队列
#include <iostream>
#include <stack>
class MyQueue {
public:
std::stack<int> in; // 用于存储新加入的元素
std::stack<int> out; // 用于存储待弹出的元素
MyQueue() {
// 构造函数,初始化两个栈
}
// 从 in 栈倒入 out 栈
void inToOut() {
if (out.empty()) { // 只有当 out 栈为空时,才从 in 栈倒入数据
while (!in.empty()) {
out.push(in.top());
in.pop();
}
}
}
// 向队列中加入元素
void push(int x) {
in.push(x); // 新元素直接压入 in 栈
inToOut(); // 立即进行数据倒置,确保 out 栈始终是待弹出的顺序
}
// 从队列中弹出元素
int pop() {
inToOut(); // 确保 out 栈有数据
int result = out.top(); // 获取 out 栈的栈顶元素
out.pop(); // 弹出 out 栈的栈顶元素
return result;
}
// 返回队列头部的元素(不弹出)
int peek() {
inToOut(); // 确保 out 栈有数据
return out.top(); // 返回 out 栈的栈顶元素
}
// 判断队列是否为空
bool empty() {
return in.empty() && out.empty(); // 队列为空当且仅当两个栈都为空
}
};
int main() {
MyQueue q;
q.push(1);
q.push(2);
q.push(3);
std::cout << "Front element: " << q.peek() << std::endl; // 输出队头元素
std::cout << "Pop element: " << q.pop() << std::endl; // 弹出队头元素
std::cout << "Front element after pop: " << q.peek() << std::endl; // 输出队头元素
std::cout << "Queue is empty: " << (q.empty() ? "true" : "false") << std::endl; // 判断队列是否为空
return 0;
}
014.2用队列实现栈
#include <iostream>
#include <queue>
class MyStack {
public:
std::queue<int> queue;
MyStack() {
// 构造函数,初始化队列
}
// O(n) 时间复杂度
void push(int x) {
int n = queue.size();
queue.push(x); // 将新元素加入队列
for (int i = 0; i < n; i++) {
queue.push(queue.front()); // 将队列头部元素移到队尾
queue.pop(); // 移除队列头部元素
}
}
int pop() {
int result = queue.front(); // 获取队列头部元素
queue.pop(); // 移除队列头部元素
return result;
}
int top() {
return queue.front(); // 返回队列头部元素
}
bool empty() {
return queue.empty(); // 判断队列是否为空
}
};
int main() {
MyStack stack;
stack.push(1);
stack.push(2);
stack.push(3);
std::cout << "Top element: " << stack.top() << std::endl; // 输出栈顶元素
std::cout << "Pop element: " << stack.pop() << std::endl; // 弹出栈顶元素
std::cout << "Top element after pop: " << stack.top() << std::endl; // 输出栈顶元素
std::cout << "Stack is empty: " << (stack.empty() ? "true" : "false") << std::endl; // 判断栈是否为空
return 0;
}
015最小栈
minstack1:两个栈构成
#include <iostream>
#include <stack>
#include <stdexcept> // 用于抛出异常
class MinStack {
public:
std::stack<int> data; // 存储所有元素
std::stack<int> min; // 存储当前最小值
MinStack() {
// 构造函数,初始化两个栈
}
// 向栈中压入一个元素
void push(int val) {
data.push(val); // 将元素压入 data 栈
if (min.empty() || val <= min.top()) {
min.push(val); // 如果 min 栈为空或当前值小于等于 min 栈顶,则压入 min 栈
} else {
min.push(min.top()); // 否则,将 min 栈顶元素再次压入 min 栈
}
}
// 从栈中弹出一个元素
void pop() {
if (data.empty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
data.pop(); // 弹出 data 栈顶元素
min.pop(); // 弹出 min 栈顶元素
}
// 获取栈顶元素
int top() {
if (data.empty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return data.top(); // 返回 data 栈顶元素
}
// 获取当前栈中的最小值
int getMin() {
if (min.empty()) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return min.top(); // 返回 min 栈顶元素
}
};
int main() {
MinStack stack;
stack.push(3);
stack.push(1);
stack.push(2);
std::cout << "Top element: " << stack.top() << std::endl; // 输出栈顶元素
std::cout << "Minimum element: " << stack.getMin() << std::endl; // 输出当前最小值
stack.pop();
std::cout << "Top element after pop: " << stack.top() << std::endl; // 输出栈顶元素
std::cout << "Minimum element after pop: " << stack.getMin() << std::endl; // 输出当前最小值
return 0;
}
minstack2:数组构成
#include <iostream>
#include <stdexcept> // 用于抛出异常
class MinStack {
public:
static const int MAXN = 8001; // 最大容量
int data[MAXN]; // 存储栈中的数据
int min[MAXN]; // 存储当前栈中的最小值
int size; // 当前栈的大小
MinStack() : size(0) {
// 构造函数,初始化栈
}
// 向栈中压入一个元素
void push(int val) {
if (size >= MAXN) {
throw std::out_of_range("Stack overflow"); // 如果栈已满,抛出异常
}
data[size] = val;
if (size == 0 || val <= min[size - 1]) {
min[size] = val;
} else {
min[size] = min[size - 1];
}
size++;
}
// 从栈中弹出一个元素
void pop() {
if (size == 0) {
throw std::out_of_range("Stack underflow"); // 如果栈为空,抛出异常
}
size--;
}
// 获取栈顶元素
int top() {
if (size == 0) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return data[size - 1];
}
// 获取当前栈中的最小值
int getMin() {
if (size == 0) {
throw std::out_of_range("Stack is empty"); // 如果栈为空,抛出异常
}
return min[size - 1];
}
};
int main() {
MinStack stack;
stack.push(3);
stack.push(1);
stack.push(2);
std::cout << "Top element: " << stack.top() << std::endl; // 输出栈顶元素
std::cout << "Minimum element: " << stack.getMin() << std::endl; // 输出当前最小值
stack.pop();
std::cout << "Top element after pop: " << stack.top() << std::endl; // 输出栈顶元素
std::cout << "Minimum element after pop: " << stack.getMin() << std::endl; // 输出当前最小值
return 0;
}
016双端队列
- 使用deque容器
#include <iostream>
#include <deque>
class MyCircularDeque {
public:
std::deque<int> deque; // 使用 std::deque 作为底层数据结构
int size; // 当前队列的大小
int limit; // 队列的最大容量
// 构造函数,初始化队列的最大容量
MyCircularDeque(int k) : size(0), limit(k) {}
// 在队列前端插入一个元素
bool insertFront(int value) {
if (isFull()) {
return false; // 如果队列已满,返回 false
} else {
deque.push_front(value); // 在前端插入元素
size++;
return true;
}
}
// 在队列后端插入一个元素
bool insertLast(int value) {
if (isFull()) {
return false; // 如果队列已满,返回 false
} else {
deque.push_back(value); // 在后端插入元素
size++;
return true;
}
}
// 从队列前端删除一个元素
bool deleteFront() {
if (isEmpty()) {
return false; // 如果队列为空,返回 false
} else {
deque.pop_front(); // 从前端删除元素
size--;
return true;
}
}
// 从队列后端删除一个元素
bool deleteLast() {
if (isEmpty()) {
return false; // 如果队列为空,返回 false
} else {
deque.pop_back(); // 从后端删除元素
size--;
return true;
}
}
// 获取队列前端的元素
int getFront() {
if (isEmpty()) {
return -1; // 如果队列为空,返回 -1
} else {
return deque.front(); // 返回前端元素
}
}
// 获取队列后端的元素
int getRear() {
if (isEmpty()) {
return -1; // 如果队列为空,返回 -1
} else {
return deque.back(); // 返回后端元素
}
}
// 判断队列是否为空
bool isEmpty() {
return size == 0;
}
// 判断队列是否已满
bool isFull() {
return size == limit;
}
};
int main() {
MyCircularDeque deque(5); // 创建一个容量为 5 的循环双端队列
deque.insertFront(1);
deque.insertLast(2);
deque.insertFront(3);
deque.insertLast(4);
std::cout << "Front element: " << deque.getFront() << std::endl; // 输出前端元素
std::cout << "Rear element: " << deque.getRear() << std::endl; // 输出后端元素
deque.deleteFront();
deque.deleteLast();
std::cout << "Front element after delete: " << deque.getFront() << std::endl; // 输出前端元素
std::cout << "Rear element after delete: " << deque.getRear() << std::endl; // 输出后端元素
return 0;
}
- 使用数组
#include <iostream>
#include <vector>
class MyCircularDeque {
public:
std::vector<int> deque; // 使用 std::vector 作为底层数组
int l, r, size, limit; // l: 队列前端指针,r: 队列后端指针,size: 当前大小,limit: 最大容量
// 构造函数,初始化队列
MyCircularDeque(int k) : deque(k), l(0), r(0), size(0), limit(k) {}
// 在队列前端插入一个元素
bool insertFront(int value) {
if (isFull()) {
return false; // 如果队列已满,返回 false
}
if (isEmpty()) {
l = r = 0; // 如果队列为空,初始化指针
} else {
l = (l == 0) ? (limit - 1) : (l - 1); // 循环移动前端指针
}
deque[l] = value; // 插入元素
size++;
return true;
}
// 在队列后端插入一个元素
bool insertLast(int value) {
if (isFull()) {
return false; // 如果队列已满,返回 false
}
if (isEmpty()) {
l = r = 0; // 如果队列为空,初始化指针
} else {
r = (r == limit - 1) ? 0 : (r + 1); // 循环移动后端指针
}
deque[r] = value; // 插入元素
size++;
return true;
}
// 从队列前端删除一个元素
bool deleteFront() {
if (isEmpty()) {
return false; // 如果队列为空,返回 false
}
l = (l == limit - 1) ? 0 : (l + 1); // 循环移动前端指针
size--;
return true;
}
// 从队列后端删除一个元素
bool deleteLast() {
if (isEmpty()) {
return false; // 如果队列为空,返回 false
}
r = (r == 0) ? (limit - 1) : (r - 1); // 循环移动后端指针
size--;
return true;
}
// 获取队列前端的元素
int getFront() {
if (isEmpty()) {
return -1; // 如果队列为空,返回 -1
}
return deque[l]; // 返回前端元素
}
// 获取队列后端的元素
int getRear() {
if (isEmpty()) {
return -1; // 如果队列为空,返回 -1
}
return deque[r]; // 返回后端元素
}
// 判断队列是否为空
bool isEmpty() {
return size == 0;
}
// 判断队列是否已满
bool isFull() {
return size == limit;
}
};
int main() {
MyCircularDeque deque(5); // 创建一个容量为 5 的循环双端队列
deque.insertFront(1);
deque.insertLast(2);
deque.insertFront(3);
deque.insertLast(4);
std::cout << "Front element: " << deque.getFront() << std::endl; // 输出前端元素
std::cout << "Rear element: " << deque.getRear() << std::endl; // 输出后端元素
deque.deleteFront();
deque.deleteLast();
std::cout << "Front element after delete: " << deque.getFront() << std::endl; // 输出前端元素
std::cout << "Rear element after delete: " << deque.getRear() << std::endl; // 输出后端元素
return 0;
}
017二叉树实现
先序:任何子树先中后左再右;
中序:左中右;
后序:左右中
017.1递归实现
1、递归序
#include <iostream>
// 定义二叉树节点结构
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};
// 递归函数,用于遍历二叉树
void f(TreeNode* head) {
if (head == nullptr) {
return;
}
// 1:第一次到所在节点
f(head->left); // 递归遍历左子树
// 2:第二次到所在节点
f(head->right); // 递归遍历右子树
// 3:第三次到所在节点
}
// 辅助函数:创建一个简单的二叉树
TreeNode* createTree() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
return root;
}
// 辅助函数:释放二叉树内存
void deleteTree(TreeNode* root) {
if (root == nullptr) {
return;
}
deleteTree(root->left);
deleteTree(root->right);
delete root;
}
按照递归序,上述顺序为:1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1;可以看出在每个节点都经过三次。之后的先序、中序、后序可以看作在递归序中的三次停靠分别打印所在节点。
时间复杂度O(n),额外空间复杂度O(h),h是二叉树的高度;
2、先序
// 前序遍历:递归版
void preOrder(TreeNode* head) {
if (head == nullptr) {
return;
}
std::cout << head->val << " "; // 访问当前节点
preOrder(head->left); // 递归遍历左子树
preOrder(head->right); // 递归遍历右子树
}
3、中序
// 中序遍历:递归版
void inOrder(TreeNode* head) {
if (head == nullptr) {
return;
}
inOrder(head->left); // 递归遍历左子树
std::cout << head->val << " "; // 访问当前节点
inOrder(head->right); // 递归遍历右子树
}
4、后序
// 后序遍历:递归版
void posOrder(TreeNode* head) {
if (head == nullptr) {
return;
}
posOrder(head->left); // 递归遍历左子树
posOrder(head->right); // 递归遍历右子树
std::cout << head->val << " "; // 访问当前节点
}
017.2 非递归实现
1.先序、中序
#include <iostream>
#include <stack>
// 定义二叉树节点结构
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};
// 先序遍历:非递归版
void preOrder(TreeNode* head) {
if (head != nullptr) {
std::stack<TreeNode*> stack; // 使用 std::stack 来模拟递归
stack.push(head);
while (!stack.empty()) {
head = stack.top(); // 获取栈顶元素
stack.pop(); // 弹出栈顶元素
std::cout << head->val << " "; // 访问当前节点
// 先将右子节点入栈(如果存在)
if (head->right != nullptr) {
stack.push(head->right);
}
// 再将左子节点入栈(如果存在)
if (head->left != nullptr) {
stack.push(head->left);
}
}
std::cout << std::endl;
}
}
// 中序遍历:非递归版
void inOrder(TreeNode* head) {
if (head != nullptr) {
std::stack<TreeNode*> stack; // 使用 std::stack 来模拟递归
while (!stack.empty() || head != nullptr) {
// 将当前节点的所有左子节点压入栈
while (head != nullptr) {
stack.push(head);
head = head->left;
}
// 弹出栈顶节点并访问
head = stack.top();
stack.pop();
std::cout << head->val << " ";
// 转向右子节点
head = head->right;
}
std::cout << std::endl;
}
}
// 辅助函数:创建一个简单的二叉树
TreeNode* createTree() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
return root;
}
// 辅助函数:释放二叉树内存
void deleteTree(TreeNode* root) {
if (root == nullptr) {
return;
}
deleteTree(root->left);
deleteTree(root->right);
delete root;
}
int main() {
TreeNode* root = createTree(); // 创建一个简单的二叉树
std::cout << "PreOrder (Non-Recursive): ";
preOrder(root); // 非递归先序遍历
std::cout << "inOrder (Non-Recursive): ";
inOrder(root); // 非递归先序遍历
deleteTree(root); // 释放二叉树内存
return 0;
}
2.后序
- 使用两个栈
#include <iostream>
#include <stack>
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};
void posOrderTwoStacks(TreeNode* head) {
if (head != nullptr) {
std::stack<TreeNode*> stack; // 用于模拟递归
std::stack<TreeNode*> collect; // 用于收集节点
stack.push(head);
while (!stack.empty()) {
head = stack.top();
stack.pop();
collect.push(head);
if (head->left != nullptr) {
stack.push(head->left);
}
if (head->right != nullptr) {
stack.push(head->right);
}
}
while (!collect.empty()) {
std::cout << collect.top()->val << " ";
collect.pop();
}
std::cout << std::endl;
}
}
- 使用一个栈
void posOrderOneStack(TreeNode* h) {
if (h != nullptr) {
std::stack<TreeNode*> stack;
stack.push(h);
TreeNode* lastPrinted = nullptr; // 上一次打印的节点
while (!stack.empty()) {
TreeNode* cur = stack.top();
if (cur->left != nullptr && lastPrinted != cur->left && lastPrinted != cur->right) {
// 有左子树且左子树未处理
stack.push(cur->left);
} else if (cur->right != nullptr && lastPrinted != cur->right) {
// 有右子树且右子树未处理
stack.push(cur->right);
} else {
// 左子树和右子树都已处理或不存在
std::cout << cur->val << " ";
lastPrinted = stack.top();
stack.pop();
}
}
std::cout << std::endl;
}
}