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

【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

目录

  • 前言:
  • 一、树的概念及结构
    • 1.树的概念
    • 2.树的相关概念
    • 3.树的存储
    • 4.树在实际中的运用
  • 二、二叉树概念及结构
    • 1.概念
    • 2.特殊的二叉树
      • (1)满二叉树
      • (2)完全二叉树
    • 3.二叉树的性质
    • 4.二叉树的存储
      • (1)顺序存储
      • (2)链式存储
  • 三、二叉树的顺序结构及实现
    • 1.二叉树的顺序结构
    • 2.堆的概念及结构
    • 3.堆的实现
      • (1)初始化堆
      • (2)销毁堆
      • (3)堆的插入
    • 向上调整算法
      • (4)堆的删除
    • 向下调整算法
      • (5)获取堆顶元素/堆的元素个数/判空
    • 4.堆排序
      • (1)堆排序版本1
      • (2)堆排序版本2——对数组原地排序
      • (3)优化堆排序版本2及时间复杂度的比较
      • (4)TopK问题

前言:

之前我们学习了顺序表、链表以及栈和队列这些数据结构,但这些数据结构都是线性的(一对一)。接下来要学习非线性的数据结构——树(二叉树),相比前面的,树的结构更加复杂,话不多说,直接进入正题吧。

一、树的概念及结构

1.树的概念

树是一种非线性的数据结构,它是一对多(也有可能是一对一) 的关系。它是由n(n>=0)个有限节点组成一个具有层次关系的集合。把它叫做树是因为它看起来 像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

有一个特殊的结点,称为根结点,根节点没有前驱结点
除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i
<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继,因此树是递归定义的。

现实中的树和数据结构中的树,例如:
在这里插入图片描述

注意:树形结构中,子树之间不能有交集,否则就不是树形结构
在这里插入图片描述

2.树的相关概念

树的相关概念是指树的根、分枝和叶子等等之间的联系,与人类的亲缘关系类似
在这里插入图片描述

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A是一对6,所以A的度为6
叶节点或终端结节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支结点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为堂兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;

注意:在计算节点的层次的时候,从最上面的节点往下数可以从0开始,也可以从1开始,但是更推荐从1开始。
因为树可能是空树、只有一个节点的树或者是有多个节点的树,如果从0开始计算,那么节点的层次(或者说树的高度)当为0时到底是空树还是只有一个节点的树不易分清,所以为了方便数树的高度,从1开始比较好。

在这里插入图片描述

