C语言二叉树与堆的实现(一)

目录

二叉树

二叉树的分类(目前只谈两种)

满二叉树

完全二叉树

二叉树的性质(其余的可以自己总结)

选择练习

二叉树的存储结构

顺序存储方式

链式存储方式

一种完全二叉树:堆

堆的概念

堆的性质

建堆的时间复杂度

堆的空间复杂度:

小堆的实现

必要补充

堆的初始化

堆的销毁

向上调整算法

堆的插入

向下调整算法

堆的删除

获取堆顶元素

获取堆中元素个数

堆的判空

最终代码

思考:堆的意义是什么?

1、堆排序

2、top k问题


二叉树

定义:二叉树是一种特殊的树状数据结构,它由多个节点组成,每个节点最多有两个子节点(左结点和右结点)这些子节点可以为空,任意的二叉树均由以下几种情况复合而成的:

因此我们可以得到以下结论: 

  1. 二叉树的度小于等于2,二叉树的度不一定等于2,但是度为2的树就是二叉树
  2. 二叉树是有序树,子树有左右之分,不能颠倒

二叉树的分类(目前只谈两种)

满二叉树

定义:每一层的结点数都达到最大值的二叉树称为满二叉树

若二叉树层数为k,且结点总数为(2^k)-1 ,则为满二叉树

完全二叉树

定义:二叉树的最后一层是不满情况的二叉树称为完全二叉树,满二叉树是一种特殊的完全二叉树

若二叉树层数为k,且树中结点总数为[2^(k-1) ,(2^k) - 1],则为完全二叉树

二叉树的性质(其余的可以自己总结)

1. 若规定根节点的层数为1,则棵非空二叉树的第k层上最多有2^(k-1)个结点

2. 若规定根节点的层数为1,则深度为k的二叉树的最大结点总数为(2^k) - 1

3. 若规定根节点的层数为1,则深度为k的二叉树的最小结点总数为2^(k - 1)

4. 完全二叉树中,度为2的结点总数 = 叶子结点总数 - 1

5. 任意一棵二叉树, 叶子节点总数 = 分支节点总数 + 1(根节点)

6. 任意一棵二叉树,结点总数 = 叶子结点总数 + 分支节点总数(度为2的结点)

7. 对于一颗完全二叉树,2 * 叶子结点总数 + 度为1的结点总数 - 1 = 结点总数

8. 完全二叉树 总结点数为偶数 则度为1的结点数为1 总节点数为奇数 则度为1的结点数为0

9. 若规定根节点的层数为1,则具有n个结点的二叉树的深度,h = (log以2为底n+1的对数)

10. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:

  1. 若i=0,i为根节点下标,无双亲节点
  2. 若i>0,i下标的结点的双亲结点的下标为(i-1)/ 2
  3. 若i>0,i下标的结点的左孩子结点的下标为2 * i +1
  4. 若i>0,i下标的结点的左孩子结点的下标为2 * i +2
  5. 对于节点 i,若 2 * i + 1 < n,则节点 i 有左孩子。若 2 * i + 1 >= n,则节点 i 没有左孩子

  6. 对于节点 i,若 2 * i + 2 < n,则节点 i 有右孩子。若 2 * i + 2 >= n,则节点 i 没有左孩子

选择练习

1、某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为(B)

A 不存在这样的二叉树

B 200

C 198

D 199

结点总数 = 叶子结点总数 + 分支结点总数(度为2的结点)399    =       ?    +     199 ? =  200

2、在具有 2n 个结点的完全二叉树中,叶子结点个数为(A )

A n

B n+1

C n-1

D n/2

//文字描述
完全二叉树中,度为2的结点总数 = 叶子结点总数 - 1又因为,叶子结点总数 + 度为1的结点总数 + 度为2的结点总数= 2n则可得:2*叶子结点总数 + 度为1的结点总数 - 1 = 2n完全二叉树中,度为1的结点总数只能为0或1,由于2n为偶数,故度为1的结点总数 = 1因此,叶子结点总数 = n//简化描述
完全二叉树中,有n2 = n0 - 1再根据题设条件,得n0 + n1 + n2 = 2n则可得:2n0 + n1 - 1 = 2n完全二叉树中,n1只能为0或1,由于2n为偶数,故n1 = 1因此,n0 = n

