20道面试题001

常考语法就是指针,指针与数组、指针与字符串、指针与结构体、指针与函数之间的关系与使用, 以上课为准,辅助《深度理解C指针》这本书。
1. 指针与数组
定义: 数组名在表达式中通常被视为指向数组首元素的指针。
访问元素: 可以通过指针访问数组元素。例如,arr[i]等效于*(arr + i)。
传递数组: 在函数中可以通过指针参数传递数组,以避免复制整个数组2. 指针与字符串
定义: 字符串在C中实际上是一个字符数组,字符串常量(如 "hello")被视为字符指针。
操作: 可以使用指针来遍历字符串
传递字符串: 字符串可以作为指向字符的指针传递给函数3. 指针与结构体
定义: 结构体可以通过指针进行操作,指针可以指向结构体类型。
访问成员: 使用箭头操作符(->)来访问结构体的成员
动态分配: 可以使用指针动态分配结构体的内存4. 指针与函数
函数指针: 可以定义指向函数的指针,允许将函数作为参数传递或返回。
回调函数: 使用函数指针实现回调机制
传递指针: 可以通过指针传递参数,实现对函数外变量的修改    
———————————————————————
2、数据结构与算法:
常见的八大排序,特别的:快排、堆排,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


3. 快速排序
快速排序是基于分治法的排序算法。它通过选择一个"基准"元素,将数组分为两个部分,小于基准的放左边,大于基准的放右边,然后递归地对这两部分进行排序。平均时间复杂度为 (O(n \log n)),最坏情况为 (O(n^2))。4. 归并排序
归并排序也是一种基于分治法的算法。它将数组分成两半,分别对两部分进行递归排序,然后将两部分合并成一个有序的数组。归并排序的时间复杂度为 (O(n \log n)),并且是稳定的排序算法。5. 希尔排序
希尔排序是对插入排序的一种改进。它通过将数组分成若干个子序列,对每个子序列进行插入排序,然后逐步减少子序列的数量,最终完成整个数组的排序。希尔排序的时间复杂度因增量选择的不同而有所不同,平均一般为 (O(n^{1.3})) 到 (O(n^{2}))。6. 堆排序
堆排序利用堆这种数据结构来进行排序。首先将待排序的数组构建成一个最大堆,然后将堆顶元素(最大值)与数组的最后一个元素交换,并重新调整堆。这个过程重复进行,直到堆为空。时间复杂度为 (O(n \log n))。7. 插入排序
插入排序通过构建一个有序序列,将未排序的元素逐个插入到已排序序列中,直到所有元素都有序。它适合于数据量较小的场景,时间复杂度为 (O(n^2)),但在数据基本有序时效率较高。  1. 选择排序
基本思想是:首先找到数组中的最小元素,将其与数组的第一个元素交换;然后在剩余的元素中继续寻找最小元素,并将其与第二个元素交换,依此类推,直到整个数组排序完成。时间复杂度为 (O(n^2))。2. 冒泡排序
冒泡排序通过重复比较相邻的元素并交换它们来实现排序。如果在一趟比较中没有发生任何交换,则说明已经排序完成。这个过程重复进行,直到整个数组有序。时间复杂度同样为 (O(n^2))。    
数据结构中:链表的操作(创建、插入、删除、逆置、判断环等)、
#include <stdio.h>
#include <stdlib.h>// 链表节点结构
typedef struct ListNode {int value;struct ListNode* next;
} ListNode;// 创建链表
ListNode* create_linked_list(int* values, int size) {if (size == 0) return NULL;ListNode* head = (ListNode*)malloc(sizeof(ListNode));head->value = values[0];head->next = NULL;ListNode* current = head;for (int i = 1; i < size; i++) {ListNode* new_node = (ListNode*)malloc(sizeof(ListNode));new_node->value = values[i];new_node->next = NULL;current->next = new_node;current = new_node;}return head;
}
// 在头部插入
ListNode* insert_at_head(ListNode* head, int value) {ListNode* new_node = (ListNode*)malloc(sizeof(ListNode));new_node->value = value;new_node->next = head;return new_node;
}// 在尾部插入
ListNode* insert_at_tail(ListNode* head, int value) {ListNode* new_node = (ListNode*)malloc(sizeof(ListNode));new_node->value = value;new_node->next = NULL;if (!head) return new_node;ListNode* current = head;while (current->next) {current = current->next;}current->next = new_node;return head;
}// 在指定位置插入
ListNode* insert_at_position(ListNode* head, int value, int position) {if (position == 0) {return insert_at_head(head, value);}ListNode* new_node = (ListNode*)malloc(sizeof(ListNode));new_node->value = value;ListNode* current = head;for (int i = 0; current != NULL && i < position - 1; i++) {current = current->next;}if (current == NULL) {free(new_node); // 释放内存return head; // 超出范围}new_node->next = current->next;current->next = new_node;return head;
}
// 删除指定值的节点
ListNode* delete_node(ListNode* head, int value) {if (!head) return head;if (head->value == value) {ListNode* temp = head->next;free(head);return temp; // 删除头节点}ListNode* current = head;while (current->next) {if (current->next->value == value) {ListNode* temp = current->next;current->next = temp->next;free(temp);return head;}current = current->next;}return head;
}
// 逆置链表
ListNode* reverse_linked_list(ListNode* head) {ListNode* prev = NULL;ListNode* current = head;ListNode* next_node = NULL;while (current) {next_node = current->next; // 保存下一个节点current->next = prev;      // 逆转指针prev = current;            // 移动 prevcurrent = next_node;       // 移动 current}return prev; // 新的头节点
}
// 判断链表是否存在环        快慢指针
int has_cycle(ListNode* head) {ListNode* slow = head;ListNode* fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;if (slow == fast) {return 1; // 存在环}}return 0; // 不存在环
}

int main() {int values[] = {1, 2, 3, 4, 5};ListNode* head = create_linked_list(values, 5);// 测试插入head = insert_at_head(head, 0);head = insert_at_tail(head, 6);head = insert_at_position(head, 7, 3);// 测试删除head = delete_node(head, 3);// 逆置链表head = reverse_linked_list(head);// 检查环printf("Has cycle: %d\n", has_cycle(head));// 释放链表内存while (head) {ListNode* temp = head;head = head->next;free(temp);}return 0;
}
树(平衡二叉树、B+树(数据库索引底层数据结构)、
//1. 树的基本概念
树是一种非线性数据结构,由节点组成,节点之间通过边连接。树有以下基本属性:根节点:树的顶层节点,没有父节点。
叶子节点:没有子节点的节点。
高度:树中节点到叶子的最长路径长度。
深度:节点到根节点的路径长度。
子树:从某个节点开始的子结构。//2. 平衡二叉树//2.1 定义
平衡二叉树(AVL树)是一种特殊的二叉搜索树(BST),它保证了任何节点的两个子树的==高度差==不超过 1,从而保持树的平衡。这种平衡性确保了查找、插入和删除操作的时间复杂度为 O(log n)。//2.2 特点
自平衡:在每次插入或删除之后,AVL树会自动进行==旋转==以保持平衡。
旋转操作:主要有四种旋转操作:右旋 (Right Rotation):当树的左子树比右子树高两个层级,并且插入操作发生在左子树的左侧时(左左情况),需要进行右旋。用于解决左子树过高的问题。
左旋(Left Rotation):当树的右子树比左子树高两个层级,并且插入操作发生在右子树的右侧时(右右情况),需要进行左旋用于解决右子树过高的问题。
左-右旋 (Left-Right Rotation):当树的左子树比右子树高两个层级,并且插入操作发生在左子树的右侧时(左右情况),首先进行左旋,然后进行右旋。用于处理左子树右侧过高的情况。
右-左旋 (Right-Left Rotation):当树的右子树比左子树高两个层级,并且插入操作发生在右子树的左侧时(右左情况),首先进行右旋,然后进行左旋。用于处理右子树左侧过高的情况。//2.3 时间复杂度
查找:O(log n)
插入:O(log n)
删除:O(log n)//2.4 应用
数据库索引
内存中的动态数据结构
实现优先队列//3. B+ 树//3.1 定义
B+ 树是一种自平衡的树数据结构,广泛用于数据库和文件系统中,特别适合于大规模数据的存储与检索。B+ 树是 B 树的一种变体,它具有所有叶子节点在同一层,并且所有数据都存储在叶子节点中。//3.2 特点
多路平衡树:每个节点可以有多个子节点,而不仅仅是两个。B+ 树的每个节点通常包含多个键值对。
叶子节点链接:所有叶子节点通过指针相互链接,便于范围查询。
顺序访问:由于叶子节点的顺序链接,B+ 树能够高效地进行区间查询。//3.3 节点结构
内部节点:存储指向子节点的指针和键的值,用于导航。
叶子节点:存储实际的数据记录,并且与其他叶子节点通过指针相连。//3.4 操作
查找:与 B 树类似,使用关键字进行查找,复杂度为 O(log n)。
插入:插入操作可能需要分裂节点,复杂度为 O(log n)。
删除:也会涉及节点的合并和调整,复杂度为 O(log n)。//3.5 应用
数据库索引(如 MySQL 的 InnoDB 存储引擎)
文件系统(如 NTFS 和 ext4)
大数据存储与检索
#include <stdio.h>
#include <stdlib.h>typedef struct Node {int key;struct Node* left;struct Node* right;int height;
} Node;// 函数声明
Node* createNode(int key);            // 创建新节点
int getHeight(Node* N);               // 获取节点高度
int getBalance(Node* N);              // 获取平衡因子
Node* rightRotate(Node* y);           // 右旋
Node* leftRotate(Node* x);            // 左旋
Node* insert(Node* node, int key);    // 插入节点(内部包括四种旋转)
Node* minValueNode(Node* node);       // 获取最小值节点
Node* deleteNode(Node* root, int key);// 删除节点
void inorder(Node* root);             // 中序遍历
void preorder(Node* root);            // 前序遍历
void postorder(Node* root);           // 后序遍历
Node* search(Node* root, int key);
// 创建新节点
Node* createNode(int key) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->key = key;newNode->left = NULL;newNode->right = NULL;newNode->height = 1; // 新节点初始高度为 1return newNode;
}// 获取节点高度
int getHeight(Node* N) {if (N == NULL)return 0;return N->height;
}// 获取平衡因子
int getBalance(Node* N) {if (N == NULL)return 0;return getHeight(N->left) - getHeight(N->right);
}
// 右旋
Node* rightRotate(Node* y) {Node* x = y->left;Node* T2 = x->right;// 旋转x->right = y;y->left = T2;// 更新高度y->height = 1 + max(getHeight(y->left), getHeight(y->right));x->height = 1 + max(getHeight(x->left), getHeight(x->right));return x;
}// 左旋
Node* leftRotate(Node* x) {Node* y = x->right;Node* T2 = y->left;// 旋转y->left = x;x->right = T2;// 更新高度x->height = 1 + max(getHeight(x->left), getHeight(x->right));y->height = 1 + max(getHeight(y->left), getHeight(y->right));return y;
}
// 插入节点
Node* insert(Node* node, int key) {// 1. 执行常规的 BST 插入if (node == NULL)return createNode(key);if (key < node->key)node->left = insert(node->left, key);else if (key > node->key)node->right = insert(node->right, key);else // 不允许重复值return node;// 2. 更新节点的高度node->height = 1 + max(getHeight(node->left), getHeight(node->right));// 3. 获取平衡因子并检查是否失衡int balance = getBalance(node);// 如果失衡,则进行旋转// 左左情况if (balance > 1 && key < node->left->key)return rightRotate(node);// 右右情况if (balance < -1 && key > node->right->key)return leftRotate(node);// 左右情况if (balance > 1 && key > node->left->key) {node->left = leftRotate(node->left);return rightRotate(node);}// 右左情况if (balance < -1 && key < node->right->key) {node->right = rightRotate(node->right);return leftRotate(node);}return node;
}
// 获取最小值节点
Node* minValueNode(Node* node) {Node* current = node;while (current->left != NULL)current = current->left;return current;
}// 删除节点
Node* deleteNode(Node* root, int key) {// 1. 执行常规的 BST 删除if (root == NULL)return root;if (key < root->key)root->left = deleteNode(root->left, key);else if (key > root->key)root->right = deleteNode(root->right, key);else {// 找到该节点if ((root->left == NULL) || (root->right == NULL)) {Node* temp = root->left ? root->left : root->right;// 没有子节点if (temp == NULL) {temp = root;root = NULL;} else // 一个子节点*root = *temp; // 复制非空子节点的内容free(temp);} else {// 有两个子节点Node* temp = minValueNode(root->right);root->key = temp->key; // 复制后继节点的值root->right = deleteNode(root->right, temp->key); // 删除后继节点}}// 如果树只有一个节点,返回if (root == NULL)return root;// 更新节点高度root->height = 1 + max(getHeight(root->left), getHeight(root->right));// 获取平衡因子并检查是否失衡int balance = getBalance(root);// 如果失衡,则进行旋转// 左左情况if (balance > 1 && getBalance(root->left) >= 0)return rightRotate(root);// 左右情况if (balance > 1 && getBalance(root->left) < 0) {root->left = leftRotate(root->left);return rightRotate(root);}// 右右情况if (balance < -1 && getBalance(root->right) <= 0)return leftRotate(root);// 右左情况if (balance < -1 && getBalance(root->right) > 0) {root->right = rightRotate(root->right);return leftRotate(root);}return root;
}
// 中序遍历
void inorder(Node* root) {if (root != NULL) {inorder(root->left);printf("%d ", root->key);inorder(root->right);}
}// 前序遍历
void preorder(Node* root) {if (root != NULL) {printf("%d ", root->key);preorder(root->left);preorder(root->right);}
}// 后序遍历
void postorder(Node* root) {if (root != NULL) {postorder(root->left);postorder(root->right);printf("%d ", root->key);}
}// 查找节点
Node* search(Node* root, int key) {if (root == NULL || root->key == key)return root;if (key < root->key)return search(root->left, key);return search(root->right, key);
}
// 主程序
int main() {Node* root = NULL;// 插入节点int keys[] = {10, 20, 30, 40, 50, 25};for (int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {root = insert(root, keys[i]);}printf("中序遍历结果:");inorder(root);printf("\n");printf("前序遍历结果:");preorder(root);printf("\n");printf("后序遍历结果:");postorder(root);printf("\n");// 查找节点int searchKey = 25;Node* foundNode = search(root, searchKey);if (foundNode) {printf("找到节点: %d\n", foundNode->key);} else {printf("未找到节点: %d\n", searchKey);}// 删除节点root = deleteNode(root, 30);printf("删除节点 30 后的中序遍历结果:");inorder(root);printf("\n");return 0;
}
红黑树(STL中关联式容器的底层数据结构,这个比较繁琐一些))、
红黑树是一种自平衡的二叉搜索树,其主要特性是节点被涂成红色或黑色,以确保树的平衡性。红黑树的“红黑”指的是节点的颜色属性。具体来说,红黑树遵循以下几个性质:节点颜色:每个节点要么是红色,要么是黑色。
根节点:树的根节点是黑色。
叶子节点:每个叶子节点(空节点)是黑色。
红色节点的限制:如果一个节点是红色,则它的两个子节点必须是黑色(即不能有两个连续的红色节点)。
黑色高度:从任意节点到其每个叶子节点的路径都包含相同数量的黑色节点。这些性质保证了树的高度在一定范围内,从而在最坏情况下也能保持对数时间复杂度的查找、插入和删除操作。通过使用红黑树,能够有效地保持二叉搜索树的平衡性,避免退化为链表的情况。这些特性确保了树的高度是对数级别,从而保证了插入、删除和查找操作的时间复杂度为 (O(\log n))。
节点结构:RBNode 包含键、颜色、左右子节点和父节点指针。
红黑树结构:RBTree 包含根节点和哨兵节点。
创建节点和树:createNode 和 createRBTree 用于初始化节点和树。
左旋和右旋:leftRotate 和 rightRotate 用于维护树的平衡。
插入操作:insert 方法插入新节点并调用 fixInsert 来修复树的性质。
中序遍历:inOrder 和 inOrderHelper 用于打印树的节点。#include <stdio.h>
#include <stdlib.h>// 节点颜色定义
typedef enum { RED, BLACK } NodeColor;// 红黑树节点定义
typedef struct RBNode {int key;NodeColor color;struct RBNode *left, *right, *parent;
} RBNode;// 红黑树定义
typedef struct RBTree {RBNode *root;RBNode *TNULL; //TNULL: 一个特殊的哨兵节点,用于简化树的操作(如插入和删除)
} RBTree;// 创建新节点
RBNode* createNode(int key) {RBNode* node = (RBNode*)malloc(sizeof(RBNode));node->key = key;node->left = NULL;node->right = NULL;node->parent = NULL;node->color = RED; // 新节点默认为红色return node;
}// 创建红黑树及哨兵节点
RBTree* createRBTree() {RBTree* tree = (RBTree*)malloc(sizeof(RBTree));tree->TNULL = createNode(0); // 哨兵节点tree->TNULL->color = BLACK; // 哨兵节点为黑色tree->root = tree->TNULL;return tree;
}// 左旋转
void leftRotate(RBTree *tree, RBNode *x) {RBNode *y = x->right;x->right = y->left;if (y->left != tree->TNULL) {y->left->parent = x;}y->parent = x->parent;if (x->parent == tree->TNULL) {tree->root = y;} else if (x == x->parent->left) {x->parent->left = y;} else {x->parent->right = y;}y->left = x;x->parent = y;
}// 右旋转
void rightRotate(RBTree *tree, RBNode *x) {RBNode *y = x->left;x->left = y->right;if (y->right != tree->TNULL) {y->right->parent = x;}y->parent = x->parent;if (x->parent == tree->TNULL) {tree->root = y;} else if (x == x->parent->right) {x->parent->right = y;} else {x->parent->left = y;}y->right = x;x->parent = y;
}// 插入修正函数
void fixInsert(RBTree *tree, RBNode *k) {RBNode *u; // 叔叔节点while (k->parent->color == RED) {if (k->parent == k->parent->parent->left) {u = k->parent->parent->right;if (u->color == RED) {// 情况 1: 叔叔是红色k->parent->color = BLACK;u->color = BLACK;k->parent->parent->color = RED;k = k->parent->parent;} else {if (k == k->parent->right) {// 情况 2: 当前节点是右孩子k = k->parent;leftRotate(tree, k);}// 情况 3: 当前节点是左孩子k->parent->color = BLACK;k->parent->parent->color = RED;rightRotate(tree, k->parent->parent);}} else {u = k->parent->parent->left;if (u->color == RED) {// 情况 1: 叔叔是红色k->parent->color = BLACK;u->color = BLACK;k->parent->parent->color = RED;k = k->parent->parent;} else {if (k == k->parent->left) {// 情况 2: 当前节点是左孩子k = k->parent;rightRotate(tree, k);}// 情况 3: 当前节点是右孩子k->parent->color = BLACK;k->parent->parent->color = RED;leftRotate(tree, k->parent->parent);}}if (k == tree->root) {break;}}tree->root->color = BLACK;
}// 插入节点
void insert(RBTree *tree, int key) {RBNode *node = createNode(key);node->parent = tree->TNULL;RBNode *y = tree->TNULL;RBNode *x = tree->root;while (x != tree->TNULL) {y = x;if (node->key < x->key) {x = x->left;} else {x = x->right;}}node->parent = y;if (y == tree->TNULL) {tree->root = node;} else if (node->key < y->key) {y->left = node;} else {y->right = node;}node->left = tree->TNULL;node->right = tree->TNULL;// 修复红黑树fixInsert(tree, node);
}// 中序遍历
void inOrderHelper(RBNode *node, RBTree *tree) {if (node != tree->TNULL) {inOrderHelper(node->left, tree);printf("%d ", node->key);inOrderHelper(node->right, tree);}
}// 打印中序遍历
void inOrder(RBTree *tree) {inOrderHelper(tree->root, tree);
}// 主函数测试红黑树
int main() {RBTree *tree = createRBTree();// 插入节点insert(tree, 55);insert(tree, 40);insert(tree, 65);insert(tree, 30);insert(tree, 50);insert(tree, 60);insert(tree, 70);// 打印中序遍历printf("中序遍历结果: ");inOrder(tree);printf("\n");// 释放内存(在实际使用中需要实现完整的释放逻辑)free(tree->TNULL);free(tree);return 0;
}
红黑树的哨兵节点
哨兵节点(不实际存储数据)在红黑树等数据结构中能够简化操作,主要是因为它提供了一个统一的处理方式,避免了对空指针的特殊情况处理。具体来说,哨兵节点的优势包括:消除空指针检查:使用哨兵节点允许我们在树的每个节点都可以访问左右子节点,即使是叶子节点。这样一来,很多操作(如插入、删除和遍历)就无需特别判断当前节点是否为空,从而简化代码逻辑。统一结构:哨兵节点充当了一个虚拟的叶节点(通常为黑色),使得所有非叶节点都遵循相同的结构。这种一致性使得实现各种操作时的逻辑更加直观。减少边界条件:在执行旋转或调整颜色等操作时,哨兵节点可以作为一个稳定的参考点,减少了边界条件的复杂性。例如,在插入新节点时,无需单独处理根节点的情况,因为根节点的父指针可以指向哨兵节点,而不是 NULL。简化算法实现:许多维护红黑树性质的算法(如修复插入后的性质)可以通过统一的逻辑实现,避免了多次重复代码。因为哨兵节点的存在,算法的实现可以更加简洁且易于理解
          [10B]/      \[5R]        [15R]/   \       /    \[2B]  [7B] [12B]   [20B]|      |     |      |[NIL]  [NIL] [NIL] [NIL]
哈希表(这个在STL中无序关联式容器也会被问到)。
//哈希表的定义
哈希表(Hash Table)是一种数据结构,通过将键(Key)映射到值(Value)的方式来实现高效的数据存储和查找。它利用哈希函数将键转换为数组的索引,从而支持快速的数据访问。//哈希表的基本组成
哈希函数:将输入的键转换为固定大小的整数索引。
数组:存储值的容器,通常被称为“桶”或“槽”。
冲突处理机制:当多个键映射到同一索引时,采用的方法以解决此问题(如链地址法、开放寻址法等)。//哈希表的问题
1.hash冲突:多个键可能被哈希到同一个索引。解决方法包括:链地址法:在每个索引处使用链表存储冲突的键值对。
开放寻址法:在表中寻找下一个空槽来存储冲突的键值对。
负载因子:负载因子是哈希表中元素的数量与数组大小的比率。负载因子过高会影响性能,因此通常会在达到一定负载因子时进行扩容。2.hash函数的选择:一个好的哈希函数能均匀地分配键值,避免冲突。差的哈希函数会导致大量冲突,从而降低性能。
3.动态扩展:当哈希表装填达到一定阈值时,需要动态扩展,这涉及重新计算每个元素的哈希值并插入新的数组。//哈希表的优点
快速查找:平均时间复杂度为 O(1),非常高效。
灵活性:可以存储各种类型的数据,不仅限于数字。
哈希表的缺点
内存消耗:为了减少冲突,可能会预留较多的空间。
不支持有序操作:哈希表内部没有顺序,无法按键的顺序遍历。    
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define TABLE_SIZE 10  // 哈希表大小// 键值对结构
typedef struct KeyValue {char *key;int value;struct KeyValue *next; // 指向下一个节点
} KeyValue;// 哈希表结构
typedef struct HashTable {KeyValue **table; // 哈希桶数组
} HashTable;// 哈希函数
unsigned int hash(const char *key) {unsigned long int hashval = 0;while (*key != '\0') {hashval = (hashval << 5) + *key++; // 计算哈希值}return hashval % TABLE_SIZE;
}// 创建哈希表
HashTable *create_table() {HashTable *ht = malloc(sizeof(HashTable));ht->table = malloc(sizeof(KeyValue *) * TABLE_SIZE);for (int i = 0; i < TABLE_SIZE; i++) {ht->table[i] = NULL; // 初始化为NULL}return ht;
}// 插入键值对
void insert(HashTable *ht, const char *key, int value) {unsigned int index = hash(key);KeyValue *new_pair = malloc(sizeof(KeyValue));new_pair->key = strdup(key); // 复制键new_pair->value = value;new_pair->next = ht->table[index]; // 将新节点插入链表头ht->table[index] = new_pair;
}// 查找值
int search(HashTable *ht, const char *key) {unsigned int index = hash(key);KeyValue *pair = ht->table[index];while (pair != NULL) {if (strcmp(pair->key, key) == 0) {return pair->value; // 找到返回值}pair = pair->next; // 移动到下一个节点}return -1; // 未找到返回-1
}// 删除键值对
void delete(HashTable *ht, const char *key) {unsigned int index = hash(key);KeyValue *pair = ht->table[index];KeyValue *prev = NULL;while (pair != NULL) {if (strcmp(pair->key, key) == 0) {if (prev == NULL) {ht->table[index] = pair->next; // 删除头节点} else {prev->next = pair->next; // 删除中间或尾节点}free(pair->key);free(pair);return;}prev = pair;pair = pair->next;}
}// 释放哈希表
void free_table(HashTable *ht) {for (int i = 0; i < TABLE_SIZE; i++) {KeyValue *pair = ht->table[i];while (pair != NULL) {KeyValue *temp = pair;pair = pair->next;free(temp->key);free(temp);}}free(ht->table);free(ht);
}int main() {HashTable *ht = create_table();insert(ht, "apple", 1);insert(ht, "banana", 2);insert(ht, "orange", 3);printf("Value for 'apple': %d\n", search(ht, "apple"));printf("Value for 'banana': %d\n", search(ht, "banana"));delete(ht, "banana");printf("Value for 'banana' after deletion: %d\n", search(ht, "banana"));free_table(ht);return 0;
}
———————————————————————
3、Linux:
进程与线程的基本概念、
进程和线程是操作系统中两个重要的概念,它们用于管理程序的执行。//进程(Process)
定义:进程是一个正在运行的程序实例,是操作系统资源分配的基本单位。每个进程都有自己的地址空间、数据栈和其他辅助数据。特性:
独立性:进程之间相互独立,一个进程的崩溃不会直接影响到其他进程。
资源拥有:每个进程有自己的资源,如内存、文件句柄等。
状态:进程可以处于创建、就绪、运行、阻塞和终止等状态。
调度:操作系统通过==调度算法==来管理多个进程的运行,确保系统资源的合理利用。//线程(Thread)
定义:线程是进程中的一个执行单元,是程序执行的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源。特性:
轻量级:线程比进程更轻量,创建和销毁的开销较小。
共享资源:同一进程内的线程可以共享该进程的内存和资源,从而能够更高效地进行通信。
并发执行:多个线程可以并发执行,提高程序的运行效率。
调度:线程调度通常由操作系统或运行时环境负责,支持多线程并发执行。//进程与线程的比较
特性	      进程	      			线程
资源拥有	拥有独立的资源			 共享进程的资源
创建开销	较大	        		 较小
通信方式	通过进程间通信(IPC)	通过共享内存、信号量等
切换开销	较大					较小
独立性		 进程独立	 			线程依赖于所在的进程
//总结
进程是资源分配的基本单位,而线程是程序执行的基本单位。
理解进程和线程的区别有助于更好地进行程序设计和优化,以提高系统性能和响应速度。
进程间通信的几种方式以及区别;
进程间通信(Inter-Process Communication, IPC)是指在不同进程之间交换数据和信息的机制。以下是几种常见的进程间通信方式及其区别:1. 管道(Pipe)
定义:管道是一种半双工的通信方式,允许一个进程将数据写入管道,而另一个进程则从管道中读取数据。
特点:
只能在具有亲缘关系的进程间使用(如父子进程)。
数据传输是顺序的,先进先出(FIFO)。
适合小量数据的传输。2. 命名管道(Named Pipe)
定义:命名管道也是一种管道,但它通过一个名字在文件系统中创建,可以在不相关的进程间通信。
特点:
支持双向通信。
允许不相关的进程进行数据传输。
仍然保持先进先出的特性。3. 消息队列(Message Queue)
定义:消息队列允许进程以消息的形式异步地发送和接收数据。
特点:
支持多个发送者和接收者。
消息可以按照优先级处理。
适合需要异步处理的大量数据传输。4. 共享内存(Shared Memory)
定义:共享内存允许多个进程直接访问同一块内存区域,以进行数据交换。
特点:
速度快,因为数据直接在内存中共享。
需要同步机制(如信号量)来防止竞争条件。
适合大规模数据传输。5. 信号(Signal)
定义:信号是一种用于通知进程某个事件发生的机制,例如外部中断或进程状态变化。
特点:
主要用于进程之间的事件通知。
信号本身不传递数据,只传递控制信息。
信号处理较为复杂,易出错。6. 套接字(Socket)
定义:套接字是一种用于在网络上或本地计算机上进行进程间通信的机制。
特点:
支持跨网络的进程通信。
可以是面向连接(TCP)或无连接(UDP)的。
适合需要网络通信的应用场景。7. 文件映射(Memory-Mapped Files)
定义:文件映射允许多个进程访问同一文件的内容,就像访问内存一样。
特点:
适合大数据量的共享。
通过操作系统的文件系统进行管理。
需要注意同步问题。
OSI七层模型,每一层常规协议;
//从下到上,物链网传会表应1. 物理层 (Physical Layer)
功能: 负责数据的物理传输,包括电气信号、光信号或无线信号等。
常规协议/标准:Ethernet (IEEE 802.3),USB,DSL,RS-2322. 数据链路层 (Data Link Layer)
功能: 提供节点间的数据帧传输,处理物理地址和错误检测。
常规协议/标准:Ethernet (IEEE 802.3),Wi-Fi (IEEE 802.11),PPP (Point-to-Point Protocol)
HDLC (High-Level Data Link Control)3. 网络层 (Network Layer)
功能: 负责数据包的路由选择和转发,处理逻辑地址(如IP地址)。
常规协议/标准:IP (Internet Protocol),ICMP (Internet Control Message Protocol)
IGMP (Internet Group Management Protocol),RIP (Routing Information Protocol)4. 传输层 (Transport Layer)
功能: 提供端到端的通信,确保数据完整性和顺序。
常规协议/标准:TCP (Transmission Control Protocol),UDP (User Datagram Protocol)
SCTP (Stream Control Transmission Protocol)5. 会话层 (Session Layer)
功能: 管理会话和连接,控制对话的建立、维护和终止。
常规协议/标准:NetBIOS,RPC (Remote Procedure Call),PPTP (Point-to-Point Tunneling Protocol)6. 表示层 (Presentation Layer)
功能: 数据格式化和转换,处理加密和解密。
常规协议/标准:SSL/TLS (Secure Sockets Layer / Transport Layer Security)
JPEG, GIF, PNG (图像格式),ASCII, EBCDIC (字符编码)7. 应用层 (Application Layer)
功能: 提供用户与应用程序之间的接口,处理高层协议。
常规协议/标准:HTTP/HTTPS (Hypertext Transfer Protocol),FTP (File Transfer Protocol)
SMTP (Simple Mail Transfer Protocol),DNS (Domain Name System)
TCP三次握手与四次挥手;
//TCP三次握手(建立连接)
第一次握手:客户端发送一个SYN(同步)报文段,带有初始序列号,表示请求建立连接。
第二次握手:服务器收到SYN报文段后,回复一个SYN-ACK(同步-确认)报文段,表示同意建立连接,同时也发送			 自己的初始序列号。
第三次握手:客户端收到SYN-ACK报文段后,发送一个ACK(确认)报文段,确认收到服务器的SYN,此时连接建			  立完成。//TCP四次挥手(终止连接)
第一次挥手:主动关闭连接的一方(例如客户端)发送一个FIN(结束)报文段,表示希望关闭连接。
第二次挥手:另一方(例如服务器)收到FIN报文段后,发送一个ACK报文段,确认收到关闭请求,这一阶段连接		  的一方进入FIN_WAIT_1状态。
第三次挥手:服务器在处理完所有数据后,发送一个FIN报文段,表示也希望关闭连接。
第四次挥手:客户端收到FIN报文段后,发送一个ACK报文段,确认关闭请求,此时客户端进入TIME_WAIT状态,		  等待可能的重发报文段,之后最终关闭连接。//总结
三次握手用于建立连接,确保双方都准备好进行数据传输。
四次挥手用于安全地终止连接,确保所有数据都已成功传输并接收。
socket网络编程的流程;
//server
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t addr_len = sizeof(client_addr);// 创建 socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);// 设置地址结构 sockaddr_in
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IP
server_addr.sin_port = htons(PORT);// 绑定 bind
bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));// 监听 listen
listen(server_socket, BACKLOG);// 接受连接 accept
client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &addr_len);// 数据传输 send recv
recv(client_socket, buffer, sizeof(buffer), 0);
send(client_socket, response, sizeof(response), 0);// 关闭连接 close
close(client_socket);
close(server_socket);
//client
int client_socket;
struct sockaddr_in server_addr;// 创建 socket
client_socket = socket(AF_INET, SOCK_STREAM, 0);// 设置地址结构 address_in
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "SERVER_IP", &server_addr.sin_addr); // 服务器 IP// 连接到服务器 connect
connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));// 数据传输 send recv
send(client_socket, request, sizeof(request), 0);
recv(client_socket, buffer, sizeof(buffer), 0);// 关闭连接 close
close(client_socket);
三种IO多路复用的原理、底层数据结构、使用方法,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

IO多路复用是指在单个线程中同时监控多个文件描述符的状态,以便在某个文件描述符准备好进行读写操作时,能够及时响应。常见的三种IO多路复用机制是:select、poll和epoll。以下是它们的原理和底层数据结构的简要介绍。1. select
//原理
select函数允许程序监视多个文件描述符,以查看它们是否可以进行读、写或异常处理。
它使用一个集合(fd_set)来表示要监视的文件描述符。
//底层数据结构
fd_set:一个位域数组,每一位对应一个文件描述符。最大文件描述符的数量受限于系统常量FD_SETSIZE(通常为1024)。
使用宏FD_SET、FD_CLR、FD_ISSET来操作这个集合。
//缺点
文件描述符的数量受到FD_SETSIZE的限制,且每次调用select都需要重新设置监视的集合。
效率较低,在高并发场景下性能下降明显。2. poll
//原理
poll函数与select类似,但它不使用固定大小的集合,而是使用一个可扩展的数组来存储待监视的文件描述符及其事件。
它能监视更多的文件描述符,没有FD_SETSIZE的限制。
//底层数据结构
struct pollfd:包含文件描述符和事件类型的结构体。
poll函数接受一个pollfd数组和其大小作为参数。
//缺点
每次调用poll都需传递整个数组,仍然存在线性扫描的问题,性能在大量文件描述符时可能会下降。3. epoll
//原理
epoll是Linux特有的高效IO多路复用机制,设计用于处理大量并发连接。
它通过内核空间与用户空间的分离来提高效率,只在感兴趣的文件描述符上进行操作。
//底层数据结构
epoll_event:包含文件描述符及其关注的事件。
epoll接口提供了epoll_create、epoll_ctl和epoll_wait等函数来创建、控制和等待事件。
//优点
可以处理大规模的文件描述符,性能优于select和poll,尤其是在大量连接的情况下。
支持边缘触发(edge-triggered)和水平触发(level-triggered)两种模式。
struct pollfd {int fd;         // 文件描述符short events;   // 关注的事件(如可读、可写等)short revents;  // 返回的事件(实际发生的事件)
};struct epoll_event {uint32_t events;    // 关注的事件(如EPOLLIN、EPOLLOUT等)epoll_data_t data;  // 用户自定义的数据
};
特别是epoll的用法、边沿触发与水平触发的区别与联系;
//边沿触发与水平触发的区别与联系
水平触发(Level Triggered)
定义:在水平触发模式下,只要文件描述符的状态满足条件(例如可读或可写),epoll_wait就会返回该事件。
特点:
只要数据尚未被读取,事件就会持续触发。
程序需要确保在处理完事件后尽快读取数据,以避免反复触发同一个事件。
使用场景:适合大多数应用,因为简单易理解。边缘触发(Edge Triggered)
定义:在边缘触发模式下,只有当文件描述符的状态从未准备好变为准备好时,epoll_wait才会返回该事件。
特点:
一旦事件被触发,如果没有新数据到来,之后不会再次触发。
程序必须在事件触发时尽可能多地读取数据,否则可能会遗漏后续的可读事件。
使用场景:适合高性能、低延迟的应用,能够减少不必要的系统调用,但需要更复杂的逻辑处理。联系
性能:边缘触发通常在高负载情况下提供更好的性能,因为它减少了重复唤醒的次数。
实现复杂性:边缘触发的实现需要开发者更加小心,以确保数据完全被读取,避免漏掉后续事件。
选择:开发者需要根据应用场景和性能需求选择使用水平触发或边缘触发。
// 添加文件描述符时设置边缘触发
ev.events = EPOLLIN | EPOLLET; // 边缘触发
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);// 处理事件时确保尽可能多地读取数据
while (1) {int n = read(fd, buffer, sizeof(buffer));if (n <= 0) {// 处理错误或EOFbreak;}// 处理读取的数据
}
同步、异步、阻塞、非阻塞;
阻塞/非阻塞关注的是/*==用户态进程/线程==*/的状态
其要访问的数据是否就绪,进程/线程是否需要等待。当前接口数据还未准备就绪时,线程是否被阻塞挂起。//阻塞挂起就是当前线程还处于CPU时间片当中,调用了阻塞的方法,由于数据未准备就绪,则时间片还未到就让出CPU。//非阻塞就是当前接口数据还未准备就绪时,线程不会被阻塞挂起,可以不断轮询请求接口,看看数据是否已经准备就绪。同步/异步关注的是/*消息通信机制*/同步,就是在发出一个调用时,自己需要参与等待结果的过程,则为同步。同步需要主动读写数据,在读写数据的过程中还是会阻塞。异步IO,则指出发出调用以后到数据准备完成,自己都未参与,则为异步。异步只需要关注IO操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。
五种网络IO模型;
1. 阻塞I/O (Blocking I/O)
定义:在阻塞I/O模型中,调用I/O操作时,程序会被挂起,直到I/O操作完成。
特点:
程序在进行I/O操作时无法执行其他任务。
简单易用,但在高并发情况下可能导致性能瓶颈。2. 非阻塞I/O (Non-blocking I/O)
定义:在非阻塞I/O模型中,I/O操作不会使程序挂起。如果数据不可用,操作会立即返回。
特点:
程序可以执行其他任务,不会因I/O操作而阻塞。
通常需要轮询或使用回调机制来检查数据是否可用。3. I/O复用 (I/O Multiplexing)
定义:通过使用select、poll或epoll等系统调用,程序可以监视多个I/O流,并处理其中有数据的流。
特点:
适合于高并发场景,可以同时处理多个连接。
程序在等待I/O时不会被阻塞,而是等待多个文件描述符的状态变化。4. 信号驱动I/O (Signal-driven I/O)
定义:在信号驱动I/O模型中,程序注册一个信号处理程序,以便在I/O操作就绪时接收信号通知。
特点:
可以避免轮询,提高效率。
复杂性较高,需要处理信号安全和信号处理的细节。5. 异步I/O (Asynchronous I/O)
定义:在异步I/O模型中,程序发起I/O请求后立即返回,并在I/O操作完成时通过回调函数或其他机制获得结果。
特点:
无需轮询或等待,能够有效利用CPU资源。
编程模型相对复杂,需要管理回调和状态。
常见的并发服务器模型。
1. 单线程模型 (Single-threaded Model)
定义:服务器在一个线程中处理所有请求,通常使用非阻塞I/O或I/O复用来管理多个连接。
特点:
简单实现,无需多线程管理。
适合小型应用和低并发场景。
可能在高负载下成为瓶颈。2. 多线程模型 (Multi-threaded Model)
定义:每个客户端请求由一个独立的线程处理。
特点:
充分利用多核CPU,能处理多个请求。
线程切换开销较高,可能导致性能下降。
需要管理线程的创建、销毁和同步。3. 进程模型 (Multi-process Model)
定义:每个请求由一个独立的进程处理,通常使用系统的多进程支持。
特点:
提供更强的隔离性和安全性,避免了线程间共享内存的问题。
进程间通信开销较大。
启动和管理进程的开销高于线程。4. 线程池模型 (Thread Pool Model)
定义:预先创建一组线程,客户端请求到达时从线程池中获取线程处理,处理完成后返回线程池。
特点:
降低了线程创建和销毁的开销。
可以控制并发量,提高资源利用率。
适合高并发场景。5. 事件驱动模型 (Event-driven Model)
定义:使用事件循环和回调机制响应客户端的请求,通常结合非阻塞I/O。
特点:
高效处理大量连接,适合I/O密集型应用。
复杂性较高,需管理事件和回调。
Node.js 和 Nginx 是基于此模型的典型例子。6. 混合模型 (Hybrid Model)
定义:结合多线程和事件驱动模型,例如在每个线程中使用事件循环。
特点:
灵活性高,可以根据需求进行调整。
适合复杂的应用场景,能够同时处理计算密集型和I/O密集型任务。
———————————————————————
4、C++的基础:
常见的基础语法,比如:const可以修饰哪些(指针常量与常量指针的区别、
const int *ptr:指向的值是常量,不能通过指针修改它的值,但指针本身可以指向其他变量。
int *const ptr:指针是常量,不能改变指向,但可以修改指向的值。const 修饰   变量,指针,成员函数,参数    
数组指针与指针数组、函数指针与指针数组的区别,这个上课怎么讲的);
int (*ptr)[5] = &arr; // ptr是指向包含5个int类型元素的数组的指针
int *arr[5]; // arr是一个包含5个指针的数组
void (*funcPtr)(int) = myFunction; // funcPtr是指向myFunction的指针
void (*funcArr[2])(int) = {func1, func2}; // funcArr是一个包含2个函数指针的数组1. 数组指针与指针数组
数组指针(Pointer to Array)
定义:指向一个数组的指针。        声明:通常使用type (*ptr)[size]的形式。
指针数组(Array of Pointers)
定义:数组中的每个元素都是指针。  声明:通常使用type *arr[size]的形式。 2. 函数指针与指针数组
函数指针(Pointer to Function)
定义:指向函数的指针,可以用来调用指向的函数。  return_type (*ptr)(parameter_types)的形式    
指针数组(Array of Function Pointers)
定义:数组中的每个元素都是指向函数的指针。  return_type (*arr[size])(parameter_types)的形式    
四种强制转换叫什么,有什么区别;
float b = static_cast<float>(a); // int 转 float
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 安全转换,基类转派生类
int* b = const_cast<int*>(&a); // 去掉const修饰
void* ptr = reinterpret_cast<void*>(&a); // int* 转 void*static_cast
用途:用于在相关类型之间进行安全的转换,如基本数据类型(int、float等)之间的转换,或者类层次结构中的基类和派生类之间的转换。
特点:编译时检查,确保类型转换的安全性。dynamic_cast
用途:主要用于类的多态性,安全地将基类指针或引用转换为派生类指针或引用。
特点:运行时检查,如果转换不安全,将返回nullptr(对于指针)或抛出std::bad_cast(对于引用)const_cast
用途:用于去掉对象的常量性(const),允许修改原本被声明为const的对象。
特点:只能添加或去除常量性,不改变对象的类型reinterpret_cast
用途:用于进行底层的类型转换,可以将指针类型转换为任何其他指针类型,或将整数类型转换为指针类型等。
特点:不进行任何检查,转换结果可能是不安全的,使用时需谨慎    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1552596.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

递归函数设计技巧

目录 1.路飞吃桃子的问题--应试难度 2.弹簧板问题--应试难度 3.递归实现指数型枚举--校招难度 4.递归实现组合型枚举--校招难度 5.递归实现排列型枚举--校招难度 1.路飞吃桃子的问题--应试难度 我们可以说下两个案例&#xff0c;反正是最后一天的时候&#xff0c;只剩下了一…

pnpm在monorepo架构下不能引用其他模块的问题

一、研究背景 monorepo架构项目目录结构&#xff1a; - common- index.ts- ... - main- index.ts- ... - web- vue-demo- ... pnpm在monorepo架构下使用以下命令 pnpm -F main add common # or pnpm --filter main add common 并不能在main/index.ts中使用common/index.ts…

从概念到使用全面了解Llama 3 这个迄今为止最强大的开源模型

概述 mate最近发布了 Llama3&#xff0c;下一代最先进的开源大语言模型&#xff08;LLM&#xff09;。正如本文的综合评论所述&#xff0c;Llama 3 以其前身奠定的基础为基础&#xff0c;旨在增强 Llama 2 成为 ChatGPT 重要开源竞争对手的能力 Llama 2&#xff1a;深入探讨 C…

Spring Boot驱动的足球青训俱乐部管理解决方案

1 绪论 1.1研究背景 随着科技的发展&#xff0c;计算机的应用&#xff0c;人们的生活方方面面都和互联网密不可分。计算机的普及使得人们的生活更加方便快捷&#xff0c;网络也遍及到我们生活的每个角落&#xff0c;二十一世纪信息化时代的到来&#xff0c;随着社会科技的不断…

中国电信解锁万亿参数大模型:TeleAI的创新与突破

首个由万卡集群训练出来的万亿参数大模型&#xff0c;已被一家央企解锁。 具体而言&#xff0c;为了推动纯国产人工智能的探索&#xff0c;带来这条新路径的正是中国电信人工智能研究院&#xff08;TeleAI&#xff09;。 该研究院由中国电信集团的CTO、首席科学家兼院长李学龙…

docker零基础入门教程

注意 本系列文章已升级、转移至我的自建站点中&#xff0c;本章原文为&#xff1a;Docker入门 目录 注意1.前言2.docker安装3.docker基本使用4.打包docker镜像5.docker进阶 1.前言 如果你长期写C/C代码&#xff0c;那你应该很容易发现C/C开源项目存在的一个严重问题&#xff…

【React】入门Day01 —— 从基础概念到实战应用

目录 一、React 概述 二、开发环境创建 三、JSX 基础 四、React 的事件绑定 五、React 组件基础使用 六、组件状态管理 - useState 七、组件的基础样式处理 快速入门 – React 中文文档 一、React 概述 React 是什么 由 Meta 公司开发&#xff0c;是用于构建 Web 和原生…

XFTP-8下载安装教程

下载地址 https://www.xshell.com/zh/free-for-home-school/ 新建XFTP文件夹 安装过程 选择新建的文件夹 此处默认即可 填写信息提交注册 点击生成的链接 点击后来&#xff0c;完成安装

WebRTC Connection Negotiate解决

最近有个项目 &#xff0c;部署之后一直显示&#xff0c;查了一些资料还是没有解决&#xff0c;无奈只有自己研究解决&#xff1f; 什么是内网穿透&#xff1f; 我们访问我们自己的官网产品页面&#xff0c;我们的服务器是一个单独的个体&#xff0c;有独立的公网ip&#xf…

Redis实现每日签到(大数据量)

PHP语言使用Redis NoSQL服务器二进制数据类型实现大数据情况下签到功能 目录 问题 解决方式 封装签到类 功能调用 总结 问题 实现用户每日签到功能不难&#xff0c;但随着用户量上升之后&#xff0c;不论是存储还是判断对数据量来说都很麻烦&#xff1b;假如每天有100万用…

文心一言 VS 讯飞星火 VS chatgpt (359)-- 算法导论24.3 1题

一、在图 24-2上运行Dijkstra算法&#xff0c;第一次使用结点 s s s作为源结点&#xff0c;第二次使用结点 z z z作为源结点。以类似于图 24-6 的风格&#xff0c;给出每次while循环后的 d d d值和 π π π值&#xff0c;以及集合 S S S中的所有结点。如果要写代码&#xff0c…

使用容器启动的zk无法暴露3888问题解决

1. 问题描述 zk配置如下&#xff1a; 我通过容器启动了一个zk&#xff0c;通过-p 参数暴露了2181和3888端口&#xff0c;容器启动脚本如下&#xff1a; #!/bin/shdocker rm -f myzookeeper1docker run -p 12181:2181 -p 13888:3888 --name myzookeeper1 --restart always …

利士策分享,国庆日,共筑梦想,同庆辉煌

利士策分享&#xff0c;国庆日&#xff0c;共筑梦想&#xff0c;同庆辉煌 今天是我们的祖国成立的第75个国庆日&#xff0c;在这个举国同庆的日子里&#xff0c;我感受到了浓厚的节日氛围。 此刻的你&#xff0c;是否也在和家人朋友一起享受这份难得的宁静与快乐呢&#xff1f…

AI产品经理PRD文档与传统产品经理PRD有什么不同呢?

目录 模型输出&#xff1a;说白了&#xff0c;就是你的AI要干啥数据接入&#xff1a;你的AI要吃啥“粮食”验收标准&#xff1a;怎么判断你的AI干得好不好经验总结 你好&#xff0c;我是三桥君 在工作中&#xff0c;当我作为传统产品经理时&#xff0c;通常只需提供产品需求文…

SigmaStudio控件Cross Mixer\Signal Merger算法效果分析

衰减与叠加混音算法验证分析一 CH2:输入源为-20dB正弦波1khz CH1叠加混音&#xff1a;参考混音算法https://blog.csdn.net/weixin_48408892/article/details/129878036?spm1001.2014.3001.5502 Ch0衰减混音&#xff1a;外部多个输入源做混音时&#xff0c;建议参考该算法控件&…

宝塔的软件商店打不开怎么办?

宝塔的软件商店打不开怎么办&#xff1f; 请从下面这个按钮进入&#xff1a; 或者尝试直接打开链接&#xff1a;https://127.0.0.1:1234/soft

自定义注解加 AOP 实现服务接口鉴权以及内部认证

注解 何谓注解&#xff1f; 在Java中&#xff0c;注解&#xff08;Annotation&#xff09;是一种特殊的语法&#xff0c;用符号开头&#xff0c;是 Java5 开始引入的新特性&#xff0c;可以看作是一种特殊的注释&#xff0c;主要用于修饰类、方法或者变量&#xff0c;提供某些信…

Redis: Sentinel哨兵监控架构及环境搭建

概述 在主从模式下&#xff0c;我们通过从节点只读模式提高了系统的并发能力并发不断增加&#xff0c;只需要扩展从节点即可&#xff0c;只要主从服务器之间&#xff0c;网络连接正常主服务器就会将写入自己的数据同步更新给从服务器&#xff0c;从而保证主从服务器的数据相同…

推送k8s镜像到阿里云服务器

1、服务打包 2、打包后进入Dockerfile的同级目录 运行 docker build -t 镜像名:镜像版本 . (这个点是当前目录的意思&#xff0c;不能忽略)例如 docker build -t trac:v1.0.4 .3、上传镜像到阿里云镜像服务 注意选择区域 例如&#xff1a; docker tag 70743d9bdba3 registr…

jni动态库“%1 不是有效的win32应用程序”问题的解决以及一些windows下dll有关命令的记录

一、前因 在windows下用cmakeVS编译了一个jni动态库&#xff0c;再使用java测试程序调用这个动态库的时候报错&#xff1a;“%1 不是有效的win32应用程序” 对于这类问题&#xff0c;一般从以下几个方面考虑&#xff1a; 动态库文件损坏动态库或者其依赖库文件路径错误导致找…