算法四 习题 1.3
数组实现栈
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;class MyStack {
private:vector<int> data; // 用于存储栈元素的数组public:// 构造函数MyStack() {}// 入栈操作void push(int val) {data.push_back(val); // 在数组末尾添加元素}// 出栈操作void pop() {if (empty()) {throw runtime_error("Stack underflow");}data.pop_back(); // 移除数组末尾元素}// 获取栈顶元素int top() {if (empty()) {throw runtime_error("Stack is empty");}return data.back(); // 返回数组末尾元素}// 检查栈是否为空bool empty() const {return data.empty();}// 返回栈中的元素数量int size() const {return data.size();}
};// 测试程序
int main() {return 0;
}
链表实现栈
#include <iostream>
#include <stdexcept>
using namespace std;// 链表节点结构
class Node {
public:int data;Node* next;// 构造函数Node(int val) : data(val), next(nullptr) {}
};// 使用链表实现的栈
class LinkedListStack {
private:Node* top_ptr; // 指向栈顶的指针int n; // 栈中元素的数量public:// 构造函数LinkedListStack() : top_ptr(nullptr), n(0) {}// 析构函数,释放所有节点~LinkedListStack() {while (!empty()) {pop();}}// 入栈操作void push(int val) {Node* new_node = new Node(val);new_node->next = top_ptr;top_ptr = new_node;n++;}// 出栈操作void pop() {if (empty()) {throw runtime_error("Stack underflow");}Node* temp = top_ptr;top_ptr = top_ptr->next;delete temp;n--;}// 获取栈顶元素int top() {if (empty()) {throw runtime_error("Stack is empty");}return top_ptr->data;}// 检查栈是否为空bool empty() const {return top_ptr == nullptr;}// 返回栈中的元素数量int size() const {return n;}// 打印栈中的所有元素(用于调试)void printStack() const {if (empty()) {cout << "Stack is empty" << endl;return;}cout << "Stack (top to bottom): ";Node* current = top_ptr;while (current != nullptr) {cout << current->data << " ";current = current->next;}cout << endl;}
};// 测试程序
int main() {return 0;
}
双向队列和栈
#include <iostream>
#include <stdexcept>
using namespace std;// 双向链表节点结构
class Node {
public:int data;Node* next;Node* prev;// 构造函数Node(int val) : data(val), next(nullptr), prev(nullptr) {}
};// 使用双向链表实现的双向队列
class Deque {
private:Node* front; // 指向队列前端的指针Node* back; // 指向队列后端的指针int n; // 队列中元素的数量public:// 构造函数Deque() : front(nullptr), back(nullptr), n(0) {}// 析构函数,释放所有节点~Deque() {while (!empty()) {removeFront();}}// 检查队列是否为空bool empty() const {return n == 0;}// 返回队列中的元素数量int size() const {return n;}// 在队列前端添加元素void addFront(int val) {Node* new_node = new Node(val);if (empty()) {front = back = new_node;} else {new_node->next = front;front->prev = new_node;front = new_node;}n++;}// 在队列后端添加元素void addBack(int val) {Node* new_node = new Node(val);if (empty()) {front = back = new_node;} else {new_node->prev = back;back->next = new_node;back = new_node;}n++;}// 从队列前端移除元素int removeFront() {if (empty()) {throw runtime_error("Deque underflow");}Node* temp = front;int val = temp->data;if (front == back) {// 队列中只有一个元素front = back = nullptr;} else {front = front->next;front->prev = nullptr;}delete temp;n--;return val;}// 从队列后端移除元素int removeBack() {if (empty()) {throw runtime_error("Deque underflow");}Node* temp = back;int val = temp->data;if (front == back) {// 队列中只有一个元素front = back = nullptr;} else {back = back->prev;back->next = nullptr;}delete temp;n--;return val;}// 获取队列前端元素int peekFront() const {if (empty()) {throw runtime_error("Deque is empty");}return front->data;}// 获取队列后端元素int peekBack() const {if (empty()) {throw runtime_error("Deque is empty");}return back->data;}// 打印队列中的所有元素(用于调试)void printDeque() const {if (empty()) {cout << "Deque is empty" << endl;return;}cout << "Deque (front to back): ";Node* current = front;while (current != nullptr) {cout << current->data << " ";current = current->next;}cout << endl;}
};// 使用双向队列实现的栈
class DequeStack {
private:Deque deque; // 使用双向队列作为底层数据结构public:// 入栈操作(在队列前端添加元素)void push(int val) {deque.addFront(val);}// 出栈操作(从队列前端移除元素)int pop() {return deque.removeFront();}// 获取栈顶元素int top() const {return deque.peekFront();}// 检查栈是否为空bool empty() const {return deque.empty();}// 返回栈中的元素数量int size() const {return deque.size();}// 打印栈中的所有元素(用于调试)void printStack() const {cout << "Stack (via Deque): ";deque.printDeque();}
};// 使用双向队列实现的队列
class DequeQueue {
private:Deque deque; // 使用双向队列作为底层数据结构public:// 入队操作(在队列后端添加元素)void enqueue(int val) {deque.addBack(val);}// 出队操作(从队列前端移除元素)int dequeue() {return deque.removeFront();}// 获取队头元素int front() const {return deque.peekFront();}// 检查队列是否为空bool empty() const {return deque.empty();}// 返回队列中的元素数量int size() const {return deque.size();}// 打印队列中的所有元素(用于调试)void printQueue() const {cout << "Queue (via Deque): ";deque.printDeque();}
};// 测试程序
int main() {cout << "===== Testing Deque =====" << endl;Deque deque;deque.addFront(10);deque.printDeque();deque.addBack(20);deque.printDeque();deque.addFront(5);deque.printDeque();deque.addBack(30);deque.printDeque();cout << "Front element: " << deque.peekFront() << endl;cout << "Back element: " << deque.peekBack() << endl;cout << "Removing front: " << deque.removeFront() << endl;deque.printDeque();cout << "Removing back: " << deque.removeBack() << endl;deque.printDeque();cout << "\n===== Testing Stack implemented with Deque =====" << endl;DequeStack stack;stack.push(10);stack.push(20);stack.push(30);stack.printStack();cout << "Top element: " << stack.top() << endl;cout << "Popping: " << stack.pop() << endl;stack.printStack();cout << "\n===== Testing Queue implemented with Deque =====" << endl;DequeQueue queue;queue.enqueue(10);queue.enqueue(20);queue.enqueue(30);queue.printQueue();cout << "Front element: " << queue.front() << endl;cout << "Dequeuing: " << queue.dequeue() << endl;queue.printQueue();return 0;
}
栈与队列
#include <iostream>
#include <stack>
#include <queue>
#include <stdexcept>
using namespace std;// 使用两个栈实现队列
class StackQueue {
private:stack<int> inbox; // 用于入队操作的栈stack<int> outbox; // 用于出队操作的栈// 将元素从inbox转移到outboxvoid transfer() {// 只有当outbox为空时才进行转移,保证元素顺序if (outbox.empty()) {while (!inbox.empty()) {outbox.push(inbox.top());inbox.pop();}}}public:// 入队操作void enqueue(int val) {inbox.push(val);}// 出队操作int dequeue() {if (empty()) {throw runtime_error("Queue underflow");}transfer(); // 确保outbox不为空int val = outbox.top();outbox.pop();return val;}// 获取队头元素int front() {if (empty()) {throw runtime_error("Queue is empty");}transfer(); // 确保outbox不为空return outbox.top();}// 检查队列是否为空bool empty() const {return inbox.empty() && outbox.empty();}// 返回队列中的元素数量int size() const {return inbox.size() + outbox.size();}
};// 使用两个队列实现栈
class QueueStack {
private:queue<int> q1; // 主队列,存储所有元素queue<int> q2; // 辅助队列,用于操作public:// 入栈操作void push(int val) {// 将新元素加入主队列q1.push(val);}// 出栈操作int pop() {if (empty()) {throw runtime_error("Stack underflow");}// 将q1中除最后一个元素外的所有元素移到q2while (q1.size() > 1) {q2.push(q1.front());q1.pop();}// 保存最后一个元素(栈顶元素)int val = q1.front();q1.pop();// 交换q1和q2的角色swap(q1, q2);return val;}// 获取栈顶元素int top() {if (empty()) {throw runtime_error("Stack is empty");}// 将q1中除最后一个元素外的所有元素移到q2while (q1.size() > 1) {q2.push(q1.front());q1.pop();}// 保存最后一个元素(栈顶元素)int val = q1.front();// 将最后一个元素也移到q2q2.push(val);q1.pop();// 交换q1和q2的角色swap(q1, q2);return val;}// 检查栈是否为空bool empty() const {return q1.empty();}// 返回栈中的元素数量int size() const {return q1.size();}
};// 测试程序
int main() {cout << "===== Testing Queue implemented with Stacks =====" << endl;StackQueue queue;cout << "Enqueuing: 10, 20, 30" << endl;queue.enqueue(10);queue.enqueue(20);queue.enqueue(30);cout << "Queue size: " << queue.size() << endl;cout << "Front element: " << queue.front() << endl;cout << "Dequeuing: " << queue.dequeue() << endl;cout << "Front element after dequeue: " << queue.front() << endl;cout << "Queue size after dequeue: " << queue.size() << endl;cout << "Enqueuing: 40" << endl;queue.enqueue(40);cout << "Queue size: " << queue.size() << endl;cout << "Emptying queue..." << endl;while (!queue.empty()) {cout << "Dequeued: " << queue.dequeue() << endl;}cout << "\n===== Testing Stack implemented with Queues =====" << endl;QueueStack stack;cout << "Pushing: 10, 20, 30" << endl;stack.push(10);stack.push(20);stack.push(30);cout << "Stack size: " << stack.size() << endl;cout << "Top element: " << stack.top() << endl;cout << "Popping: " << stack.pop() << endl;cout << "Top element after pop: " << stack.top() << endl;cout << "Stack size after pop: " << stack.size() << endl;cout << "Pushing: 40" << endl;stack.push(40);cout << "Stack size: " << stack.size() << endl;cout << "Top element: " << stack.top() << endl;cout << "Emptying stack..." << endl;while (!stack.empty()) {cout << "Popped: " << stack.pop() << endl;}return 0;
}
1.3.9
编写一段程序,从标准输入得到一个缺少左括号的表达式并打印出补全括号之后的中序表达式。例如,给定输入:[需要图示]你的程序应该输出:[需要图示]
#include <iostream>
#include <stack>
#include <string>
using namespace std;string fixExpression(string& expr) {stack<string> operands;for (char c : expr) {if (c == ')') {// 遇到右括号,需要添加对应的左括号if (!operands.empty()) {string top = operands.top();operands.pop();top = "(" + top + ")";if (!operands.empty()) {// 将补全的表达式与前面的部分合并string prev = operands.top();operands.pop();operands.push(prev + top);} else {operands.push(top);}}} else if (c == '+' || c == '-' || c == '*' || c == '/') {// 遇到操作符,将其添加到前一个操作数之后if (!operands.empty()) {string top = operands.top();operands.pop();top = top + c;operands.push(top);}} else {// 处理操作数(数字或变量)string operand(1, c);if (!operands.empty()) {string prev = operands.top();operands.pop();operands.push(prev + operand);} else {operands.push(operand);}}}return operands.empty() ? "" : operands.top();
}int main() {string expression;cin >> expression;cout << fixExpression(expression) << endl;return 0;
}
1.3.10
编写一个过滤器InfixToPostfix,将算术表达式由中序表达式转为后序表达式。
#include <iostream>
#include <string>
#include <stack>
#include <cctype>using namespace std;class InfixToPostfix {
public:// 获取操作符的优先级int getPrecedence(char op) {if (op == '+' || op == '-')return 1;if (op == '*' || op == '/')return 2;return 0; // 括号的优先级最低}// 中序表达式转后序表达式的主函数string convert(const string& infix) {string postfix = "";stack<char> operatorStack;for (int i = 0; i < infix.length(); i++) {char c = infix[i];// 如果是空格,跳过if (c == ' ')continue;// 如果是操作数(数字或字母),直接添加到结果中if (isalnum(c)) {postfix += c;}// 如果是左括号,入栈else if (c == '(') {operatorStack.push(c);}// 如果是右括号,弹出栈中元素直到遇到左括号else if (c == ')') {while (!operatorStack.empty() && operatorStack.top() != '(') {postfix += operatorStack.top();operatorStack.pop();}// 弹出左括号if (!operatorStack.empty() && operatorStack.top() == '(') {operatorStack.pop();}}// 如果是运算符else {// 弹出优先级大于或等于当前运算符的所有运算符while (!operatorStack.empty() && operatorStack.top() != '(' && getPrecedence(operatorStack.top()) >= getPrecedence(c)) {postfix += operatorStack.top();operatorStack.pop();}// 当前运算符入栈operatorStack.push(c);}}// 处理栈中剩余的运算符while (!operatorStack.empty()) {if (operatorStack.top() != '(') {postfix += operatorStack.top();}operatorStack.pop();}return postfix;}
};int main() {InfixToPostfix converter;string infix;cout << "请输入中序表达式: ";getline(cin, infix);string postfix = converter.convert(infix);cout << "后序表达式: " << postfix << endl;return 0;
}
1.3.11
编写一段程序EvaluatePostfix,从标准输入中得到一个后序表达式,求值并打印结果(将上一题的程序中得到的输出用管道传递给这一段程序可以得到和Evaluate相同的行为)。
const int maxn = 1e4 + 50;
int st[maxn],idx;
class Solution {
public:int evalRPN(vector<string>& tokens) {idx = -1;for(const auto& t : tokens){if(t != "+" and t != "-" and t != "*" and t != "/"){st[++idx] = stoi(t);}else{auto a = st[idx--];auto b = st[idx--];if(t == "+") st[++idx] = b + a;else if(t == "-") st[++idx] = b - a;else if(t == "*") st[++idx] = b * a;else st[++idx] = b / a;}}return st[idx];}
};
1.3.12
编写一个可迭代的Stack用例,它含有一个静态的copy()方法,接受一个字符串的栈作为参数并返回该栈的一个副本。注意:这种能力是迭代器价值的一个重要体现,因为有了它我们无需改变基本API就能够实现这种功能。
1.3.14
编写一个类ResizingArrayQueueOfStrings,使用定长数组实现队列的抽象,然后扩展实现,使用调整数组的方法突破大小的限制。
1.3.15
编写一个Queue的用例,接受一个命令行参数k并打印出标准输入中的倒数第k个字符串(假设标准输入中至少有k个字符串)。
#include <iostream>
#include <string>
#include <deque>
#include <stdexcept>int main(int argc, char* argv[]) {// 检查命令行参数if (argc != 2) {std::cerr << "用法: " << argv[0] << " k" << std::endl;return 1;}// 解析参数kint k;try {k = std::stoi(argv[1]);if (k <= 0) {throw std::invalid_argument("k必须是正整数");}} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << std::endl;return 1;}// 使用deque作为循环缓冲区std::deque<std::string> buffer;std::string line;// 从标准输入读取字符串while (std::getline(std::cin, line)) {if (!line.empty()) {// 将字符串加入缓冲区buffer.push_back(line);// 如果缓冲区大小超过k,移除最早的元素if (buffer.size() > k) {buffer.pop_front();}}}// 检查输入是否至少有k个字符串if (buffer.size() < k) {std::cerr << "错误:输入中的字符串数量少于" << k << "个" << std::endl;return 1;}// 打印倒数第k个字符串// 在含有k个元素的缓冲区中,最前面的元素就是倒数第k个std::cout << buffer.front() << std::endl;return 0;
}
链表练习
struct Node{String item;int val;Node* next;Node(string s, Node* nxt = nullptr) : item(s), val(0), next(nxt) {} Node(int v, Node* nxt = nullptr) : item(""), val(v), next(nxt) {}
};
1.3.19
给出一段代码,删除链表的尾结点,其中链表的首结点为first。
void removeTail(Node* first){Node* cur = first;if(first == nullptr || first->next ==nullptr){delete first;first = nullptr;return;}while(cur->next->next!=nullptr){cur = cur->next;}delete cur->next;cur->next = nullptr;
}
1.3.20
编写一个方法delete(),接受一个int参数k,删除链表的第k个元素(如果它存在的话)。
void delete(int k,Node* first){if(first ==nullptr) return;if(k==1){Node *tmp = first;first = first->next;delete tmp;return;}// 寻找第k-1个结点Node* cur = first;int i = 1;while(i<k-1&&cur!=nullptr){cur = cur->next;i++;}// 如果找不到第k个节点说明链表比k短if(cur==nullptr||cur->next == nullptr) return;Node* tmp = cur->next;cur->next = tmp->next;delete tmp;
}
1.3.21
编写一个方法find(),接受一条链表和一个字符串key作为参数。如果链表中的某个结点的item域的值为key,则方法返回true,否则返回false。
bool find(Node* first, const string& key) {Node* cur = first;while (cur != nullptr) {if (cur->item == key) {return true;}cur = cur->next;}return false;
}
1.3.24
编写一个方法 removeAfter(),接受一个链表结点作为参数并删除该结点的后续结点(如果参数结点或参数结点的后续结点为空则什么也不做)。
void removeAfter(Node* node) {// 如果结点为空或结点的后续结点为空,什么也不做if (node == nullptr || node->next == nullptr) return;// 保存后续结点的引用Node* toDelete = node->next;// 更新链接,跳过后续结点node->next = toDelete->next;// 删除后续结点delete toDelete;
}
1.3.25
编写一个方法 insertAfter(),接受两个链表结点作为参数,将第二个结点插入链表并使之成为第一个结点的后续结点(如果两个参数为空则什么也不做)。
void insertAfter(Node* first, Node* second) {// 如果任一结点为空,什么也不做if (first == nullptr || second == nullptr) return;// 将second插入到first之后second->next = first->next;first->next = second;
}
1.3.26
编写一个方法 remove(),接受一条链表和一个字符串 key 作为参数,删除链表中所有 item 域为 key 的结点。
void remove(Node* head,const string& key){if(head!=nullptr&&head->item==key){{Node* toDelete = head;head = toDelete->next;delete toDelete;}if(head==nullptr) return;Node* cur = head;while(cur->next!=nullptr){if(cur->next->item==key){Node* tmp = cur->next;cur->next = tmp->next;delete tmp;// cur = cur->next; }else {cur = cur->next;}}
}
void remove(Node* &head, const string& key) {// 处理头部节点while (head != nullptr && head->item == key) {Node* toDelete = head;head = head->next;delete toDelete;}// 如果链表变为空if (head == nullptr) return;// 处理剩余节点Node* cur = head;while (cur->next != nullptr) {if (cur->next->item == key) {Node* toDelete = cur->next;cur->next = toDelete->next;delete toDelete;// 注意这里不移动cur,因为cur->next已经变成了新的节点} else {cur = cur->next;}}
}
1.3.27
编写一个方法 max(),接受一条链表的首结点作为参数,返回链表中最大的节点的值。假设所有链接均为正整数,如果链表为空则返回0。
// 1.3.27 返回链表中最大的节点值(假设为整数)
int max(Node* first) {// 如果链表为空,返回0if (first == nullptr) return 0;int maxVal = first->val;Node* cur = first->next;while (cur != nullptr) {if (cur->val > maxVal) {maxVal = cur->val;}cur = cur->next;}return maxVal;
}
1.3.28
用递归解答上一题。
int max(Node* first) {// 基本情况:如果链表为空,返回0if (first == nullptr) return 0;// 递归计算剩余链表中的最大值int restMax = max(first->next);// 返回当前节点值和剩余最大值中的较大者return (first->val > restMax) ? first->val : restMax;
}
1.3.29
用环形链表实现 Queue。环形链表也是一条链表,只是没有任何结点的链接为空,且只需链接非空则 last.next 的值为 first。只能使用一个 Node 类型的实例变量(last)。
struct Node{string item; // 节点存储的数据 Node* next; // 指向下一个节点的指针 // 构造函数 Node(const string& data) : item(data), next(nullptr) {}
}class MyQueue{Node* last;int n;
public:CircularQueue() : last(nullptr), n(0) {}// 检查队列是否为空bool isEmpty() const {return last == nullptr;}// 返回队列中的元素数量int size() const {return n;}// 入队操作(在队尾添加元素)void enqueue(const string& item) {Node* newNode = new Node(item);if (isEmpty()) {// 如果队列为空,创建一个只有一个节点的环newNode->next = newNode;last = newNode;} else {// 将新节点插入到环中,成为新的last->next之后的节点newNode->next = last->next;last->next = newNode;// 更新last指向新节点last = newNode;}n++;}// 出队操作(从队头移除元素)string dequeue() {if (isEmpty()) {throw runtime_error("Queue underflow");}// 获取队头元素(last->next)Node* first = last->next;string item = first->item;if (first == last) {// 如果队列中只有一个元素delete first;last = nullptr;} else {// 从环中移除队头元素last->next = first->next;delete first;}n--;return item;}// 获取队头元素(不移除)string peek() const {if (isEmpty()) {throw runtime_error("Queue underflow");}return last->next->item;}
}
1.3.31
在两个有序链表中的三个变量的作用:reverse、first 和 second。在递归实现中,我们从原链表中提取结点 first 并将它插入到逆链表的开头。我们需要一直保持 first 指向原链表中所有剩余结点的首结点,second 指向原链表中所有剩余结点的第二个结点。
提取的题目还包括两个关于链表反转的实现:
- 使用迭代方法实现链表反转函数 reverse()
- 使用递归方法实现链表反转函数 reverse()
#include <iostream>
using namespace std;// 单向链表节点结构
class Node {
public:int data;Node* next;// 构造函数Node(int val) : data(val), next(nullptr) {}
};// 打印链表(用于测试)
void printList(Node* head) {Node* current = head;while (current != nullptr) {cout << current->data << " -> ";current = current->next;}cout << "nullptr" << endl;
}// 创建测试链表
Node* createTestList() {Node* head = new Node(1);head->next = new Node(2);head->next->next = new Node(3);head->next->next->next = new Node(4);head->next->next->next->next = new Node(5);return head;
}// 释放链表内存
void freeList(Node* head) {while (head != nullptr) {Node* temp = head;head = head->next;delete temp;}
}// 使用迭代方法实现链表反转
Node* reverseIterative(Node* head) {Node* reverse = nullptr; // 反转后的链表头Node* first = head; // 指向原链表中所有剩余结点的首结点while (first != nullptr) {Node* second = first->next; // 指向原链表中所有剩余结点的第二个结点// 将 first 插入到反转链表的开头first->next = reverse;reverse = first;// 移动到下一个节点first = second;}return reverse;
}// 使用递归方法实现链表反转
Node* reverseRecursive(Node* head) {// 基本情况:空链表或只有一个节点if (head == nullptr || head->next == nullptr) {return head;}// first 指向原链表的第一个节点Node* first = head;// second 指向原链表的第二个节点Node* second = first->next;// 递归反转剩余的链表(从第二个节点开始)Node* rest = reverseRecursive(second);// 将第一个节点连接到反转后链表的末尾second->next = first;first->next = nullptr;return rest;
}// 另一种递归实现(更接近题目描述)
Node* reverseRecursiveAlt(Node* first) {// 基本情况:空链表if (first == nullptr) return nullptr;// 基本情况:只有一个节点if (first->next == nullptr) return first;// second 指向原链表的第二个节点Node* second = first->next;// 递归反转剩余的链表(从第二个节点开始)Node* rest = reverseRecursiveAlt(second);// 将第二个节点指向第一个节点second->next = first;// 将第一个节点指向 nullptr(它将成为新链表的最后一个节点)first->next = nullptr;return rest;
}int main() {// 测试迭代方法return 0;
}
1.3.32
实现一个泛型类 DoubleNode 用来构造双向链表,其中每个结点都含有一个指向前面元素的引用和一项指向后面元素的引用(如果不存在则为 null)。为以下任务实现若干静态方法:在表头插入结点、在表尾插入结点、从表头删除结点、从表尾删除结点、在指定结点之前插入新结点、在指定结点之后插入新结点、删除指定结点。
#include <iostream>
#include <stdexcept>
using namespace std;// 泛型双向链表节点类
template <typename T>
class DoubleNode {
public:T data;DoubleNode<T>* prev;DoubleNode<T>* next;// 构造函数DoubleNode(T val) : data(val), prev(nullptr), next(nullptr) {}
};// 双向链表操作的静态方法类
template <typename T>
class DoublyLinkedList {
public:// 在表头插入结点static DoubleNode<T>* insertAtHead(DoubleNode<T>* head, T val) {DoubleNode<T>* newNode = new DoubleNode<T>(val);if (head == nullptr) {return newNode;}newNode->next = head;head->prev = newNode;return newNode;}// 在表尾插入结点static DoubleNode<T>* insertAtTail(DoubleNode<T>* head, T val) {DoubleNode<T>* newNode = new DoubleNode<T>(val);if (head == nullptr) {return newNode;}// 找到链表的尾结点DoubleNode<T>* current = head;while (current->next != nullptr) {current = current->next;}current->next = newNode;newNode->prev = current;return head;}// 从表头删除结点static DoubleNode<T>* removeFromHead(DoubleNode<T>* head) {if (head == nullptr) {throw runtime_error("Cannot remove from empty list");}DoubleNode<T>* newHead = head->next;if (newHead != nullptr) {newHead->prev = nullptr;}delete head;return newHead;}// 从表尾删除结点static DoubleNode<T>* removeFromTail(DoubleNode<T>* head) {if (head == nullptr) {throw runtime_error("Cannot remove from empty list");}// 如果只有一个节点if (head->next == nullptr) {delete head;return nullptr;}// 找到尾结点和倒数第二个结点DoubleNode<T>* current = head;while (current->next != nullptr) {current = current->next;}// 删除尾结点DoubleNode<T>* newTail = current->prev;newTail->next = nullptr;delete current;return head;}// 在指定结点之前插入新结点static DoubleNode<T>* insertBefore(DoubleNode<T>* head, DoubleNode<T>* node, T val) {if (head == nullptr || node == nullptr) {throw runtime_error("Invalid operation");}// 如果在头结点之前插入if (node == head) {return insertAtHead(head, val);}DoubleNode<T>* newNode = new DoubleNode<T>(val);DoubleNode<T>* prevNode = node->prev;newNode->next = node;newNode->prev = prevNode;node->prev = newNode;prevNode->next = newNode;return head;}// 在指定结点之后插入新结点static DoubleNode<T>* insertAfter(DoubleNode<T>* head, DoubleNode<T>* node, T val) {if (head == nullptr || node == nullptr) {throw runtime_error("Invalid operation");}DoubleNode<T>* newNode = new DoubleNode<T>(val);DoubleNode<T>* nextNode = node->next;newNode->prev = node;newNode->next = nextNode;node->next = newNode;if (nextNode != nullptr) {nextNode->prev = newNode;}return head;}// 删除指定结点static DoubleNode<T>* remove(DoubleNode<T>* head, DoubleNode<T>* node) {if (head == nullptr || node == nullptr) {throw runtime_error("Invalid operation");}// 如果删除的是头结点if (node == head) {return removeFromHead(head);}DoubleNode<T>* prevNode = node->prev;DoubleNode<T>* nextNode = node->next;prevNode->next = nextNode;if (nextNode != nullptr) {nextNode->prev = prevNode;}delete node;return head;}// 查找指定值的节点static DoubleNode<T>* find(DoubleNode<T>* head, T val) {DoubleNode<T>* current = head;while (current != nullptr) {if (current->data == val) {return current;}current = current->next;}return nullptr;}// 打印链表(用于测试)static void printList(DoubleNode<T>* head) {cout << "Forward: nullptr <-> ";DoubleNode<T>* current = head;while (current != nullptr) {cout << current->data << " <-> ";current = current->next;}cout << "nullptr" << endl;// 从尾到头打印链表(验证prev指针)cout << "Backward: nullptr <-> ";if (head != nullptr) {// 找到尾结点current = head;while (current->next != nullptr) {current = current->next;}// 从尾到头打印while (current != nullptr) {cout << current->data << " <-> ";current = current->prev;}}cout << "nullptr" << endl;}// 释放链表内存static void freeList(DoubleNode<T>* head) {while (head != nullptr) {DoubleNode<T>* temp = head;head = head->next;delete temp;}}
};// 测试程序
int main() {DoubleNode<int>* list = nullptr;return 0;
}
lc
138 随机链表的复制
25 k个一组反转链表
实现最小栈
相关文章:
算法四 习题 1.3
数组实现栈 #include <iostream> #include <vector> #include <stdexcept> using namespace std;class MyStack { private:vector<int> data; // 用于存储栈元素的数组public:// 构造函数MyStack() {}// 入栈操作void push(int val) {data.push_back…...
el-tabs与table样式冲突导致高度失效问题解决(vue2+elementui)
背景 正常的el-table能根据父容器自动计算剩余高度,并会在列表中判断自适应去放出滚动条。而el-tabs本身就是自适应el-tab-pane内容的高度来进行自适应调节,这样就会导致el-table计算不了当前剩余的高度,所以当el-tabs里面包含el-table时&am…...
Access开发:轻松一键将 Access 全库表格导出为 Excel
hi,大家好呀! 在日常工作中,Access 常常是我们忠实的数据管家,默默守护着项目信息、客户列表或是库存记录。它结构清晰,录入便捷,对于许多中小型应用场景来说,无疑是个得力助手。然而ÿ…...
合并多个Excel文件到一个文件,并保留格式
合并多个Excel文件到一个文件,并保留格式 需求介绍第一步:创建目标文件第二步:创建任务列表第三步:合并文件第四步:处理合并后的文件之调用程序打开并保存一次之前生成的Excel文件第五步:处理合并后的文件之…...
使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第十讲)
这一期我们讲解demo中登录、ok按键的回调函数以及界面的美化,以下是上期界面的图片如图所示: 首先点击界面在右侧的工具栏中调配颜色渐变色,具体设置如下图所示: 然后是关于界面内框也就是容器的美化,具体如下图所示…...
论文笔记(八十二)Transformers without Normalization
Transformers without Normalization 文章概括Abstract1 引言2 背景:归一化层3 归一化层做什么?4 动态 Tanh (Dynamic Tanh (DyT))5 实验6 分析6.1 DyT \text{DyT} DyT 的效率6.2 tanh \text{tanh} tanh 和 α α α 的消融实验…...
Mysql之数据库基础
🌟 各位看官好,我是maomi_9526! 🌍 种一棵树最好是十年前,其次是现在! 🚀 今天来学习Mysql的相关知识。 👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更…...
shell(5)
位置参数变量 1.介绍 当我们执行一个shell脚本时,如果希望获取到命令行的参数信息,就可以使用到位置参数变量. 比如:./myshell.sh100 200,这就是一个执行shell的命令行,可以在myshell脚本中获取到参数信息. 2.基本语法 $n(功能描述:n为数字,$0代表命令…...
VARIAN安捷伦真空泵维修清洁保养操作SOP换油操作流程内部转子图文并茂内部培训手侧
VARIAN安捷伦真空泵维修清洁保养操作SOP换油操作流程内部转子图文并茂内部培训手侧...
动画震动效果
项目场景: 提示:这里简述项目相关背景: 在有的相关目中特别是在C端一般都要求做的炫酷一些,这就需要一些简易的动画效果,这里就弄了一个简易的震动的效果如下视频所示 让图标一大一小的震动视频 分析: 提…...
DB-GPT V0.7.1 版本更新:支持多模态模型、支持 Qwen3 系列,GLM4 系列模型 、支持Oracle数据库等
V0.7.1版本主要新增、增强了以下核心特性 🍀DB-GPT支持多模态模型。 🍀DB-GPT支持 Qwen3 系列,GLM4 系列模型。 🍀 MCP支持 SSE 权限认证和 SSL/TLS 安全通信。 🍀 支持Oracle数据库。 🍀 支持 Infini…...
C++23 std::invoke_r:调用可调用 (Callable) 对象 (P2136R3)
文章目录 引言背景知识回顾可调用对象C17的std::invoke std::invoke_r的诞生提案背景std::invoke_r的定义参数和返回值异常说明 std::invoke_r的使用场景指定返回类型丢弃返回值 std::invoke_r与std::invoke的对比功能差异使用场景差异 结论 引言 在C的发展历程中,…...
pymysql
参数(会导致SQL注入) import pymysql# 创建数据库连接 conn pymysql.connect(user "root",password "root",host "127.0.0.1",port 3306,database "test" )# 创建游标对象 cur conn.cursor(cursorpymysql.…...
基于Spring Boot + Vue 项目中引入deepseek方法
准备工作 在开始调用 DeepSeek API 之前,你需要完成以下准备工作: 1.访问 DeepSeek 官网,注册一个账号。 2.获取 API 密钥:登录 DeepSeek 平台,进入 API 管理 页面。创建一个新的 API 密钥(API Key&#x…...
Spring Boot集成Kafka并使用多个死信队列的完整示例
以下是Spring Boot集成Kafka并使用多个死信队列的完整示例,包含代码和配置说明。 1. 添加依赖 (pom.xml) <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId&…...
全面了解CSS语法 ! ! !
CSS(层叠样式表)是网页设计的灵魂之一,它赋予了网页活力与美感。无论是为一个简单的个人博客增添色彩,还是为复杂的企业网站设计布局,CSS都是不可或缺的工具。那么,CSS语法到底是什么样的呢?它背…...
Springboot使用ThreadLocal提供线程局部变量,传递登录用户名
文章目录 概述使用创建ThreadLocalUtil工具类在登录拦截器中使用ThreadLocal存储登录用户名在/userInfo接口中获取登录用户名 注意事项参考视频 概述 使用 创建ThreadLocalUtil工具类 utils/ThreadLocalUtil.java package org.example.utils;/*** ThreadLocal 工具类*/ Supp…...
排序算法——选择排序
一、介绍 「排序算法sortingalgorithm」用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用,因为有序数据通常能够被更有效地查找、分析和处理。 如图所示,排序算法中的数据类型可以是整数、浮点数、字符或字符串等。排序的判断规则可根据需求…...
AlphaFold蛋白质结构数据库介绍
AlphaFold Protein Structure Database (AlphaFold DB) 是 DeepMind + EMBL-EBI 合作开发的公开蛋白质结构预测数据库,是利用 AlphaFold2/AlphaFold3 AI模型 预测的全基因组级蛋白质三维结构库。 网址: https://alphafold.ebi.ac.uk 项目内容主办单位DeepMind + EMBL-EBI上线…...
Roboflow标注数据集
使用Roboflow进行标注 关键点标注目标检测标注图像分类标注分割标注 Roboflow是一款易于使用的在线 图像标注。 关键点标注 每个图像的标注包括: 1、边界框坐标(每个物品应该有一个边界框,用*[x1,y1,x2,y2]*格式即左上角和右下角点描述&…...
大厂经验:第三方包Paramunittest参数化 VS Unittest内置参数化文本管理器subtest
大厂经验:第三方包Paramunittest参数化 VS Unittest内置参数化文本管理器subtest 代码解析 Paramunittest 核心逻辑 paramunittest.parametrized((Testerr, test, Invalid Login or Password., test_login_admin is passed),(Sam, test, Invalid Login or Passwo…...
[特殊字符]适合五四青年节的SVG模版[特殊字符]
宝藏模版 往期推荐(点击阅读): 趣味效果|高大上|可爱风|年终总结I|年终总结II|循环特效|情人节I|情人节II|情人节IIII|妇女节I&…...
当插入排序遇上“凌波微步“——希尔排序的奇幻漂流
文章目录 一、排序江湖的隐藏高手二、分而治之的魔法1. 核心思想拆解2. 动态演示(脑补版) 三、C语言实现大揭秘代码要点解析: 四、性能分析与实战技巧1. 时间复杂度迷思2. 实测性能对比 五、为什么说它永不过时?六、进阶思考题 一…...
【Hot 100】 148. 排序链表
目录 引言十大排序算法1. 冒泡排序 (Bubble Sort)2. 选择排序 (Selection Sort)3. 插入排序 (Insertion Sort)4. 希尔排序 (Shell Sort)简单代码说明关键特点 5. 归并排序 (Merge Sort)6. 快速排序 (Quick Sort)7. 堆排序 (Heap Sort)8. 计数排序 (Counting Sort)9. 桶排序 (Bu…...
大连理工大学选修课——机器学习笔记(9):线性判别式与逻辑回归
线性判别式与逻辑回归 概述 判别式方法 产生式模型需要计算输入、输出的联合概率 需要知道样本的概率分布,定义似然密度的隐式参数也称为基于似然的分类 判别式模型直接构造判别式 g i ( x ∣ θ i ) g_i(x|\theta_i) gi(x∣θi),显式定义判别式…...
[特殊字符] 开发工作高内存占用场景下,Windows 内存压缩机制是否应该启用?实测分析与优化建议
在日常开发中,我们往往需要同时运行多个高占用内存的工具,例如: IntelliJ IDEA VMware 虚拟机 多个 Java 后端程序 这些应用程序非常“吃内存”,轻松就能把 16GB、甚至 24GB 的物理内存用满。那么,Windows 的“内存…...
相机的基础架构
📷 相机相关基础架构学习路径 一、了解手机相机系统架构 Android Camera HAL(如果你是做 Android 平台) 学习 Camera HAL3 架构(基于 camera_device_t, camera3_device_ops 接口) 熟悉 CameraService → CameraProvid…...
C# 类成员的访问:内部与外部
在 C# 编程中,了解如何从类的内部和外部访问成员是非常重要的。本文将详细介绍这两种访问方式,并通过示例代码展示其具体应用。 从类的内部访问成员 类的成员可以在类的内部自由地互相访问,即使这些成员被声明为private。在类的方法中&…...
DAPO:对GRPO的几点改进
DAPO:对GRPO的几点改进 TL; DR:对 GRPO 的几处细节进行了优化,包括去除 KL 约束、解耦 ppo-clip 的上下界,上界设置更高以鼓励探索、超长回答过滤、token level 损失计算等。相比于原始 GRPO,在 AIME24 上提升非常显著…...
从零构建 MCP Server 与 Client:打造你的第一个 AI 工具集成应用
目录 🚀 从零构建 MCP Server 与 Client:打造你的第一个 AI 工具集成应用 🧱 1. 准备工作 🛠️ 2. 构建 MCP Server(服务端) 2.1 初始化服务器 🧩 3. 添加自定义工具(Tools&…...
2025.4.27 Vue.js 基础学习笔记
一、Vue.js 简介 Vue.js(简称 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。它具有以下特点: 轻量级 :核心库体积小,性能优秀,不占用过多资源,加载速度快,适合各种规模的应…...
基于用户场景的汽车行驶工况构建:数据驱动下的能耗优化革命
行业现状:标准工况与用户场景的割裂 全球汽车行业普遍采用WLTC工况进行能耗测试,但其与真实道路场景差异显著。据研究,WLTC工况下车辆能耗数据比实际道路低10%-30%,导致用户对续航虚标投诉激增(数据来源:东…...
IoTDB集群部署中的网络、存储与负载配置优化
一、引言 在现代计算机系统和应用程序中,网络I/O性能是决定整体系统表现的关键因素之一。特别是在IoTDB集群环境中,网络I/O的重要性尤为突出,特别是在处理大量测点数据、客户端请求以及集群内部通信时。本文将介绍IoTDB数据库集群部署过程中…...
Unity URPShader:实现和PS一样的色相/饱和度调整参数效果(修复)
目录 前言: 一、问题原因 二、算法修复 三、全代码 前言: 在之前的文章我已经实现了标题所述的内容功能:Unity URPShader:实现和PS一样的色相/饱和度调整参数效果-CSDN博客 但在偶然测试的时候,发现当采样的图片为…...
告别手动时代!物联网软件开发让万物自动互联
清晨,智能窗帘随着阳光自动拉开;运动时,手表精准记录着健康数据;回到家,室温早已调节至最舒适状态...这些场景的实现,都离不开物联网软件开发的技术支撑。在智能家居软件开发、智能穿戴软件开发、医疗器械软…...
Vue ui初始化项目并使用iview写一个菜单导航
winR 输入命令 vue ui浏览器会自动打开http://localhost:8000/ 找到创建 image.png 选择一个目录创建vue项目 image.png 点击再此创建新项目 image.png 我一般都是再已经有git仓库的目录进行项目创建,所以这个勾去掉 点击下一步 image.png 这里可以选择默认&#x…...
函数调用及Chain——SQL+GLM
Langchainchain数据库操作_langchain 操作数据库-CSDN博客 本文和基于上述链接 进一步。 初始化数据库&模型 # temperature0,此处仅需要SQL语句,不需要多样化返回。 from langchain.chains.sql_database.query import create_sql_query_chain from …...
数据科学与计算
Seaborn的介绍 Seaborn 是一个建立在 Matplotlib 基础之上的 Python 数据可视化库,专注于绘制各种统计图形,以便更轻松地呈现和理解数据。 Seaborn 的设计目标是简化统计数据可视化的过程,提供高级接口和美观的默认主题,使得用户…...
【AI提示词】二八法则专家
提示说明 精通二八法则(帕累托法则)的广泛应用,擅长将其应用于商业、管理、个人发展等领域,深入理解其在不同场景中的具体表现和实际意义。 提示词 # Role: 二八法则专家## Profile - language: 中文 - description: 精通二八法…...
PostgreSQL Patroni集群组件作用介绍:Patroni、etcd、HAProxy、Keepalived、Watchdog
1. Watchdog 简介 1.1 核心作用 • 主节点故障检测 Watchdog 会定时检测数据库主节点(或 Pgpool 主节点)的运行状态。 一旦主节点宕机,它会发起故障切换请求。 • 协调主备切换 多个 Pgpool 节点时,Watchdog 保证只有一个 Pg…...
【计算机视觉】图像分割:Segment Anything (SAM):通用图像分割的范式革命
Segment Anything:通用图像分割的范式革命 技术突破与架构创新核心设计理念关键技术组件 环境配置与快速开始硬件要求安装步骤基础使用示例 深度功能解析1. 多模态提示融合2. 全图分割生成3. 高分辨率处理 模型微调与定制1. 自定义数据集准备2. 微调训练配置 常见问…...
改进系列(10):基于SwinTransformer+CBAM+多尺度特征融合+FocalLoss改进:自动驾驶地面路况识别
目录 1.代码介绍 1. 主训练脚本train.py 2. 工具函数与模型定义utils.py 3. GUI界面应用infer_QT.py 2.自动驾驶地面路况识别 3.训练过程 4.推理 5.下载 代码已经封装好,对小白友好。 想要更换数据集,参考readme文件摆放好数据集即可,…...
大型连锁酒店集团数据湖应用示例
目录 一、应用前面临的严峻背景 二、数据湖的精细化构建过程 (一)全域数据整合规划 (二)高效的数据摄取与存储架构搭建 (三)完善的元数据管理体系建设 (四)强大的数据分析平台…...
element.scrollIntoView(options)
handleNextClick 函数详解 功能描述 该函数实现在一个表格中“跳转到下一行”的功能,并将目标行滚动至视图顶部。通常用于导航或高亮显示当前选中的数据行。 const handleNextClick () > {// 如果当前已经是最后一行,则不执行后续操作if (current…...
python查看指定的进程是否存在
import os class Paly_Install(object):"""项目根目录"""def get_path(self):self.basedir os.path.dirname(os.path.abspath(__file__))"""安装失败的txt文件"""def test_app(self):self.app["com.faceboo…...
HAproxy+keepalived+tomcat部署高可用负载均衡实践
目录 一、前言 二、服务器规划 三、部署 1、jdk18安装 2、tomcat安装 3、haproxy安装 4、keepalived安装 三、测试 1、服务器停机测试 2、停止haproxy服务测试 总结 一、前言 HAProxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、…...
C++负载均衡远程调用学习之自定义内存池管理
目录 1.内存管理_io_buf的结构分析 2.Lars_内存管理_io_buf内存块的实现 3.buf总结 4.buf_pool连接池的单例模式设计和基本属性 5.buf_pool的初始化构造内存池 6.buf_pool的申请内存和重置内存实现 7.课前回顾 1.内存管理_io_buf的结构分析 ## 3) Lars系统总体架构 …...
mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz的下载安装和使用
资源获取链接: mysql-5.7.24-linux-glibc2.12-x86-64.tar.gz和使用说明资源-CSDN文库 详细作用 数据库服务器的核心文件: 这是一个压缩包,解压后包含 MySQL 数据库服务器的可执行文件、库文件、配置文件模板等。 它用于在 Linux 系统上安装…...
Kafka Producer的acks参数对消息可靠性有何影响?
1. acks0 可靠性最低生产者发送消息后不等待任何Broker确认可能丢失消息(Broker处理失败/网络丢失时无法感知)吞吐量最高,适用于允许数据丢失的场景(如日志收集) 2. acks1 (默认值) Leader副本确认模式生产者等待Le…...
Linux-04-用户管理命令
一、useradd添加新用户: 基本语法: useradd 用户名:添加新用户 useradd -g 组名 用户:添加新用户到某个组二、passwd设置用户密码: 基本语法: passwd 用户名:设置用户名密码 三、id查看用户是否存在: 基本语法: id 用户名 四、su切换用户: 基本语法: su 用户名称:切换用…...