3.树的存储

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既要保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法。(又称左孩子右兄弟表示法

struct TreeNode
{int val;struct TreeNode* firstchild;struct TreeNode* nextbrother;
};

一个节点可以任意指向多个节点,这个节点的firstchild指针指向它的第一个孩子节点(没有则指向空指针),nextbrother指针指向它的兄弟节点(没有则指向空指针)。

在这里插入图片描述
先找到第一个孩子,(例如B是A的第一个孩子)然后此时的第一个孩子再找他的兄弟,直到为空指针结束。

TreeNode* Anode假设为节点A
找A的第一个孩子B节点:TreeNode* child = Anode->firstchild;
while(child)直到空结束
{
…………
child = child->nextbrother;
}

在这里插入图片描述

4.树在实际中的运用

我们平时使用电脑文件系统的目录就是树结构,打开此电脑,有D盘、C盘等,每层又有多个文件。
在这里插入图片描述

二、二叉树概念及结构

1.概念

一棵二叉树是结点的一个有限集合
1.最多两个节点,也可以是1个或者0个
2. 由一个根节点加上两棵别称为左子树右子树的二叉树组成

在这里插入图片描述
从上图可以看出,二叉树不存在度大于2的结点,并且二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序

注意:对于任意的二叉树都是由以下几种情况复合而成的:
在这里插入图片描述
现实中的二叉树:
在这里插入图片描述

2.特殊的二叉树

(1)满二叉树

概念:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为h,且结点总数是2^h - 1 ,则它就是满二叉树。
在这里插入图片描述

每一层都是满的:2^(i-1)个节点(i是某一层的位置)
F(h) = 2 ^ 0 + 2 ^ 1 + ……+2 ^ (h-2) + 2 ^ (h-1)等比数列求和

假设这个二叉树的节点是N个
N = 2 ^ h - 1 => h = log(N+1) (log以2为底)

(2)完全二叉树

概念:完全二叉树是在满二叉树的基础上变化的。假设它的高度是h,那么它的前h-1层是满的,最后一层可能是满的,也可能是不满的,并且它的最后一层必须从左到右是连续的。满二叉树是特殊的完全二叉树。

在这里插入图片描述
因此,完全二叉树的节点总个数是有范围的。当它为满二叉树时,也就是节点总个数最多的情况下,它的节点总个数是2^h - 1;当最后一层只有一个节点,也就是节点总个数最少的情况下,它的节点总个数是2 ^ (h-1),所以节点范围:【2 ^ (h-1) , 2 ^ h - 1】。

3.二叉树的性质

1.如果根节点的层数为1,则一个非空二叉树的第h层最多有2^(h-1)个节点;
2.如果根节点的层数为1,则深度为h的二叉树最多一共有2^h-1个节点;
3.对任何一棵二叉树, 如果度为0其叶结点个数为n0, 度为2的分支结点个数为n2,则满足公式n0=n2+1;
4.若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log(n+1)

4.二叉树的存储

二叉树的存储方式分为顺序存储链式存储

(1)顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

使用数组存储时有一个规律:
在这里插入图片描述

可在任意位置通过下标找到父亲或者孩子
注意:前提是满二叉树或者完全二叉树才能用这个规律

在这里插入图片描述
所以,对于非完全二叉树,使用链式结构存储更适合。

总结:满二叉树或者完全二叉树适合用数组存储

(2)链式存储

二叉树的链式结构是用链表来表示一个二叉树,分为三个作用域(数据域和左右指针域),左右指针来存储左右孩子的地址。
在这里插入图片描述

三、二叉树的顺序结构及实现

1.二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

2.堆的概念及结构

堆:非线性结构的二叉树,更准确的说是完全二叉树。适合用数组存储。

堆分为两种:根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
小堆:树中任意一个父亲都<=孩子(比的是节点的值)
大堆:树中任意一个父亲都>=孩子

在这里插入图片描述

3.堆的实现

(1)初始化堆

初始化堆有两种方式。
第一种:把结构体指向的数组里面置空(没有元素),有效个数和容量置为0

void HPInit(HP* php)
{assert(php);php->a = NULL;php->capacity = 0;php->size = 0;
}

第二种:接收外来数组的大小n,动态开辟大小为n的空间,把数据拷贝到结构体指向的数组里

void HPInitArray(HP* php, HPDataType* a, int n)
{assert(php);assert(a);php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (php->a == NULL){perror("malloc fail");exit(-1);}php->size = n;php->capacity = n;memcpy(php->a, a, sizeof(HPDataType) * n);for (int i = 1; i < n; i++){AdjustUp(php->a, i);}

AdjustUp 是向上调整算法,下面会分析。

(2)销毁堆

堆的销毁不必多说,与顺序表的销毁相同

void HPDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = php->size = 0;
}

(3)堆的插入

堆的插入采用顺序表的尾插(一个一个插入),因为数组的尾插效率高(还有尾删,后面也会用到)。插入数据要考虑扩容,因为前面初始化的时候空间大小为0。这里可以设置一个变量newcapacity,用条件操作符,如果容量为0,就给初始容量4,否则就是原来容量的两倍。扩容完插入新的数据,元素个数++。

void HPPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* ptr = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);if (ptr == NULL){perror("realloc fail");exit(-1);}php->a = ptr;php->capacity = newcapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);//向上调整
}

难道把外来的数据一个一个插入就行了吗?其实不然,我们插入数据后要使结构体指向的数组变成一个堆,这里要介绍一个算法。

向上调整算法

从最后一个叶子节点开始向上调整。以建小堆为例,插入一个新的数据为最后一个叶子节点,小堆的特点是父节点<=孩子节点,所以要先找到父节点。

公式:parent = (child - 1) / 2

