当前位置: 首页 > news >正文

4-2. 二叉搜索树 (BST)

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// 二叉树节点结构定义
typedef struct BiTNode {char data;                  // 数据域struct BiTNode *lchild;     // 左孩子指针struct BiTNode *rchild;     // 右孩子指针
} BiTNode;// 二叉树类型的定义
typedef BiTNode *BiTree;/*-------------------------链栈结构定义,用于非递归遍历------------------------*/// 栈节点结构定义
typedef struct StackNode {BiTree data_ptr;              // 数据域: 存储二叉树节点指针struct StackNode *next;
} StackNode;// 链栈结构
typedef struct LinkedStack {StackNode *top;         // 栈顶指针int length;             // 节点个数
} LinkedStack;/*** 初始化栈* 时间复杂度: O(1)* 空间复杂度: O(1)*/
void InitStack(LinkedStack *stack) {if (stack == NULL) return;stack->top = NULL;stack->length = 0;
}/*** 判断栈是否为空* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool StackIsEmpty(LinkedStack *stack) {if (stack == NULL) return true;return stack->top == NULL;
}/*** 入栈操作* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool Push(LinkedStack *stack, BiTree treeNode) {if (stack == NULL || treeNode == NULL) return false;StackNode *newNode = (StackNode *) malloc(sizeof(StackNode));if (!newNode) return false;newNode->data_ptr = treeNode;newNode->next = stack->top;stack->top = newNode;stack->length++;return true;
}/*** 出栈操作* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool Pop(LinkedStack *stack, BiTree *treeNode) {if (StackIsEmpty(stack)) return false;StackNode *tmp = stack->top;*treeNode = tmp->data_ptr;stack->top = tmp->next;stack->length--;free(tmp);return true;
}/*** 获取栈顶元素* 时间复杂度: O(1)* 空间复杂度: O(1)*/
BiTree GetTop(LinkedStack *stack) {if (StackIsEmpty(stack)) return NULL;return stack->top->data_ptr;
}/*-------------------------队列结构定义,用于层次遍历------------------------*/typedef struct QueueNode {BiTree data_ptr;               // 数据域:存储二叉树节点指针struct QueueNode *next;
} QueueNode;typedef struct LinkedQueue {QueueNode *front;          // 队头指针QueueNode *rear;           // 队尾指针int length;                // 队列长度
} LinkedQueue;/*** 初始化队列* 时间复杂度: O(1)* 空间复杂度: O(1)*/
void InitQueue(LinkedQueue *queue) {if (queue == NULL) return;queue->front = NULL;queue->rear = NULL;queue->length = 0;
}/*** 判断队列是否为空* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool QueueIsEmpty(LinkedQueue *queue) {if (queue == NULL) return true;return queue->front == NULL;
}/*** 入队操作* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool Enqueue(LinkedQueue *queue, BiTree treeNode) {if (queue == NULL || treeNode == NULL) return false;QueueNode *newNode = (QueueNode *) malloc(sizeof(QueueNode));if (!newNode) return false;newNode->data_ptr = treeNode;newNode->next = NULL;if (QueueIsEmpty(queue)) {queue->front = newNode;queue->rear = newNode;} else {queue->rear->next = newNode;queue->rear = newNode;}queue->length++;return true;
}/*** 出队操作* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool DeQueue(LinkedQueue *queue, BiTree *treeNode) {if (QueueIsEmpty(queue)) return false;QueueNode *tmp = queue->front;*treeNode = tmp->data_ptr;queue->front = tmp->next;queue->length--;if (queue->front == NULL) queue->rear = NULL;free(tmp);return true;
}/*-------------------------二叉搜索树操作函数------------------------*//*** 初始化二叉树* 时间复杂度: O(1)* 空间复杂度: O(1)*/
void InitBiTree(BiTree *tree) {if (tree == NULL) return;*tree = NULL;
}/*** 二叉搜索树插入节点(递归)* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
bool BST_Insert(BiTree *tree, char key) {if (tree == NULL) return false;if (*tree == NULL) {*tree = (BiTNode *)malloc(sizeof(BiTNode));if (!*tree) return false;(*tree)->data = key;(*tree)->lchild = (*tree)->rchild = NULL;return true;}if (key < (*tree)->data) {return BST_Insert(&(*tree)->lchild, key);} else if (key > (*tree)->data) {return BST_Insert(&(*tree)->rchild, key);} else {return false; // 键值已存在}
}/*** 二叉搜索树插入节点(非递归)* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度: O(1)*/
bool BST_Insert_NonRecursive(BiTree *tree, char key) {if (tree == NULL) return false;BiTNode *newNode = (BiTNode *)malloc(sizeof(BiTNode));if (!newNode) return false;newNode->data = key;newNode->lchild = newNode->rchild = NULL;if (*tree == NULL) {*tree = newNode;return true;}BiTNode *current = *tree;BiTNode *parent = NULL;while (current != NULL) {parent = current;if (key < current->data) {current = current->lchild;} else if (key > current->data) {current = current->rchild;} else {free(newNode);return false;}}if (key < parent->data) {parent->lchild = newNode;} else {parent->rchild = newNode;}return true;
}/*** 二叉搜索树查找(递归)* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
BiTNode* BST_Search(BiTree tree, char key) {if (tree == NULL) return NULL;if (key == tree->data) {return tree;} else if (key < tree->data) {return BST_Search(tree->lchild, key);} else {return BST_Search(tree->rchild, key);}
}/*** 二叉搜索树查找(非递归)* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度: O(1)*/
BiTNode* BST_Search_NonRecursive(BiTree tree, char key) {BiTNode *current = tree;while (current != NULL) {if (key == current->data) {return current;} else if (key < current->data) {current = current->lchild;} else {current = current->rchild;}}return NULL;
}/*** 查找二叉搜索树的最小值节点* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度: O(1)*/
BiTNode* BST_FindMin(BiTree tree) {if (tree == NULL) return NULL;BiTNode *current = tree;while (current->lchild != NULL) {current = current->lchild;}return current;
}/*** 查找二叉搜索树的最大值节点* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度: O(1)*/
BiTNode* BST_FindMax(BiTree tree) {if (tree == NULL) return NULL;BiTNode *current = tree;while (current->rchild != NULL) {current = current->rchild;}return current;
}/*** 二叉搜索树删除节点(递归)* 时间复杂度:*   - 平均情况: O(log n), 其中n为节点个数*   - 最坏情况: O(n), 当树退化为链表时* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
bool BST_Delete(BiTree *tree, char key) {if (tree == NULL || *tree == NULL) return false;if (key < (*tree)->data) {return BST_Delete(&(*tree)->lchild, key);} else if (key > (*tree)->data) {return BST_Delete(&(*tree)->rchild, key);} else {BiTNode *temp = *tree;if ((*tree)->lchild == NULL) {*tree = (*tree)->rchild;free(temp);} else if ((*tree)->rchild == NULL) {*tree = (*tree)->lchild;free(temp);} else {BiTNode *minNode = BST_FindMin((*tree)->rchild);(*tree)->data = minNode->data;BST_Delete(&(*tree)->rchild, minNode->data);}return true;}
}/*** 判断是否为二叉搜索树* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
bool IsBST(BiTree tree) {static BiTNode *prev = NULL;if (tree == NULL) return true;if (!IsBST(tree->lchild)) return false;if (prev != NULL && tree->data <= prev->data) return false;prev = tree;return IsBST(tree->rchild);
}/*** 创建二叉搜索树(通过插入多个元素)* 时间复杂度: O(n log n), 其中n为keys数组长度*   - 平均情况: 每次插入O(log n),共n次*   - 最坏情况: O(n²), 当插入有序序列导致树退化为链表时* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
void CreateBST(BiTree *tree, char keys[], int n) {if (tree == NULL || keys == NULL) return;InitBiTree(tree);for (int i = 0; i < n; i++) {BST_Insert(tree, keys[i]);}
}/*** 中序遍历(递归)- 用于验证BST的有序性* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
void InOrderTraverse(BiTree tree) {if (tree) {InOrderTraverse(tree->lchild);printf("%c ", tree->data);InOrderTraverse(tree->rchild);}
}/*** 层次遍历* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度: O(w), 其中w为树的最大宽度(队列中最多同时存储一层的节点)*/
void LevelOrderTraverse(BiTree tree) {if (tree == NULL) return;LinkedQueue queue;InitQueue(&queue);BiTNode *current = tree;Enqueue(&queue, current);while (!QueueIsEmpty(&queue)) {DeQueue(&queue, &current);printf("%c ", current->data);if (current->lchild != NULL) Enqueue(&queue, current->lchild);if (current->rchild != NULL) Enqueue(&queue, current->rchild);}printf("\n");
}/*** 计算二叉树深度* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
int BiTreeDepth(BiTree tree) {if (tree == NULL) return 0;int leftDepth = BiTreeDepth(tree->lchild);int rightDepth = BiTreeDepth(tree->rchild);return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
}/*** 统计节点个数* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
int NodeCount(BiTree tree) {if (tree == NULL) return 0;return NodeCount(tree->lchild) + NodeCount(tree->rchild) + 1;
}/*** 统计叶子节点个数* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
int LeafCount(BiTree tree) {if (tree == NULL) return 0;if (tree->lchild == NULL && tree->rchild == NULL) return 1;return LeafCount(tree->lchild) + LeafCount(tree->rchild);
}/*** 销毁二叉树* 时间复杂度: O(n), 其中n为节点个数(需要遍历所有节点)* 空间复杂度:*   - 平均情况: O(log n), 递归栈深度*   - 最坏情况: O(n), 当树退化为链表时*/
void DestroyBiTree(BiTree *tree) {if (tree == NULL || *tree == NULL) return;DestroyBiTree(&(*tree)->lchild);DestroyBiTree(&(*tree)->rchild);free(*tree);*tree = NULL;
}/*-------------------------测试函数------------------------*/int main() {printf("=== 二叉搜索树(BST)完整测试 ===\n\n");BiTree bst;InitBiTree(&bst);// 测试数据char keys[] = {'D', 'B', 'F', 'A', 'C', 'E', 'G'};int n = sizeof(keys) / sizeof(keys[0]);printf("1. 创建二叉搜索树\n");printf("   插入序列: ");for (int i = 0; i < n; i++) {printf("%c ", keys[i]);}printf("\n");CreateBST(&bst, keys, n);printf("\n2. 遍历验证\n");printf("   中序遍历: ");InOrderTraverse(bst);printf("\n");printf("   层次遍历: ");LevelOrderTraverse(bst);printf("\n3. 搜索测试\n");char testKeys[] = {'C', 'X', 'A', 'Z'};for (int i = 0; i < 4; i++) {BiTNode *result = BST_Search(bst, testKeys[i]);if (result) {printf("   找到节点: %c\n", testKeys[i]);} else {printf("   未找到节点: %c\n", testKeys[i]);}}printf("\n4. 最值测试\n");BiTNode *minNode = BST_FindMin(bst);BiTNode *maxNode = BST_FindMax(bst);printf("   最小值: %c\n", minNode->data);printf("   最大值: %c\n", maxNode->data);printf("\n5. 删除操作测试\n");printf("   删除前中序遍历: ");InOrderTraverse(bst);printf("\n");printf("   删除节点 'B'\n");BST_Delete(&bst, 'B');printf("   删除后中序遍历: ");InOrderTraverse(bst);printf("\n");printf("\n6. BST属性验证\n");if (IsBST(bst)) {printf("   这是一棵二叉搜索树!\n");} else {printf("   这不是二叉搜索树!\n");}printf("\n7. 树属性统计\n");printf("   树深度: %d\n", BiTreeDepth(bst));printf("   节点总数: %d\n", NodeCount(bst));printf("   叶子节点数: %d\n", LeafCount(bst));printf("\n8. 非递归操作测试\n");printf("   插入节点'H'\n");BST_Insert_NonRecursive(&bst, 'H');printf("   插入后中序遍历: ");InOrderTraverse(bst);printf("\n");printf("   非递归查找 'E': ");BiTNode *nonRecResult = BST_Search_NonRecursive(bst, 'E');if (nonRecResult) {printf("找到!\n");} else {printf("未找到!\n");}printf("\n9. 内存清理\n");DestroyBiTree(&bst);printf("   二叉搜索树已销毁!\n");return 0;
}

