zoukankan      html  css  js  c++  java
  • 结点遍历C语言实现二叉树的常用的算法(递归与非递归实现遍历)

    在本文中,我们主要介绍结点遍历的内容,自我感觉有个不错的建议和大家分享下

    队列头文件:
    #include <stdio.h>
    
    #include "BinaryTree.h"
    
    //
    // 队列头文件:Queue.h
    
    #ifndef QUEUE_H
    #define QUEUE_H
    
    //
    // 队列最大元素个数
    #define MAX_QUEUE_SIZE 10
    
    typedef BTree QueueElemType;
    
    //
    // 队列结构体
    typedef struct tagQueue
    {
    	BTree *base;
    	int front;      // 头指针指示器,若队列不空,则指向队列中队头元素
    	int rear;       // 尾指针指示吕,若队列不空,则指向队列队尾的下一个位置
    }Queue;
    
    //
    // 构造一个空的队列
    int InitializeQueue(Queue *pQueue);
    
    //
    // 判断队列是否为空
    int IsQueueEmpty(Queue queue);
    
    //
    // 判断队列是否为满
    int IsQueueFull(Queue queue);
    
    //
    // 入队
    int EnQueue(Queue *pQueue, QueueElemType e);
    
    //
    // 退队
    int DeQueue(Queue *pQueue, QueueElemType *e);
    #endif
    队列实现文件:
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include "Queue.h"
    #include "BinaryTree.h"
    
    //
    // 循环队列的实现文件:Queue.c
    
    //
    // 构造一个空的队列
    int InitializeQueue(Queue *pQueue)
    {
    	pQueue->base = (QueueElemType *)malloc(sizeof(QueueElemType) * MAX_QUEUE_SIZE);
    
    	// 请求空间失败,则退出程序
    	if (pQueue->base == NULL)
    	{
    		exit(OVERFLOW);
    	}
    
    	pQueue->front = pQueue->rear = 0;
    
    	return OK;
    }
    
    //
    // 判断队列是否为空
    // 返回0表现非空,返回非0,表现空
    int IsQueueEmpty(Queue queue)
    {
    	return !(queue.front - queue.rear);
    }
    
    //
    // 判断队列是否为满
    // 返回0表现示满,返回非0,表现已满
    int IsQueueFull(Queue queue)
    {
    	return (queue.rear + 1) % MAX_QUEUE_SIZE == queue.front ;
    }
    
    //
    // 入队
    int EnQueue(Queue *pQueue, QueueElemType e)
    {
    	if (IsQueueFull(*pQueue))
    	{
    		printf("队列已经满,不能入队!\n");
    
    		return ERROR;
    	}
    	else
    	{
    		pQueue->base[pQueue->rear] = e;
    		pQueue->rear = (pQueue->rear + 1) % MAX_QUEUE_SIZE;
    
    		return OK;
    	}
    }
    
    //
    // 退队
    int DeQueue(Queue *pQueue, QueueElemType *e)
    {
    	if (IsQueueEmpty(*pQueue))
    	{
    		printf("队列为空,不能执行退队操作\n");
    
    		return ERROR;
    	}
    	else
    	{
    		*e = pQueue->base[pQueue->front];
    		pQueue->front = (pQueue->front + 1) % MAX_QUEUE_SIZE;
    
    		return OK;
    	}
    }
    栈头文件:
    
    #ifndef STACK_H
    #define STACK_H
    
    
    #include <stdio.h>
    
    #include "BinaryTree.h"
    //
    // 栈的头文件声明部份:Stack.h
    
    // 栈初始容量
    #define STACK_INIT_SIZE 20
    
    // 栈容量不够用时,栈的增量
    #define STACK_SIZE_INCREMENT 10
    
    typedef BTree StackElemType;
    
    //
    // 次序栈结构体
    typedef struct tagStack
    {
    	StackElemType *base; // 指向栈底
    	StackElemType *top;  // 指向栈顶
    	int stackSize;       // 栈的大小
    }Stack;
    
    //
    // 初始化栈
    int InitStack(Stack *s);
    
    //
    // 销毁栈
    void DestroyStack(Stack *s);
    
    //
    // 入栈
    void Push(Stack *s, StackElemType e);
    
    //
    // 出栈
    void Pop(Stack *s, StackElemType *e);
    
    //
    // 判断栈是否为空
    int IsStackEmpty(Stack s);
    
    //
    // 取栈顶元素
    int GetTop(Stack s, StackElemType *e);
    
    #endif
    栈实现文件:
    
    //
    // 次序栈的实现文件:Stack.c
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include "Stack.h"
    
    //
    // 初始化栈
    int InitStack(Stack *s)
    {
    	s->base = (StackElemType *)malloc(sizeof(StackElemType) * STACK_INIT_SIZE);
    
    	if (!s->base) // 请求栈内存失败
    	{
    		exit(OVERFLOW);
    	}
    
    	s->top = s->base;
    	s->stackSize = STACK_INIT_SIZE;
    
    	return OK;
    }
    
    //
    // 销毁栈
    void DestroyStack(Stack *s)
    {
        if (s != NULL)
        {
    		free(s->base);
    
    		s->top = NULL;
    		s->base = NULL;
    
    		s->stackSize = 0;
        }
    }
    
    //
    // 入栈
    void Push(Stack *s, StackElemType e)
    {
    	StackElemType *tmp;
    	if (s->top - s->base >= s->stackSize) // 栈已经满
    	{
    		tmp = (StackElemType *)realloc(s->base, (STACK_SIZE_INCREMENT + s->stackSize) 
    			                                     * sizeof(StackElemType));
    		if (!tmp)
    		{
    			exit(OVERFLOW); // 重新分配失败则退出
    		}
    
    		s->base = tmp;
    		s->top = s->base + s->stackSize;
    		s->stackSize += STACK_SIZE_INCREMENT;
    	}
    
    	*(s->top) = e;
    	s->top++;
    }
    
    //
    // 出栈
    void Pop(Stack *s, StackElemType *e)
    {
    	if (s->top == s->base) // 如果栈为空栈
    	{
    		return;
    	}
    
    	*e = *(--s->top);
    }
    
    //
    // 判断栈是否为空
    // 返回非0表现空
    int IsStackEmpty(Stack s)
    {
    	return !(s.top - s.base);
    }
    
    //
    // 取栈顶元素
    int GetTop(Stack s, StackElemType *e)
    {
    	if (!IsStackEmpty(s))
    	{
    		*e = *(s.top - 1); // 此处犯错,原因? 
    
    		return OK;
    	}
    	else
    	{
    		return ERROR;
    	}
    }
    二叉树头文件:
    #include <stdio.h>
    
    //
    // 二叉树的头文件:BinaryTree.h
    
    #ifndef BINARY_TREE_H
    #define BINARY_TREE_H
    
    #define OK 1
    #define ERROR 0
    #define OVERFLOW -1
    
    //
    // 结点的数据的类型
    typedef char ElemType;
    
    //
    // 二叉树结构体
    typedef struct tagBinaryTree
    {
    	ElemType data;                 // 数据
    	struct tagBinaryTree *lchild;     // 指向左孩子
    	struct tagBinaryTree *rchild;     // 指向右孩子
    }BTree;
    
    #endif
        每日一道理
    成功的花朵开放在啊勤劳的枝头,失败的苦果孕育在懒惰的温床之中。
    二叉树实现文件及测试:
    #include <stdio.h>
    #include <stdlib.h>
    
    #include "BinaryTree.h"
    #include "Queue.h"
    #include "Stack.h"
    
    /*****************************************************************************
    * 方法名:CreateBinaryTree
    * 描述:  递归创立一棵二叉树,按先序输入二叉树中结点的元素的值,“#”号表现空树
    * 参数:  pBTree--指向BTree结构体的指针的指针
    * 返回值:返回OK--表现创立成功
    *         返回ERROR--表现创立失败
    ******************************************************************************/
    int CreateBinaryTree(BTree **pBTree)
    {
    	ElemType data;
    	
    	scanf("%c", &data);
    
    	if (data == '#')
    	{
    		*pBTree = NULL;
    
    		return OK;
    	}
    	else 
    	{
    		if (((*pBTree) = (BTree *)malloc(sizeof(BTree))) == NULL)
    		{
    			exit(OVERFLOW);
    		}
    
    		(*pBTree)->data = data;
    		CreateBinaryTree(&(*pBTree)->lchild); // 创立左子树
    		CreateBinaryTree(&(*pBTree)->rchild); // 创立右子树
    	}
    
    	return OK;
    }
    
    /*****************************************************************************
    * 方法名:PreOrderTraverse
    * 描述:  先序遍历二叉树
    * 参数:  pBTree--指向BTree结构体的指针
    ******************************************************************************/
    void PreOrderTraverse(BTree *pBTree)
    {
    	if (pBTree)
    	{
    		printf("%c", pBTree->data);       // 先序访问根结点
    
    		PreOrderTraverse(pBTree->lchild); // 先序遍历左子树
    		PreOrderTraverse(pBTree->rchild); // 先序遍历右子树
    	}
    }
    
    /*****************************************************************************
    * 方法名:InOrderTraverse
    * 描述:  中序遍历二叉树
    * 参数:  pBTree--指向BTree结构体的指针
    ******************************************************************************/
    void InOrderTraverse(BTree *pBTree)
    {
    	if (pBTree)
    	{
    		InOrderTraverse(pBTree->lchild); // 中序遍历左子树
    		printf("%c", pBTree->data);       // 中序访问根结点
    		InOrderTraverse(pBTree->rchild); // 中序遍历右子树
    	}
    }
    
    /*****************************************************************************
    * 方法名:PostOrderTraverse
    * 描述:  后序遍历二叉树
    * 参数:  pBTree--指向BTree结构体的指针
    ******************************************************************************/
    void PostOrderTraverse(BTree *pBTree)
    {
    	if (pBTree)
    	{
    		PostOrderTraverse(pBTree->lchild);   // 后序遍历左子树
    		PostOrderTraverse(pBTree->rchild);   // 后序遍历右子树
    				printf("%c", pBTree->data); // 后序访问根结点
    	}
    }
    
    /*****************************************************************************
    * 方法名:LevelOrderTraverse
    * 描述:  层序遍历二叉树
    * 参数:  pBTree--指向BTree结构体的指针
    ******************************************************************************/
    void LevelOrderTraverse(BTree *pBTree)
    {
    	Queue queue;         // 队列变量
    	QueueElemType e;     // 队列元素指针变量
    
    	InitializeQueue(&queue); // 初始化队列
    
    	if (pBTree != NULL)
    	{
    		EnQueue(&queue, *pBTree); // 将根结点指针入队
    	}
    
    	while (!IsQueueEmpty(queue))
    	{
    		DeQueue(&queue, &e);
    
    		printf("%c", e.data);
    
    		if (e.lchild != NULL)   // 若存在左孩子,则左孩子入队
    		{
    			EnQueue(&queue, *e.lchild);
    		}
    
    		if (e.rchild != NULL)   // 若存在右孩子,则右孩子入队
    		{
    			EnQueue(&queue, *e.rchild);
    		}
    	}
    }
    
    /*****************************************************************************
    * 方法名:GetDepth
    * 描述:  获得树的深度
    * 参数:  pBTree--指向BTree结构体的指针
    * 返回值:树的深度
    ******************************************************************************/
    int GetDepth(BTree *pBTree)
    {
    	int depth = 0;
    	int leftDepth = 0;
    	int rightDepth = 0;
    
    	if (pBTree)
    	{
    		leftDepth = GetDepth(pBTree->lchild);  // 获得左子树的深度
    		rightDepth = GetDepth(pBTree->rchild); // 获得右子树的深度
    
    		depth = leftDepth > rightDepth ? leftDepth + 1: rightDepth + 1;
    	}
    
    	return depth;
    }
    
    /*****************************************************************************
    * 方法名:IsLeaf
    * 描述:  判断该结点是否为叶子结点
    * 参数:  node--结点
    * 返回值:1--表现叶子结点,0--表现非叶子结点
    ******************************************************************************/
    int IsLeaf(BTree node)
    {
    	if (node.lchild == NULL && node.rchild == NULL)
    	{
    		return 1;
    	}
    
    	return 0;
    }
    
    /*****************************************************************************
    * 方法名:TraverseLeafNodes
    * 描述:  遍历全部的叶子结点
    * 参数:  pBTree--指向BTree结构体的指针
    ******************************************************************************/
    void TraverseLeafNodes(BTree *pBTree)
    {
    	if (pBTree != NULL)
    	{
    		if (1 == IsLeaf(*pBTree))
    		{
    			printf("%c", pBTree->data);
    		}
    		else
    		{
    			TraverseLeafNodes(pBTree->lchild);
    			TraverseLeafNodes(pBTree->rchild);
    		}	
    	}
    }
    
    //
    // 判断一棵二叉树是否为平衡二叉树
    // 平衡二叉树的定义: 如果恣意节点的阁下子树的深度相差不超过1,那这棵树就是平衡二叉树
    // 算法思路:递归判断每一个节点的阁下子树的深度是否相差大于1,如果大于1,说明该二叉树不
    //           是平衡二叉树,否则继续递归判断
    int IsBalanceBinaryTree(BTree *pBTree)
    {
    	int leftDepth = 0;
    	int rightDepth = 0;
    	int distance = 0; 
    
    	if (pBTree != NULL)
    	{
    		leftDepth = GetDepth(pBTree->lchild);  // 获得左子树的深度
    		rightDepth = GetDepth(pBTree->rchild); // 获得右子树的深度
    		distance = leftDepth > rightDepth ? leftDepth - rightDepth : rightDepth - leftDepth;
    
    		return distance > 1 ? 0 : IsBalanceBinaryTree(pBTree->lchild) && IsBalanceBinaryTree(pBTree->rchild);
    	}
    }
    
    //
    // 获得叶子结点的个数
    int GetLeafCount(BTree *pBTree)
    {
    	int count = 0;
    
    	if (pBTree != NULL)
    	{
    		if (IsLeaf(*pBTree))
    		{
    			count++;
    		}
    		else
    		{
    			count = GetLeafCount(pBTree->lchild) + GetLeafCount(pBTree->rchild);
    		}
    	}
    
    	return count;
    }
    
    //
    // 获得度为1的结点的个数
    int GetCountOfOneDegree(BTree *pBTree)
    {
    	int count = 0;
    
    	if (pBTree != NULL)
    	{
    		if ((pBTree->lchild != NULL && pBTree->rchild == NULL) || (pBTree->lchild == NULL && pBTree->rchild != NULL))
    		{
    			count++;
    		}
    
    		count += GetCountOfOneDegree(pBTree->lchild) + GetCountOfOneDegree(pBTree->rchild);
    	}
    
    	return count;
    }
    
    //
    // 获得度为2的结点的个数
    int GetCountOfTwoDegree(BTree *pBTree)
    {
    	int count = 0;
    
    	if (pBTree != NULL)
    	{
    		if (pBTree->lchild != NULL && pBTree->rchild != NULL)
    		{
    			count++;
    		}
    
    		count += GetCountOfTwoDegree(pBTree->lchild) + GetCountOfTwoDegree(pBTree->rchild);
    	}
    
    	return count;
    }
    //
    // 获得二叉树的结点的总数
    int GetNodesCount(BTree *pBTree)
    {
    	int count = 0;
    
    	if (pBTree != NULL)
    	{
    		count++;
    
    		count += GetNodesCount(pBTree->lchild) + GetNodesCount(pBTree->rchild);
    	}
    
    	return count;
    }
    
    //
    // 交换阁下子树
    void SwapLeftRightSubtree(BTree **pBTree)
    {
    	BTree *tmp = NULL;
    
    	if (*pBTree != NULL)
    	{
    		// 交换以后结点的阁下子树
    		tmp = (*pBTree)->lchild;
    		(*pBTree)->lchild = (*pBTree)->rchild;
    		(*pBTree)->rchild = tmp;
    
    		SwapLeftRightSubtree(&(*pBTree)->lchild);
    		SwapLeftRightSubtree(&(*pBTree)->rchild);
    	}
    }
    
    //
    // 判断值e是否为二叉树中某个结点的值,返回其所在的层数,返回0表现不在树中
    int GetLevelByValue(BTree *pBTree, ElemType e)
    {
    	int leftDepth = 0;
    	int rightDepth = 0;
    	int level = 0;
    
    	if (pBTree->data == e)//这里的1是绝对于以pBTree为根节点的层数值。
    	{
    		return 1;
    	}
    
    	if (pBTree->lchild != NULL)//leftDepth所代表的层数是绝对以pBTree的左节点为根的树的层数
    	{
    		leftDepth = GetLevelByValue(pBTree->lchild, e);
    	}
    
    	if (pBTree->rchild != NULL)
    	{
    		// rightDepth所代表的层数是绝对以pBTree的右节点为根的树的层数
    		rightDepth = GetLevelByValue(pBTree->rchild, e);
    	}
    
    	//
    	// 查找结果要么在左子树找到,要么在右子树中找到,要么找不到
    	if (leftDepth > 0 && rightDepth == 0) // 在左子树中找到
    	{
    		level = leftDepth;
    	}
    	else if (leftDepth == 0 && rightDepth > 0) // 在右子树中找到
    	{
    		level = rightDepth;
    	}
    
    	if (leftDepth != 0 || rightDepth != 0) // 判断是否找到该结点
    	{
    		level++;
    	}
    
    	return level;
    }
    
    //
    // 非递归中序遍历
    void NoneRecursionInOrder(BTree tree)
    {
    	Stack s;
    	StackElemType *p = NULL, *q;
    
    	q = (StackElemType *)malloc(sizeof(StackElemType)); // 用于指向退栈元素的地址
    	InitStack(&s);
    	p = &tree;
    
    	while (p || !IsStackEmpty(s))
    	{
    		if (p)
    		{
    			Push(&s, *p); 
    			p = p->lchild;
    		}
    		else
    		{
    			Pop(&s, q);
    			printf("%c", q->data);
    			p = q->rchild;
    		}
    	}
    
    	free(q);
    }
    
    //
    // 非递归前序遍历
    void NoneRecursionPreOrder(BTree tree)
    {
    	Stack s;
    	StackElemType *p = NULL, *q;
    
    	q = (StackElemType *)malloc(sizeof(StackElemType)); // 用于指向退栈元素的地址
    	InitStack(&s);
    	p = &tree;
    
    	while (p || !IsStackEmpty(s))
    	{
    		while (p)
    		{
    			printf("%c", p->data); // 访问根结点
    			Push(&s, *p);           // 根结点指针入栈
    			p = p->lchild;          // 始终向左走到底
    		}
    
    		Pop(&s, q);
    		p = q->rchild;   // 向右走一步
    	}
    
    	free(q);
    }
    
    //
    // 非递归后序遍历
    void NoneRecursionPostOrder(BTree tree)
    {
    	StackElemType *stack[STACK_INIT_SIZE], *p;
    	int tag[STACK_INIT_SIZE], // 值只有0和1,其中0表现该结点的左子树已经访问
    		                      // 值为1表现该结点的右子树已经访问
    		top = 0; // 栈顶指示器
    
    	p = &tree;
    
    	while (p || top != 0)// 树未遍历终了或者栈不空
    	{
    		while (p)
    		{
    			top++;
    			stack[top] = p; 
    			tag[top] = 0;
    			p = p->lchild; // 从根开始向左走到底
    		}
    
    		if (top > 0) // 栈不空
    		{
    			if (tag[top] == 1)// 表现已经访问该结点的右子树,并返回 
    			{
    				p = stack[top--]; // 退栈
    				printf("%c", p->data);
    
    				p = NULL; // 下次进入循环时,就不会再遍历左子树
    			}
    			else // 表现刚从该结点的左子树返回,当初开始遍历右子树
    			{
    				p = stack[top]; // 取栈顶元素
    				if (top > 0) // 栈不空
    				{
    					p = p->rchild;
    					tag[top] = 1; // 标识该结点的右子树已经访问
    				}
    			}
    		}
    	}
    }
    
    int main()
    {
    	BTree *tree = NULL;
    
    	printf("按先序输入二叉树结点元素的值,输入#表现空树:\n");
    
    	freopen("test.txt", "r", stdin);
    
    	if (CreateBinaryTree(&tree) == OK)  // 创立二叉树
    	{
    		printf("二叉树创立成功!\n");
    	}
    
    	printf("先序遍历(#表现空子树):\n");
    	PreOrderTraverse(tree);
    
    	printf("\n中序遍历(#表现空子树):\n");
    	InOrderTraverse(tree);
    
    	printf("\n后序遍历(#表现空子树):\n");
    	PostOrderTraverse(tree);
    
    	printf("\n树的深度为:%d\n", GetDepth(tree));
    
    	printf("\n层序遍历:\n");
    	LevelOrderTraverse(tree);
    
    	printf("\n遍历叶子结点:\n");
    	TraverseLeafNodes(tree);
    
    	printf("\n叶子结点的个数:%d\n", GetLeafCount(tree));
    	printf("度为1的结点的个数:%d\n", GetCountOfOneDegree(tree));
    	printf("度为2的结点的个数:%d\n", GetCountOfTwoDegree(tree));
    	printf("\n二叉树的结点总数为:%d\n", GetNodesCount(tree));
    
    	printf("\n该二叉树是否为平衡二叉树?\n");
    	if (IsBalanceBinaryTree(tree))
    	{
    		printf("Yes!\n");
    	}
    	else
    	{
    		printf("No!\n");
    	}
    
    
    	printf("\n结点值为A的结点在第%d层\n", GetLevelByValue(tree, 'A'));
    	printf("\n结点值为B的结点在第%d层\n", GetLevelByValue(tree, 'B'));
    	printf("\n结点值为C的结点在第%d层\n", GetLevelByValue(tree, 'C'));
    	printf("\n结点值为D的结点在第%d层\n", GetLevelByValue(tree, 'D'));
    	printf("\n结点值为E的结点在第%d层\n", GetLevelByValue(tree, 'E'));
    	printf("\n结点值为F的结点在第%d层\n", GetLevelByValue(tree, 'F'));
    	printf("\n结点值为G的结点在第%d层\n", GetLevelByValue(tree, 'G'));
    	printf("\n结点值为M的结点在第%d层\n", GetLevelByValue(tree, 'M'));
    
    	printf("\n非递归中序遍历:\n");
    	NoneRecursionInOrder(*tree);
    
    	printf("\n非递归前序遍历:\n");
    	NoneRecursionPreOrder(*tree);
    
    	printf("\n非递归后序遍历:\n");
    	NoneRecursionPostOrder(*tree);
    
    	printf("\n=======================================================\n");
    
    	printf("下面执行交换阁下子树操作:\n");
    	SwapLeftRightSubtree(&tree);
    
    	printf("先序遍历(#表现空子树):\n");
    	PreOrderTraverse(tree);
    
    	printf("\n中序遍历(#表现空子树):\n");
    	InOrderTraverse(tree);
    
    	printf("\n后序遍历(#表现空子树):\n");
    	PostOrderTraverse(tree);
    
    	printf("\n树的深度为:%d\n", GetDepth(tree));
    
    	printf("\n层序遍历:\n");
    	LevelOrderTraverse(tree);
    
    	printf("\n遍历叶子结点:\n");
    	TraverseLeafNodes(tree);
    
    	
    
    	fclose(stdin);
    
    	printf("\n");
    	return 0;
    }
    text.txt的内容:
    ABC##DE#G##F###

    文章结束给大家分享下程序员的一些笑话语录: 打赌
    飞机上,一位工程师和一位程序员坐在一起。程序员问工程师是否乐意和他一起玩一种有趣的游戏。工程师想睡觉,于是他很有礼貌地拒绝了,转身要睡觉。程序员坚持要玩并解释说这是一个非常有趣的游戏:"我问你一个问题,如果你不知道答案,我付你5美元。然后你问我一个问题,如果我答不上来,我付你5美元。"然而,工程师又很有礼貌地拒绝了,又要去睡觉。  程序员这时有些着急了,他说:"好吧,如果你不知道答案,你付5美元;如果我不知道答案,我付50美元。"果然,这的确起了作用,工程师答应了。程序员就问:"从地球到月球有多远?"工程师一句话也没有说,给了程序员5美元。  现在轮到工程师了,他问程序员:"什么上山时有三条腿,下山却有四条腿?"程序员很吃惊地看着工程师,拿出他的便携式电脑,查找里面的资料,过了半个小时,他叫醒工程师并给了工程师50美元。工程师很礼貌地接过钱又要去睡觉。程序员有些恼怒,问:"那么答案是什么呢?"工程师什么也没有说,掏出钱包,拿出5美元给程序员,转身就去睡觉了。

  • 相关阅读:
    禅道admin忘记密码
    redis conf 解析
    MySQL 安装
    Centos7上安装docker (抄)
    Linux查看CPU和内存使用情况 抄
    上传图片到阿里云OSS和获取上传图片的外网url的步骤
    docker mysql
    Oracle 11g,exp导出时空表、少表的解决办法
    使用com.aspose.words将word模板转为PDF乱码解决方案
    oracle数据库,检索出某几个字段不唯一的那些数据
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3078621.html
Copyright © 2011-2022 走看看