我们知道,堆的实现虽然是二叉树,但它的存储方式本质是数组,空间是连续的,所以可以通过孩子节点下标找到父节点。这时孩子节点可以与父节点比较,如果父节点大于孩子节点,就交换,然后孩子节点向上到达父节点的位置,原来父节点到达它的父节点的位置。注意控制孩子节点的范围,孩子节点不可能是堆顶元素,否则就会越界,所以child>0。如果父子节点大小关系满足小堆特点,就跳出循环,此时该数组就是小堆了。
在这里插入图片描述

向上调整的前提:该数组原来是小堆或者大堆

既然如此,那插入一个数据前怎么知道这个数组已经是小堆或者大堆了呢?其实这里指针所指向的数组原来什么也没有,是一个一个插入数据的,而我每插入一个数据就调整一次,相当于说我要插入下一个数据时前面的数据已经调整为小堆了,那么插入下一个数据我就调整这个数据和它的父节点的关系就行了。

void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[parent] > a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

(4)堆的删除

删除堆的一个元素采用顺序表的尾删,但是这里要注意的是仅仅删除堆的最后一个元素是没有意义的。假如是小堆,那么它有个特点,它的堆顶元素一定是所以元素里最小的,所以应该删除堆顶元素。可是数组的头删要挪动数据,比较麻烦,所以这里把堆顶元素与最后一个元素交换,然后再尾删,就可以把最小的元素删除了。但是删除之后,堆顶的元素大小就不确定了,此时不一定是小堆。这里要介绍另一个算法。

void HPPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}

向下调整算法

从堆顶元素开始往下调整,因为前面的铺垫只改变了堆顶元素,所以堆顶元素是父节点,可以通过以下公式找到它的孩子节点:

左孩子:child = parent * 2 + 1
右孩子:child = parent * 2 + 2

那么问题来了,是选择左孩子还是选择右孩子?这里我们可以默认选择左孩子,然后加一个判断,如果左孩子的值大于右孩子的值,左孩子下标加1,变成右孩子。因为向下调整变成小堆父节点所要交换的节点是两个孩子节点中的较小的。接着比较父节点与孩子节点的大小关系,如果父节点大于孩子节点,就交换(父节点与孩子交换就是与较小的孩子节点交换),然后父节点到达孩子节点的位置,孩子节点到到达它的左孩子节点的位置。不满足条件说明此时是小堆就跳出循环。

注意:这里要控制一个细节,孩子节点下标的范围小于元素个数。同时,如果一个父节点只有一个孩子节点的情况下(只有左孩子),那么这个左孩子的下标加1就越界了。
child + 1 < n ——这个条件成立说明只有左孩子没有右孩子

在这里插入图片描述

void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){                         child++;}if (a[parent] > a[child]){Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}

(5)获取堆顶元素/堆的元素个数/判空

//获取堆顶元素
HPDataType HPTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}
//获取堆的元素个数
int HPSize(HP* php)
{assert(php);return php->size;
}
//堆的判空
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}

4.堆排序

我们要把堆里面的数据排序(以升序为例),有两种方式,一个是打印出来有序,另一个是原地使数组有序。

(1)堆排序版本1

把数据一个一个插入堆中,只要数组不为空就取堆顶元素,然后删除堆顶元素,直到把所有元素打印出来为升序。

void HeapSort(int* a, int n)
{HP hp;HPInit(&hp);int i = 0;for (i = 0; i < n; i++){HPPush(&hp, a[i]);}HPPrint(&hp);while (!HPEmpty(&hp)){printf("%d ", HPTop(&hp));HPPop(&hp);}HPDestroy(&hp);
}

这个方法有缺点:
1.频繁的扩容造成空间复杂度的消耗
2.先要有一个堆的数据结构

(2)堆排序版本2——对数组原地排序

假如一个数组进行堆排序后还要使用,此时只对它打印出来排序就不适合了。所以要对数组原地进行堆排序,使数组的内容有序。

例如:
原来的数组:65,100,70,32,50,60
排序后的数组:32,50,60,65,70,100