运行结果

=== 二叉搜索树(BST)完整测试 ===1. 创建二叉搜索树插入序列: D B F A C E G2. 遍历验证中序遍历: A B C D E F G层次遍历: D B F A C E G3. 搜索测试找到节点: C未找到节点: X找到节点: A未找到节点: Z4. 最值测试最小值: A最大值: G5. 删除操作测试删除前中序遍历: A B C D E F G删除节点 'B'删除后中序遍历: A C D E F G6. BST属性验证这是一棵二叉搜索树!7. 树属性统计树深度: 3节点总数: 6叶子节点数: 38. 非递归操作测试插入节点'H'插入后中序遍历: A C D E F G H非递归查找 'E': 找到!9. 内存清理二叉搜索树已销毁!

二叉搜索树 (BST) 相关知识点

1. 基础概念

二叉搜索树定义:

  • 左子树所有节点值 < 根节点值
  • 右子树所有节点值 > 根节点值
  • 左右子树也都是BST

重要性质:

  • 中序遍历结果为有序序列
  • 查找、插入、删除的平均时间复杂度:O(log n)
  • 最坏情况(退化为链表)时间复杂度:O(n)

时间复杂度分析

操作平均情况最坏情况空间复杂度
查找O(log n)O(n)O(1)非递归 / O(h)递归
插入O(log n)O(n)O(1)非递归 / O(h)递归
删除O(log n)O(n)O(h)
中序遍历O(n)O(n)O(h)