3、一棵完全二叉树的节点数位为531个,那么这棵树的高度为(B )

A 11

B 10

C 8

D 12

若二叉树层数为k,且树中结点总数为[2^(k-1) ,(2^k) - 1],则为完全二叉树A:[2^(11-1) ,(2^11) - 1] = [1024,2047]   1024 > 531B:[2^(10-1) ,(2^10) - 1] = [512,1023]    512  < 531 < 1023C:[2^(8-1) ,(2^8) - 1] = [128,511]       511  < 531D:[2^(12-1) ,(2^12) - 1] = [2048,4095]   2048 > 531

4、一个具有767个节点的完全二叉树,其叶子节点个数为(B)

A 383

B 384

C 385

D 386

完全二叉树 总结点数为偶数 则度为1的结点数为1 总节点数为奇数 则度为1的结点数为02 * 叶子结点总数 + 度为1的结点总数 - 1 = 结点总数767为奇数,故度为1的结点总数为0,故:2 * 叶子结点总数 - 1 = 结点总数2 *      ?     - 1 =    767? = 384

二叉树的存储结构

顺序存储方式

        顺序存储方式即使用组为底层逻辑进行存储一般用于实现完全二叉树,因为对不完全二叉树使用顺序存储会存在空间浪费:

二叉树顺序存储在物理逻辑上是一个数组,在虚拟逻辑上是一颗二叉树

链式存储方式

        链式存储方式即使用链表为底层逻辑进行存储,同时链表还可以表示二叉树中各元素间的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。链式结构又分为二叉链表和三叉链表,前我们学习的一般都是二叉链,在学习至红黑树时会用到三叉链......

一种完全二叉树:堆

堆的概念

概念:如果有一个关键码的集合K = { k0,k1,k2,…},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,若分别满足以下两种情况,则称该堆为小/大堆: 

根节点为最大值的堆叫做最大堆或大根堆,根节点为最小值的堆叫做最小堆或小根堆

堆的性质

  1. 堆是一个完全二叉树;
  2. 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值

建堆的时间复杂度

1、我们将一个高度为h的满二叉树转变成一个小堆,该小堆的结点总数为log(N+1)

一般来说对数的底我们将其忽略,所以这里原本是log(以2为底N+1的对数)

第一层的结点个数为2^0、第二层的结点个数为2^1......第h层的结点个数为2^(h-1)

结点总数为:2^0 + 2^1 +......+2^(h-1) = 2^h - 1 

(等比数列求和公式:Sn=a1 (1-q^n)/ (1-q))
假设结点总数为N,则N与h的关系是N = 2^h - 1 == h = log(N+1)

即一个高h的满二叉树的结点个数为log(N+1)

2、进行堆的调整,得到最终的时间复杂度为O(N*logN)

第一层元素向上调整0次,第二层元素向上调整1次......第h层元素向上调整h-1次

同时每一层元素中的结点个数又分别为2^0、2^1......2^(h-1)

总调整次数T为:T = 2^0×0 + 2^1×1 +......2^(h-1)×(h-1)     ① 

利用错位相减法列出第二个式子:2*T = 2^1×0 + 2^2×1 +......2^h×(h-1)     ②

由① - ②得: T = 2 + 2^h * (h-2)     ③

将h = log(以2为底N+1的对数)代入③得④:

T = (N+1)log(以2为底N+1的对数)- 2 * N     ④

将④利用大O渐进表示法转换为 T(N) = O(N*log以2为底N的对数)

结论:建堆的空间复杂度为O(N*logN)

堆的空间复杂度:

        小堆的空间复杂度为O(n),其中n是小堆中元素的个数。在建堆的过程中,需要额外的空间来存储数组中的元素。

结论:建堆的时间复杂度为O(N)

小堆的实现

我们利用顺序表(顺序存储方式)实现堆

必要补充

堆的任何一个父结点的编号parent与其左孩子结点的编号leftchild满足如下关系式:

理解这里是我们后续在编写向上调整算法与向下调整算法时的基础 

堆的初始化