在排序前我们要建堆,那么是建小堆还是建大堆?
先来建小堆:
以升序为例,我们在前面的堆排序是打印出来的,用向上调整法是建小堆。这里也用建小堆的方法,小堆建成后堆顶的元素是最小的,所以这个元素排在数组的第一个;然后是次小的,依次从前往后排,直到把数组里的元素从前往后全部排完就是升序了。

但是先以建小堆的方法有缺陷:
分析:堆顶的元素是堆当中最小的依次对应数组从前往后排,每次排完一个数据,要从后面的数据中找出次小的,但问题在于排完的那个数据的后一个数据不一定是次小的,所以后面的数据要重新建堆,重新建的堆的堆顶元素就是次小的。而每次重新建堆使时间复杂度变大,造成效率很低。

向上调整法一个数据遍历时时间复杂度为:logN
有N个数据所以建堆的时间复杂度为:N * logN
每排完一个数据都要建堆一次时间复杂度为:N * (N * logN)
相当于建N次小堆

所以这里应该是建大堆,大堆的堆顶是堆中最大的元素,可是它排在数组的第一个位置,怎么让它到数组的最后一个位置呢?

在这步可以采用堆的删除的思路。把堆顶的元素与最后一个元素交换,这时最大的元素就到了它应该在的位置,然后接下来的步骤很关键,不是真的把最后一个元素删除,那最大的元素排在最后不就没了吗?所以这里我们可以定义一个变量end,它指向的是数组的最后一个元素,完成交换后向下调整,调整的范围是0(第一个元素)到end的有效个数,注意,这时end指向的那个元素不在调整的范围内。然后end减1,控制end>0循环,因为最后只有一个元素就不需要调整了,它是最小的。
在这里插入图片描述

//建堆for (int i = 1; i < n; i++){AdjustUp(a, i);}//调整int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}

(3)优化堆排序版本2及时间复杂度的比较

相较于向上调整法建堆,使用向下调整法建堆的时间复杂度更优秀。前面我们都是使用向上调整法建堆,那么用向下调整法是怎么建堆的呢?

我们知道,用向上调整法建堆是从最后一个节点开始,找它的父节点然后进行比较完成建堆。

调整的执行总次数:每层数据个数 * 向上或向下移动的层数

向上调整法时间复杂度的计算
如图:
在这里插入图片描述
向下调整法建堆:
前面使用向下调整法是从堆顶开始向下调整,依次比较。这里不是从堆顶开始,而是从下面开始调整。找到最后一个父节点,(只要是父节点一定有孩子节点),然后进行比较、调整;接着再到前一个父节点进行前面的操作,最终建成堆。

找最后一个父节点:
最后一个孩子节点:child = n-1
有孩子找父亲:(child-1)/ 2
所以最后一个父节点:(n - 1 - 1) / 2

向下调整法时间复杂度的计算
如图:
在这里插入图片描述
通过对比:向上调整与向下调整的总次数相比多了2 ^ (h-1),也就是说多了一层(最后一层)的计算。最后一层占比大约整体的一半,所以向下调整法建堆更好。