2. 常见题型及解析

题型1: BST基本操作实现

解答模版:

/*** BST节点插入(递归)* 时间复杂度: 平均O(log n), 最坏O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
bool BST_Insert(BiTree *T, int key) {if (T == NULL) return false;if (*T == NULL) {*T = (BSTNode*)malloc(sizeof(BSTNode));(*T)->data = key;(*T)->lchild = (*T)->rchild = NULL;return true;}if (key < (*T)->data) {return BST_Insert(&(*T)->lchild, key);} else if (key > (*T)->data) {return BST_Insert(&(*T)->rchild, key);} else {return false; // 重复元素}
}/*** BST查找(非递归)* 时间复杂度: 平均O(log n), 最坏O(n)* 空间复杂度: O(1)*/
BSTNode* BST_Search(BiTree T, int key) {while (T != NULL) {if (key == T->data) return T;else if (key < T->data) T = T->lchild;else T = T->rchild;}return NULL;
}/*** BST删除节点* 时间复杂度: 平均O(log n), 最坏O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
bool BST_Delete(BiTree *T, int key) {if (T == NULL || *T == NULL) return false;if (key < (*T)->data) {return BST_Delete(&(*T)->lchild, key);} else if (key > (*T)->data) {return BST_Delete(&(*T)->rchild, key);} else {// 找到要删除的节点BSTNode *temp = *T;// 情况1:叶子节点或只有一个子节点if ((*T)->lchild == NULL) {*T = (*T)->rchild;free(temp);} else if ((*T)->rchild == NULL) {*T = (*T)->lchild;free(temp);} else {// 情况2:有两个子节点// 找到右子树的最小节点BSTNode *minNode = (*T)->rchild;while (minNode->lchild != NULL) {minNode = minNode->lchild;}// 用最小节点的值替换当前节点(*T)->data = minNode->data;// 递归删除右子树中的最小节点BST_Delete(&(*T)->rchild, minNode->data);}return true;}
}

题型2: 判断二叉树是否为BST

解答方法1: 利用中序遍历性质

/*** 方法1:中序遍历判断* 时间复杂度: O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
bool isBST_InOrder(BiTree T) {static int prev = INT_MIN;  // 静态变量记录前一个值static bool first = true;   // 标记是否为第一个节点if (T == NULL) return true;// 检查左子树if (!isBST_InOrder(T->lchild)) return false;// 检查当前节点if (!first && T->data <= prev) return false;first = false;prev = T->data;// 检查右子树return isBST_InOrder(T->rchild);
}

解答方法2: 递归检查上下界

/*** 方法2:上下界递归判断* 时间复杂度: O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
bool isBST_Recursive(BiTree T, int min, int max) {if (T == NULL) return true;// 检查当前节点是否在合法范围内if (T->data <= min || T->data >= max) return false;// 递归检查左右子树return isBST_Recursive(T->lchild, min, T->data) && isBST_Recursive(T->rchild, T->data, max);
}// 调用方式
bool isBST(BiTree T) {return isBST_Recursive(T, INT_MIN, INT_MAX);
}

题型3: BST第k小的元素

解答模版:

/*** BST中第k小的元素* 时间复杂度: O(k)* 空间复杂度: 平均O(log n), 最坏O(n)*/
int kthSmallest(BiTree T, int k) {LinkedStack stack;InitStack(&stack);BSTNode *current = T;int count = 0;while (current != NULL || !StackIsEmpty(&stack)) {while (current != NULL) {Push(&stack, current);current = current->lchild;}Pop(&stack, &current);count++;if (count == k) return current->data;current = current->rchild;}return -1; // 未找到
}