//初始化堆
void HeapInit(HP* php)
{//检查堆的有效性assetr(php);php->a = NULL;php->capacity = 0;php->size = 0;}

堆的销毁

free可以用来检查是否为空,若指针为空,则free不执行任何操作,指针不为空就释放内存空间

//堆的销毁
void HeapDestroy(HP* php)
{assert(php);//释放a指针的内存空间并将a指针置为空free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}

向上调整算法

//向上调整
void AdjustUP(HPDataType* a,int child)
{int parent = (child - 1) / 2;//当孩子等于0的时候它已经位于树顶了没有父亲了就应该结束循环while(child > 0){if (a[child] < a[parent]){//如果儿子小于父亲就交换父子位置,同时将此时Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}//由于是写一个小堆,所以当孩子大于等于父亲时不需要交换,直接退出循环即可else{break;}}
}

堆的插入

//堆的插入
void HeapPush(HP* php, HPDataType x)
{assert(php);//扩容if (php->size == php->capacity){size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc error");return -1;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;//向上调整,从顺序表最后一个元素开始调整,该元素的下标为size-1AdjustUP(php->a, php->size - 1);}

每插入一个新的元素都需要进行一次向上调整的判断 

向下调整算法

//向下调整算法
void AdjustDown(HPDataType* a, int size, int parent)
{//根据之前的推论,左孩子的下标值为父亲下标值的两倍+1,左孩子的下标值为父亲下标值的两倍+2int child = parent * 2 + 1;//循环结束的条件是走到叶子结点while (child < size){//假设左孩子小,若假设失败则更新child,转换为有孩子小,同时保证child的下笔爱哦if (child + 1< size && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){//如果此时满足孩子小于父亲则交换父子位置,同时令父亲的下标变为此时的儿子所在下标,儿子所在下标变为自己的儿子所在的下标(向下递归)Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}//如果父亲小于等于左孩子就证明删除后形成的新堆是一个小堆,不再需要向下调整算法,循环结束else{break;}}
}

堆的删除

//堆的删除
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);//这里并不采用惯用的顺序表头删的办法(向前覆盖),因为那样会引起堆的顺序被完全打乱的问题,我们在这里选择交换堆顶元素与堆尾元素然后再进行一次顺序表的尾删(直接size--即可)Swap(&php->a[0], &php->a[php->size - 1]);php->size--;//在交换完并尾删完后,可能此时的堆并不能满足小堆的要求(堆顶元素比它的两个孩子都大),所以我们必须再次对堆进行调整,经过向下调整算法将堆恢复至小堆(由于是堆顶元素开始调整所以还需传入堆顶元素对应的下标0)AdjustDown(php->a, php->size, 0);
}

 每删除一个元素都需要进行一次向下调整的判断  

获取堆顶元素

//获取堆顶元素
HPDataType HeapTop(HP* php)
{assert(php);//获取堆顶元素则堆里应该有元素,故首先要保证size不小于等于零assert(php->size > 0);//确定堆中有元素后返回a[0]即可return php->a[0];
}

获取堆中元素个数

//获取堆中元素个数
size_t HeapSize(HP* php)
{assert(php);//判断size大于零后返回size大小即可return php->size;
}

堆的判空

//堆的判空
bool HeapEmpty(HP* php)
{assert(php);//返回对php->size == 0的判断结果return php->size == 0;
}

最终代码

Heap.h文件

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>#pragma once
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;  //指向存储元素的指针int capacity;	//当前顺序表容量int size;		//当前顺序表的长度
}HP;//初始化堆
void HeapInit(HP* php);//销毁堆
void HeapDestroy(HP* php);//向上调整算法
void AdjustUP(HPDataType* a, int child);//堆的插入
void HeapPush(HP* php,HPDataType x);//向下调整算法
void AdjustDown(HPDataType* a, int child);//堆的删除(删除堆顶的数据)
void HeapPop(HP* php);//获取堆顶元素
HPDataType HeapTop(HP* php);//获取堆中元素个数
size_t HeapSize(HP* php);//堆的判空
bool HeapEmpty(HP* php);

Heap.c文件