void HeapSort(int* a, int n)
{//建堆//选择向上调整——O(N*log(N))/*for (int i = 1; i < n; i++){AdjustUp(a, i);}*///选择向下调整——O(N)int i = 0;for ( i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}//调整int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}

(4)TopK问题

假设有N个数据,找出最大的前K个

1.读取文件的前K个数据,在内存数组中建一个小堆
2.再依次读取后面的数据,跟堆顶元素比较,只要比堆顶元素大就替换堆顶元素进堆,然后向下调整
3.读完所有数据,堆里面的数据就是最大的前K个

这个方法很巧妙,建小堆这样堆顶的元素是堆中最小的,只要比它大就交换进堆。大的数据进堆往下沉在堆底,只有堆里面是最大的前K个才没有交换。当堆里面是最大的前K个时,因为是小堆,堆顶是堆中元素最小的,但是它又比不在堆里面的元素都要大。就算刚开始堆里面已经有K-1个元素在所有元素最大的前K个元素的范围内,进行向下调整保持小堆那么必然有一个元素在堆顶且这个元素小于后面的某个数据,读取到后面的那个元素就交换然后向下调整,最大的前K个就找到了。
如果是建大堆,大堆的堆顶元素是堆中最大的,比它大才能进堆,万一刚开始的堆建好后正好堆顶的元素是所有元素中最大的,那岂不是把所有元素都挡住了。

void PrintTopK(const char* file, int k)
{FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen fail");return;}int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("minheap fail");return;}int i = 0;for (i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);}for (i = (k - 2) / 2; i >= 0; i--){AdjustDown(minheap, k, i);}int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (minheap[0] < x){minheap[0] = x;AdjustDown(minheap, k, 0);}}for (i = 0; i < k; i++){printf("%d ", minheap[i]);}free(minheap);fclose(fout);
}
void CreateNDate()
{// 造数据int n = 10000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = rand() % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}
int main()
{CreateNDate();PrintTopK("data.txt", 5);return 0;
}

在这里插入图片描述
感谢观看~

相关文章:

【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

目录 前言&#xff1a;一、树的概念及结构1.树的概念2.树的相关概念3.树的存储4.树在实际中的运用 二、二叉树概念及结构1.概念2.特殊的二叉树&#xff08;1&#xff09;满二叉树&#xff08;2&#xff09;完全二叉树 3.二叉树的性质4.二叉树的存储(1)顺序存储(2)链式存储 三、…...

rust学习-string

介绍 A UTF-8–encoded, growable string(可增长字符串). 拥有string内容的所有权 A String is made up of three components: a pointer to some bytes, a length, and a capacity. The length is the number of bytes currently stored in the buffer pub fn as_bytes(&…...

No167.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…...

【python】pycharm导入anaconda环境

参考 Pycharm导入anaconda环境的教程图解 - 知乎 (zhihu.com)...

【数据结构】逻辑结构与物理结构

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f333;逻辑结构 1.集合结构 2.线性结构 3.树形结构 4.图形结构或网状结构 &#x1f333;物理结构 1.顺序存储结构 2.链式存储结构 结语 根据视点的不同,我…...

HTML5高级部分

目录 一、拖拽API1.1 拖拽元素1.2 监听事件1.3 dataTransfer传递数据 二、媒体API2.1 常用监听事件2.2 常用API 三、画布API3.1 canvas 标签3.2 创建canvas对象3.3 常用API 四、地理API4.1 方法 一、拖拽API 1.1 拖拽元素 页面中设置了draggable"true"的元素可以进…...

浏览器输入 URL 并回车发生了什么

本文节选自我的博客&#xff1a;浏览器输入 URL 并回车发生了什么 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是MilesChen&#xff0c;偏前端的全栈开发者。&#x1f4dd; CSDN主页&#xff1a;爱吃糖的猫&#x1f525;&#x1f4e3; 我的博客&#xff1a;爱吃糖…...

asp.net core mvc 文件上传,下载,预览

//文件上传用到了IformFile接口 1.1文件上传视图 <form action"/stu/upload" method"post" enctype"multipart/form-data"><input type"file" name"img" /><input type"submit" value"上传&…...

Axios有哪些常用的方法?

Axios是一个常用的JavaScript库&#xff0c;用于进行HTTP请求。它提供了一组简洁而强大的方法来发送各种类型的请求&#xff0c;并处理响应数据。以下是Axios中一些常用的方法及其格式&#xff1a; GET请求&#xff1a; axios.get(url[, config]).then(response > {// 请求…...

PL/SQL+cpolar公网访问内网Oracle数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…...

stable diffusion和gpt4-free快速运行

这是一个快速搭建环境并运行的教程 stable diffusion快速运行gpt快速运行 包含已经搭建好的环境和指令&#xff0c;代码等运行所需。安装好系统必备anaconda、conda即可运行。 stable diffusion快速运行 github: AUTOMATIC1111/稳定扩散网络UI&#xff1a;稳定扩散网页用户界…...

分享三个国内可用的免费GPT-AI网站

AIchatOS国内的不需要梯子 AItianhu同上 国内百度的文心一言一样非常优秀...

使用SDKMAN在Linux系统上安装JDK

本文使用的Linux发行版为Rocky Linux 9.2&#xff0c;可以当做CentOS的平替产品。 SDKMAN是一个sdk包管理工具&#xff0c;通过自带的命令可以快速切换软件环境&#xff0c; 官网地址&#xff1a;https://sdkman.io/。 1、安装sdkman&#xff1a; # curl -s "https://ge…...

MySQL(8) 优化、MySQL8、常用命令

一、MySQL优化 从上图可以看出SQL及索引的优化效果是最好的&#xff0c;而且成本最低&#xff0c;所以工作中我们要在这块花更多时间。 服务端参数配置&#xff1b; max_connections3000 连接的创建和销毁都需要系统资源&#xff0c;比如内存、文件句柄&#xff0c;业务说的支持…...

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(三)

思维导图 全选案例 大按钮控制小按钮 小按钮控制大按钮 css伪类选择器checked <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><…...

嵌入式汇编大合集

嵌入式汇编 内联汇编的基本格式: asm volatile( /* volatile : 可选,禁止编译器对汇编代码进行优化 */"汇编指令" /* 汇编指令间使用\n分隔 */:"=限制符"(输出参数):"限制符"(输入参数):保留列表 )共四个部分:汇编语句,输出部分,输入部分…...

C#WPF框架MvvMLight应用实例

本文实例演示C#WPF框架MvvMLight应用实例。 目录 一、MVVM概述 二、MVVMLight概述 三、使用MVMLight框架 一、MVVM概述 MVVM概述MVVM是Model-View-ViewModel的简写,主要目的是为了解耦视图(View)和模型(Model)。...

【JVM】双亲委派模型

双亲委派模型 1. 什么是双亲委派模型2. 双亲委派模型的优点 1. 什么是双亲委派模型 提到 类加载 机制&#xff0c;不得不提的一个概念就是“双亲委派模型”。 双亲委派模型指的就是 JVM 中的类加载器如何根据类的全限定名找到 .class 文件的过程 类加载器: JVM 里面专门提供…...

多叉树+图实现简单业务流程

文章目录 场景整体架构流程业务界面技术细节小结 场景 这次遇到一个需求,大致就是任务组织成方案,方案组织成预案,预案可裁剪调整.预案关联事件等级配置,告警触发预案产生事件.然后任务执行是有先后的,也就是有流程概念. 整体架构流程 方案管理、预案管理构成任务流程的基础条…...

Word | 简单可操作的快捷公式编号、右对齐和引用方法

1. 问题描述 在理工科论文的写作中&#xff0c;涉及到大量的公式输入&#xff0c;我们希望能够按照章节为公式进行编号&#xff0c;并且实现公式居中&#xff0c;编号右对齐的效果。网上有各种各样的方法来实现&#xff0c;操作繁琐和简单的混在一起&#xff0c;让没有接触过公…...

leetCode 123.买卖股票的最佳时机 III 动态规划 + 状态压缩

123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 给定一个数组&#xff0c;它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意&#xff1a;你不能同时参与多笔交易&#xff0…...

JavaScript计算两个时间相差多少个小时的封装函数

js中计算两个时间相差小时数 在JavaScript中&#xff0c;你可以使用Date对象来处理日期和时间。下面是一个函数&#xff0c;它接受两个时间字符串作为参数&#xff0c;并返回两者之间的时间差&#xff08;以小时为单位&#xff09;&#xff1a; function calculateHours(time…...

Qt 画自定义饼图统计的例子

先给出结果图&#xff0c;这个例子是将各种事件分类然后统计的其比例&#xff0c;然后画饼图显示出来 这个是我仿照官方给的例子&#xff0c;让后自己理解后&#xff0c;修改的&#xff0c;要生成饼图&#xff0c;需要QT的 charts 支持&#xff0c;安装QT 没有选择这个的&#…...

【数据结构】链表与LinkedList

作者主页&#xff1a;paper jie 的博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精…...

Flink RoaringBitmap去重

1、RoaringBitmap的依赖 <!-- 去重大哥--> <dependency><groupId>org.roaringbitmap</groupId><artifactId>RoaringBitmap</artifactId><version>0.9.21</version> </dependency> 2、Demo去重 package com.gwm.driver…...

Elasticsearch—(MacOs)

1⃣️环境准备 准备 Java 环境&#xff1a;终端输入 java -version 命令来确认版本是否符合 Elasticsearch 要求下载并解压 Elasticsearch&#xff1a;前往&#xff08;https://www.elastic.co/downloads/elasticsearch&#xff09;选择适合你的 Mac 系统的 Elasticsearch 版本…...

插入排序与希尔排序

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 前言&#xff1a; 这两个排序在思路上有些相似&#xff0c;所以有人觉得插入排序和希尔排序差别不大&#xff0c;事实上&#xff0c;他们之间的差别不小&#xff0c;插入排序只是希尔排序的最后一步。 目录 前言&#xff1a;…...

C# OpenCvSharp 基于直线检测的文本图像倾斜校正

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenCvSharp_基于直线检测的文…...

“智慧时代的引领者:探索人工智能的无限可能性“

目录 一.背景 二.应用 2.1金融领域 2.2医疗领域 2.3教育领域 三.发展 四.总结: 一.背景 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;&#xff0c;是指通过计算机程序模拟人类智能的一种技术。它是计算机科学、工程学、语言学、哲学等多…...

PMSM——转子位置估算基于QPLL

文章目录 前言仿真模型观测器速度观测位置观测转矩波形电流波形 前言 今后是电机控制方向的研究生的啦&#xff0c;期待有同行互相交流。 仿真模型 观测器 速度观测 位置观测 转矩波形 电流波形...

教育机构网站建设方案书/今日头条热搜

phpcms2008远程代码执行漏洞 描述&#xff1a; 近日&#xff0c;互联网爆出PHPCMS2008代码注入漏洞&#xff08;CVE-2018-19127&#xff09;。攻击者利用该漏洞&#xff0c;可在未授权的情况下实现对网站文件的写入。该漏洞危害程度为高危(High)。目前&#xff0c;漏洞利用原理…...

创造与魔法官方网站-做自己喜欢的事/北京网

目录 文档用途 详细信息 文档用途 本文介绍向PostgreSQL中加载数据的几种方法。 详细信息 1.服务器端 1.1 COPY/COPY FROM PROGRAM COPY用于在PostgreSQL表和标准文件系统文件之间移动数据。COPY TO把一个表的内容复制到一个文件&#xff0c;而COPY FROM则从一个文件复…...

成都网站建设制作服务/谷歌seo营销

用户介绍安装创建达梦企业版数据库后&#xff0c;系统会默认创建四类数据库账号&#xff0c;分别是&#xff1a;SYS:达梦数据库内置管理用户&#xff0c;不能登录&#xff0c;数据库使用的大部分的数据字典和动态性能视图。SYSDBA:数据库管理员&#xff0c;主要管理维护数据库。…...

wordpress评论区美化/摘抄一则新闻

Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 GPU的DVFS不是在dts而是在驱动中直接控制并且默认打开的. DVFS控制核心函数是mali_dvfs_event_proc(), 放在队列中处理,而队列是否开启受变量dvfs->is_enabled的控制. int kbase_platform_dvfs_event(struct kbase…...

网站客服怎么做的/今日世界杯比分预测最新

前言 很多次小伙伴问到学习方法&#xff0c;我也很想写这样的一篇文章来跟大家讨论下关于学习方法这件事情。 其实学习方法这个事情&#xff0c;我没啥发言权&#xff0c;因为我自己本身都是没啥方法可言的&#xff0c;就瞎折腾那种&#xff0c;但是大家想看这样的一篇文章&a…...

昆明官方网站建设/福州百度推广电话

ExecutorService是java中的一个异步执行的框架&#xff0c;通过使用ExecutorService可以方便的创建多线程执行环境。 本文将会详细的讲解ExecutorService的具体使用。 创建ExecutorService 通常来说有两种方法来创建ExecutorService。 第一种方式是使用Executors中的工厂类…...