题型4: BST的范围和

题目要求: 计算BST中在[L,R]范围内的所有节点值之和

解答模版:

/*** BST范围求和* 时间复杂度: O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
int rangeSumBST(BiTree T, int L, int R) {if (T == NULL) return 0;if (T->data < L) {// 当前节点值小于L,只需递归右子树return rangeSumBST(T->rchild, L, R);} else if (T->data > R) {// 当前节点值大于R,只需递归左子树return rangeSumBST(T->lchild, L, R);} else {// 当前节点在范围内,递归左右子树并加上当前值return T->data + rangeSumBST(T->lchild, L, R) + rangeSumBST(T->rchild, L, R);}
}

题型5: 有序数组构建BST

题目要求: 将有序数组转换为高度平衡的BST

解答模版:

/*** 有序数组构建平衡BST* 时间复杂度: O(n)* 空间复杂度: O(log n)*/
BSTNode* sortedArrayToBST(int arr[], int start, int end) {if (start > end) return NULL;// 总是选择中间元素作为根节点int mid = start + (end - start) / 2;BSTNode *root = (BSTNode*)malloc(sizeof(BSTNode));root->data = arr[mid];root->lchild = sortedArrayToBST(arr, start, mid - 1);root->rchild = sortedArrayToBST(arr, mid + 1, end);return root;
}