#include "Heap.h"//初始化堆
void HeapInit(HP* php)
{//检查堆的有效性assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}//堆的销毁
void HeapDestroy(HP* php)
{assert(php);//释放a指针的内存空间并将a指针置为空free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}//交换父子位置
void Swap(HPDataType* p1,HPDataType* p2)
{HPDataType* tmp = *p1;*p1 = *p2;*p2 = tmp;
} //向上调整,此时传递过来的是最后一个孩子的元素下标我们用child表示
void AdjustUP(HPDataType* a,int child)
{//由于我们要调整父亲与孩子的位置所以此时也需要父亲元素的下标,而0父亲元素的下标值 = (任意一个孩子的下标值-1)/ 2 int parent = (child - 1) / 2;//当孩子等于0的时位于树顶(数组首元素的位置),树顶元素没有父亲,循环结束while(child > 0){//如果孩子还未到顶且它的下标对应的元素值小于它的父亲的下标对应的元素值,就将父子位置交换,交换玩后还要将下标对应的值“向上移动”if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}//由于这是一个小堆,所以当孩子大于等于父亲时不需要交换,直接退出循环即可else{break;}}
}//堆的插入
void HeapPush(HP* php, HPDataType x)
{assert(php);//扩容if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc error");return -1;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;//向上调整,从顺序表最后一个元素开始调整,该元素的下标为size-1AdjustUP(php->a, php->size-1);
}//向下调整算法
void AdjustDown(HPDataType* a, int size, int parent)
{//根据之前的推论,左孩子的下标值为父亲下标值的两倍+1,左孩子的下标值为父亲下标值的两倍+2int child = parent * 2 + 1;//循环结束的条件是走到叶子结点while (child < size){//假设左孩子小,若假设失败则更新child,转换为有孩子小,同时保证child的下笔爱哦if (child + 1< size && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){//如果此时满足孩子小于父亲则交换父子位置,同时令父亲的下标变为此时的儿子所在下标,儿子所在下标变为自己的儿子所在的下标(向下递归)Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}//如果父亲小于等于左孩子就证明删除后形成的新堆是一个小堆,不再需要向下调整算法,循环结束else{break;}}
}//堆的删除
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);//这里并不采用惯用的顺序表头删的办法(向前覆盖),因为那样会引起堆的顺序被完全打乱的问题,我们在这里选择交换堆顶元素与堆尾元素然后再进行一次顺序表的尾删(直接size--即可)Swap(&php->a[0], &php->a[php->size - 1]);php->size--;//在交换完并尾删完后,可能此时的堆并不能满足小堆的要求(堆顶元素比它的两个孩子都大),所以我们必须再次对堆进行调整,经过向下调整算法将堆恢复至小堆(由于是堆顶元素开始调整所以还需传入堆顶元素对应的下标0)AdjustDown(php->a, php->size, 0);
}//获取堆顶元素
HPDataType HeapTop(HP* php)
{assert(php);//获取堆顶元素则堆里应该有元素,故首先要保证size不小于等于零assert(php->size > 0);//确定堆中有元素后返回a[0]即可return php->a[0];
}//获取堆中元素个数
size_t HeapSize(HP* php)
{assert(php);//判断size大于零后返回size大小即可return php->size;
}//堆的判空
bool HeapEmpty(HP* php)
{assert(php);//返回对php->size == 0的判断结果return php->size == 0;
}

test.c文件

#include "Heap.h"int main()
{int arr[] = { 4,6,2,1,5,8,2,9 };int sz = sizeof(arr) / sizeof(arr[0]);HP hp;HeapInit(&hp);for (int i = 0;i < sz; ++i){HeapPush(&hp,arr[i]);}//堆不为空就获取堆顶元素并删除while (!HeapEmpty(&hp)){printf("%d ",HeapTop(&hp));HeapPop(&hp);}printf("\n");return 0;
}

思考:堆的意义是什么?

1、堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:建堆与利用堆删除思想进行排序
  1. 建堆:升序:建大堆、降序:建小堆
  2. 利用堆删除思想来进行排序:建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序

当一棵树为满二叉树时,高度h与结点总数N的关系是:h = log (N + 1)

当一棵树为完全二叉树时,高度h与结点总数N最坏的关系是:h = log N + 1

所以如果有一百万个结点,这棵树的高度仅仅为20,十亿个结点,高度仅仅为30

结论:使用堆可以更高效的维护数据的有序性,不需要对整个数据集进行排序 

2、top k问题

问题描述:获取N个数里找最大的前K个数(N远大于K)

解决思路一:

N个数插入进大堆中,Pop K次

时间复杂度:N*logN + K*logN == O(N*logN)

但如果N为100亿(100亿个整数需要40GB左右的内存空间),K为10? 

解决思路二:

1、取前K个值,建立K个数的小堆

2、再读取后面的值,跟堆顶元素比较,若大于堆顶元素,交换二者位置然后重新堆化成小堆

时间复杂度:O(N*logK)

注意事项:必须要建立小堆,不能建立大堆,因为

模拟实现:

使用数组[7, 8, 15, 4, 20, 23, 2, 50]演示如何使用最小堆找出前3个最大的元素。

首先,我们创建一个最小堆,并将数组的前3个元素[7, 8, 15]插入堆中,初始堆的表示如下:

       7/   \8     15

接下来遍历数组,发现 4 < 7,因此我们不做任何操作

继续遍历数组,发现 20 > 7,因此将 7 替换为 20 并重新堆化成小堆

       8/   \20    15

继续遍历数组,发现 23 大于 8,因此我们将 8 替换为 23 并重新堆化成小堆

       15/    \20     23

继续遍历数组,发现 2 < 15,因此我们不做任何操作

继续遍历数组,发现 50 > 15,因此我们将 15 替换为 50 并重新堆化成小堆

       20/    \50     23

最后,数组遍历完成,得到了最终的最小堆

       20/    \50     23

此时,堆中的前3个最大元素为 `[20, 50, 23]`,它们就是原数组中的前3个最大元素

~over~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA调优

1 JAVA虚拟机 1.1 基本组成 通常来说Java平台标准版&#xff08;Java SE&#xff09;包括 Java SE开发工具包&#xff08;JDK&#xff09;和Java SE运行时环境&#xff08;JRE&#xff09;。 JRE提供了运行以Java编程语言编写的applet和应用程序所必需的库&#xff0c;Java虚…

Microsoft Expression Web - 网页布局

在本章中&#xff0c;我们将介绍网页的基本布局。在创建我们的网页布局之前&#xff0c;我们需要考虑我们的内容&#xff0c;然后设计我们希望如何呈现该内容&#xff0c;因为它是在我们的网站上可见的内容。 由我们如何呈现我们的内容&#xff0c;以便我们的观众找到我们的网…

Python教程 – 简单代码实现HTML 转Word

之前文章分享过如何使用Spire.Doc for Python库将Word文档转为HTML格式&#xff0c;反过来&#xff0c;该库也能实现HTML到Word文档的转换。通过代码进行转换&#xff0c;避免了手动复制粘贴费时间&#xff0c;并且可能会出现错误或格式混乱等问题。 Spire.Doc for Python库能…

拥抱未来:大语言模型解锁平台工程的无限可能

01 了解大型语言模型 (LLM) 大型语言模型&#xff08;LLM&#xff09;是一种人工智能&#xff08;AI&#xff09;算法&#xff0c;它使用深度学习技术和海量数据集来理解、总结、生成和预测新内容。凭借合成大量信息的能力&#xff0c;LLM 可以提高以前需要人类专家的业务流程的…

基于ASP.Net的图书管理系统的设计与实现

摘 要 图书馆管理系统是一整套高科技技术与书本管理知识结合的产物。它把传统书籍静态的服务这个缺陷完美化&#xff0c;完成多媒体数据的交互、远程网络连接、检查搜索智能化、多数据库无障碍联系、跨时空信息服务。图书管理系统用计算机程序替代了传统手工记录的工作模式&am…

Ruby和HTTParty库下载代码示例

ruby require httparty require nokogiri # 设置服务器 proxy_host "" proxy_port "" # 定义URL url "" # 创建HTTParty对象&#xff0c;并设置服务器 httparty HTTParty.new( :proxy > "#{proxy_host}:#{proxy_port}" ) …

MySQL之binlog日志

聊聊BINLOG binlog记录什么&#xff1f; MySQL server中所有的搜索引擎发生了更新&#xff08;DDL和DML&#xff09;都会产生binlog日志&#xff0c;记录的是语句的原始逻辑 为什么需要binlog&#xff1f; binlog主要有两个应用场景&#xff0c;一是数据复制&#xff0c;在…

训练自己的个性化Stable diffusion模型,LORA

一、背景 需要训练自己的LORA模型 二、分析 1、有sd-webui有训练插件功能 2、有单独的LORA训练开源web界面 两个开源训练界面 1、秋叶写的SD-Trainer https://github.com/Akegarasu/lora-scripts/ 没成功&#xff0c;主要也是cudnn和nvidia-smi中的CUDA版本不一致退出 2…