题型6: BST转换为双向链表

题目要求: 将BST转换为排序的双向链表(不允许创建新节点)

解答模版:

/*** BST转双向链表* 时间复杂度: O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
void BSTToDLL(BiTree T, BSTNode **head, BSTNode **prev) {if (T == NULL) return;// 递归处理左子树BSTToDLL(T->lchild, head, prev);// 处理当前节点if (*prev == NULL) {*head = T;  // 第一个节点} else {(*prev)->rchild = T;T->lchild = *prev;}*prev = T;// 递归处理右子树BSTToDLL(T->rchild, head, prev);
}// 调用示例
BSTNode* convertBSTToDLL(BiTree T) {BSTNode *head = NULL;BSTNode *prev = NULL;BSTToDLL(T, &head, &prev);return head;
}

题型7: BST中两节点最近公共祖先

题目要求: 在BST中找到两个节点的最近公共祖先

解答模版:

/*** BST中最近公共祖先* 时间复杂度: O(h), h为树高* 空间复杂度: O(1)非递归 / O(h)递归*/
BSTNode* lowestCommonAncestor(BSTNode* root, BSTNode* p, BSTNode* q) {// 利用BST性质while (root != NULL) {if (p->data < root->data && q->data < root->data) {root = root->lchild;  // 都在左子树} else if (p->data > root->data && q->data > root->data) {root = root->rchild;  // 都在右子树} else {return root;  // 分别在左右子树,当前节点就是LCA}}return NULL;
}

真题1: BST验证

题目: 实现一个函数,验证二叉树是否为BST

参考答案:

/*** 验证BST* 时间复杂度: O(n)* 空间复杂度: 平均O(log n), 最坏O(n)*/
bool isValidBST(BiTree root) {return isValidBSTHelper(root, LONG_MIN, LONG_MAX);
}bool isValidBSTHelper(BiTree node, long min_val, long max_val) {if (node == NULL) return true;if (node->data <= min_val || node->data >= max_val) return false;return isValidBSTHelper(node->lchild, min_val, node->data) && isValidBSTHelper(node->rchild, node->data, max_val);
}

真题2: BST迭代器

题目: 设计BST迭代器,实现hasNext()和next()操作

参考答案:

typedef struct {LinkedStack stack;
} BSTIterator;/*** 初始化BST迭代器* 时间复杂度: O(h)* 空间复杂度: O(h)*/
void bstIteratorInit(BSTIterator *iter, BiTree root) {InitStack(&iter->stack);// 将左子树全部入栈while (root != NULL) {Push(&iter->stack, root);root = root->lchild;}
}/*** 返回下一个最小值* 时间复杂度: 平均O(1)* 空间复杂度: O(h)*/
int bstIteratorNext(BSTIterator *iter) {BSTNode *node;Pop(&iter->stack, &node);int result = node->data;// 如果有右子树,将其左子树全部入栈node = node->rchild;while (node != NULL) {Push(&iter->stack, node);node = node->lchild;}return result;
}/*** 判断是否还有下一个元素* 时间复杂度: O(1)* 空间复杂度: O(1)*/
bool bstIteratorHasNext(BSTIterator *iter) {return !StackIsEmpty(&iter->stack);
}
http://www.dtcms.com/a/435296.html

相关文章:

  • 做英文网站的标准字体鱼巴士设计师服务平台
  • 做网站流量是什么珠海网站建设优化
  • NTLite(操作系统定制工具)
  • 模块即服务?厘清 Linux 系统服务与微服务架构的本质区别
  • 成都电商网站开发免费送的广告怎么在网站上做
  • 熊猫网站ppt外贸网建站
  • 设计模式第六章(观察者模式)
  • C4D R20新增功能平滑滤镜和调整外形滤镜深度解析
  • 数据安全风险评估
  • 动漫共和国 | window版本
  • 校园网站如何建立在浙学网页设计与制作答案
  • 企业创建网站的途径都有啥深圳市浩天建设网站
  • 做外贸国外网站苏宁电器网站建设特点分析
  • AI自动化测试:接口测试全流程自动化的实现方法——技术深度与行业实践剖析
  • LeeCode 328. 奇偶链表
  • 【Linux CentOS 7 版本更换yum源】
  • ⚡ WSL2 搭建 s5p6818 Linux 嵌入式开发平台 (part 3):Wifi驱动移植、ssh移植、e2fsprogs移植
  • 建网站怎么弄在线制作app平台
  • 进程的概念(下)
  • 网站开发安装win10家庭版广告设计公司组织结构图
  • 自媒体时代做网站有前途吗网站备案审批号
  • 为何“过度工程”会发生
  • Linux多线程服务端编程:使用muduo C++网络库学习之环境配置
  • 新质生产力
  • 嵌入式八股文篇——P1 关键字篇
  • 河源正规网站建设价格广东省深圳市公司
  • Core Speech Kit简介
  • 【OTA专题】3.实现简单的boot和APP程序逻辑
  • 营销单页模板网站怎么买网站域名
  • 织梦做商城网站wordpress 自建邮件