Netty Review - 探索Channel和Pipeline的内部机制

文章目录 概念Channel Pipeline实现原理分析详解 Inbound事件和Outbound事件演示Code 概念 Netty中的Channel和Pipeline是其核心概念&#xff0c;它们在构建高性能网络应用程序时起着重要作用。 Channel&#xff1a; 在Netty中&#xff0c;Channel表示一个开放的连接&#xff…

由于找不到msvcp120.dll的解决方法,msvcp120.dll修复指南

当你尝试运行某些程序或游戏时&#xff0c;可能会遇到系统弹出的错误消息&#xff0c;提示"找不到msvcp120.dll"或"msvcp120.dll丢失"。这种情况通常会妨碍程序的正常启动。为了帮助解决这一问题&#xff0c;本文将深入讨论msvcp120.dll是什么&#xff0c;…

YOLOv8优化策略:检测头结构全新创新篇 | RT-DETR检测头助力,即插即用

🚀🚀🚀本文改进:RT-DETR检测头助力YOLOv8检测,保持v8轻量级的同时提升检测精度 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1.RT-DETR介绍 论文: https://arxiv.org/pdf/2304.08069.pdf 摘要:…

再探Java集合系列—LinkedHashMap

LinkedHashMap 继承了 HashMap 所以LinkedHashMap也是一种k-v的键值对&#xff0c;并且内部是双链表的形式维护了插入的顺序 LinkedHashMap如何保证顺序插入的&#xff1f; 在HashMap中时候说到过HashMap插入无序的 LinkedHashMap使用了双向链表&#xff0c;内部的node节点包含…

CityEngine2023安装与快速入门

目录 0 引言1 安装2 CityEngine官方示例2.1 官方地址2.2 导入示例工程 3 结尾 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;CityEngine专栏&#x1f4a5; 标题&#xff1a;CityEngine2023安装与快速入门❣️ 寄语&#xff1a;书到用时方恨少&am…

P1025 [NOIP2001 提高组] 数的划分

暴搜 剪枝 枚举固定的位置 #include<bits/stdc.h> using namespace std; using ll long long; const int N 1e310; int n,k; int res; void dfs(int last,int sum,int cur){if(curk){if(sumn)res;return;}for(int ilast;isum<n;i)dfs(i,sumi,cur1); } int main() {c…

倒计时(JS计时器)

<script>function countDown() {document.body.innerHTML ;//清空页面内容var nowTimer new Date(); //现在时间的毫秒数var valueTimer new Date("2024-1-1 12:00"); //用户输入年份倒计时时间毫秒数var timer (valueTimer - nowTimer) / 1000; //倒计时秒…

有什么值得推荐的node. js练手项目吗?

前言 可以参考一下下面的nodejs相关的项目&#xff0c;希望对你的学习有所帮助&#xff0c;废话少说&#xff0c;让我们直接进入正题 1、 NodeBB Star: 13.3k 一个基于Node.js的现代化社区论坛软件&#xff0c;具有快速、可扩展、易于使用和灵活的特点。它支持多种数据库&…

解决:ValueError: the first two maketrans arguments must have equal length

解决&#xff1a;ValueError: the first two maketrans arguments must have equal length 文章目录 解决&#xff1a;ValueError: the first two maketrans arguments must have equal length背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在…

如何在Ubuntu系统上安装Git

简单介绍 Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。Git 与常用的版本控制工具CVS&#xff0c;Subversion 等不同&#xff0c;它采用了分布式版…

四、shell - 字符串

目录 1、单引号 2、双引号 3、拼接字符串 3.1 使用双引号拼接 3.2 使用单引号拼接 4、获取字符串长度 ​​​​​​​5、提取子字符串 ​​​​​​​6、查找子字符串 ​​​​​​​字符串是shell编程中最常用最有用的数据类型&#xff08;除了数字和字符串&#xff0…

Flutter应用程序加固的问题及解决方案

​&#x1f680;Flutter应用程序加固的问题及解决方案引言在移动应用开发中&#xff0c;为了保护应用程序的安全性&#xff0c;开发者需要对应用进行加固。在使用Flutter技术进行应用程序开发时&#xff0c;也需要注意应用程序的安全问题和加固方案。本文将介绍在Flutter应用程…