一种高效的动态优先队列数据结构_祁彬斌

合集下载

车联网基于信任度的RSU识别及资源分配算法

车联网基于信任度的RSU识别及资源分配算法

传 输 车 辆 W 到 接 收 车 辆 V;有 V2 V 链路 传 输 模 式 和 V2 I 链 路 传 输 模 式 2 种 传 输 方 式 • 在 V2 V 传 输 模 式 中 ,传 输 车 辆 直 接 与 接 收 车 辆 通 信 ; 在 V2 I 传 输 模 式 中 ,通 信 被 分 为 2 个 传 输 过 程 :传输车辆到中 继 RSU(V - I ) 的 通 信 ;中 继 R S U 到 接 收 车 辆 (I V )的通信. 1 . 2 通信模型
保证车辆对的传输需求.为了更好地支持车辆的移 动 性 ,R S U 按 照 一 定 的 间 隔 均 匀 部 署 在 道 路 两 旁 , 为车辆提供数据传输服务和相关的信令消息.备选 的中继R S U 节 点 集 合 为 二 H ,其 中 ,不 可 信 任 的 R S U 节 点 数 量 为 m .
图 1 车联网网络模®
Abstract :In network of vehicles, clue to some roadside units ( R S U ) are remote and cannot be main­ tained timely by network administrator, they m a y be attacked and become untrustworthy R S U s , resulting in reduction of vehicle communication quality and network energy efficiency. To effectively identify the reliability of the R S U , a trust degree-based R S U recognition and resource allocation algorithm ( T R R A ) is proposed. In this algorithm, roadside unit identification,transmission m o d e selection , and power alloca­ tion are jointly modeled as the network energy efficiency optimization problem , which is divided into three sub-optimization problems to solve respectively. Firstly, the trust degree-based R S U recognition algorithm is used to identify the trusted and untrusted roadside units;Secondly, the resource block allocation and the link selection are optimized;Finally, the sub-gradient algorithm is used to optimize the transmission power and maximize the network energy efficiency. Simulations show that the proposed algorithm has low­ er complexity, which can achieve higher recognition accuracy of roadside units, and effectively improve the overall network energy efficiency. Key words:resource allocation ;trust degree; road side unit identification;network energy efficiency

Lec3_数据结构基础1

Lec3_数据结构基础1

可以直接存取某一指定项而不需要先访问其前驱或者后 继的线性表 典型代表:数组(存储于连续空间并具有相同数据类型 的数据元素的集合,属于定长线性表)



数组元素不再有分量,则是一维数组
数组元素为数组,则是多维数组 在数组中存取任一元素只需通过其下标计算即可,时间均为 O(1),因此可理解为直接存取的结构 字符串可以按下标直接存取其中某一字符,也属于直接存取
ACM 程序设计竞赛入门
——数据结构基础(线性数据结构)
浙江工业大学 计算机科学与技术学院 韩姗姗
主要内容
1
直接存取类线性表 顺序存取类线性表
2
数据结构基本概念(1)
什么是数据结构


是计算机存储、组织数据的方式
由相互之间存在着一种或多种关系的数据元素的集合 和该集合中数据元素之间的关系组成 Data_Structure=(D,R) 数据结构直接关系到算法的选择和效率


直接存取类线性表是程序设计中使用最多的数据结构
一维数组(1)
小明与成绩
一次期末考试后,小明得到了他的期末成绩(5门课程),他想 快速知道他的平均成绩,以及最高成绩和最低成绩,你可以帮他 解决这个问题吗? INPUT 23345 OUTPUT The average is 3.4. The max is 5. The min is 2.
输入 11 15 1997 1 1 2000 输出 November 15, 1997 is a Saturday January 1, 2000 is a Saturday
7 4 1998
9 2 1752 9 14 1752 4 33 1997 00 0
July 4,Hale Waihona Puke 1998 is a Saturday

queue的数据结构

queue的数据结构

queue的数据结构在计算机科学中,队列是最常见的数据结构之一。

队列是一种线性数据结构,使用先进先出的规则,即最先进入队列的元素将最先从队列中取出来。

在队列中,元素只能在队尾添加,只能从队头移除。

下面是围绕“队列的数据结构”分讲队列的相关知识。

1. 队列的定义队列是一种抽象数据类型,用于保存按照特定顺序排列的元素。

它是一种线性的、连续的、存储有序的数据结构,具有先进先出(FIFO)的特点。

2. 队列的操作队列的主要操作包括入队和出队。

入队操作:将元素添加到队列的末尾。

出队操作:从队列的头部删除一个元素并返回其值。

除此之外,队列还有其他一些常用的操作,如:队列初始化操作:用于创建一个空的队列。

队列长度操作:用于获取队列中元素的数量。

队列查找操作:用于查找队列中是否存在某个元素。

队列清空操作:用于清空队列中存储的所有元素。

3. 队列的应用队列在计算机科学中有着广泛的应用。

它经常用于实现异步任务处理、消息队列、多线程任务调度等场景。

在异步任务处理中,任务会被添加到队列中,异步任务处理程序会从队列中依次取出任务并执行。

这样可以使任务处理更高效,减少了重复的等待时间。

在消息队列中,队列用于保存需要传递的信息。

当消息到达队列的头部,消费者程序将该消息从队列中读取并处理。

在多线程任务调度中,队列用于保存需要执行的任务。

任务分发程序会将任务添加到队列中,线程池中的线程会从队列中获取任务并执行。

4. 队列的实现队列可以使用数组或链表实现。

使用数组实现队列时,需要维护两个指针,分别指向队列的头部和尾部。

使用链表实现队列时,每个元素都包含一个指向下一个元素的指针。

无论使用数组还是链表实现队列,都需要保证队列元素的顺序,以便快速执行出队操作。

同时,还需要注意到队列的空间限制,避免在添加元素时队列溢出。

5. 队列的效率队列的效率取决于其实现方式。

在数组实现中,入队和出队操作的时间复杂度为O(1);在链表实现中,入队和出队操作的时间复杂度也是O(1)。

郝斌 数据结构

郝斌 数据结构

链表new1# include <stdio.h># include <malloc.h># include <stdlib.h>struct node{int data; //存放数据本身struct node *next;};struct node *CreateList( void ){struct node *pH = NULL;int len; //存放节点的个数int i;int temp; //存放用户暂时存入的节点的值域的值struct node *pNew = NULL;//存放临时分配好的节点本身的内容struct node *pTail = NULL;//pTail永远指向链表的尾节点//创建一个头节点pH = ( struct node * )malloc( sizeof( struct node ) );if( NULL == pH ){puts( "动态内存分配失败" );exit( -1 );}pH ->next = NULL;pTail = pH;printf( " 请输入节点的个数:len = " );scanf( " %d ", &len );for( i = 0; i < len; ++ i ){printf( " 请输入第%d 节点的值: ", i + 1 );scanf( " %d ", &temp );//临时构造一个节点pNew = ( struct node * )malloc( sizeof( struct node ) );if( NULL == pNew )//如果p所指向的节点存在{}}}int main ( void ){struct node *pH = NULL;pH = CreateList();//通过CreateList()函数创建一个链表,j将该链表的首地址发松给pH TraverseList( ph );//遍历整个单链表return 0;}跨函数使用内存malloc//2012年9月9日晚上# include <stdio.h># include <malloc.h>struct Student{int sid;int age;};struct Student *CreateStudent( void );void ShowStudent( struct Student * );int main( void ){struct Student *ps;ps = CreateStudent();ShowStudent( ps );return 0;}struct Student *CreateStudent( void ){struct Student *p = ( struct Student * )malloc( sizeof( struct Student ) );p->sid = 99;p->age = 88;return p;void ShowStudent( struct Student *pst ){printf( " %d %d\n ", pst->sid, pst->age );}快速排序# include <stdio.h>void QuickSort( int *a, int low, int high );int FindPos( int *a, int low, int high );int main( void ){int a[6] = { 2, 1, 0, 5, 4, 3 };int i;QuickSort( a, 0, 5 );//第二个参数表示第一个元素的下标,第三个参数表示最后一个元素的下标for( i = 0; i < 6; ++ i )printf( "%d ", a[i] );printf( "\n" );return 0;}void QuickSort( int *a, int low, int high ){int pos;if( low < high ){pos = FindPos( a, low, high );QuickSort( a, low, pos - 1 );QuickSort( a, pos + 1, high );}}int FindPos( int *a, int low, int high ){int val = a[low];while( low < high ){while( low < high && a[high] > val )-- high;a[low] = a[high];while( low < high && a[low] <= val )++ low;a[high] = a[low];}//终止while循环之后low和high一定相等的a[low] = val;return low;//或者return high;}连续存储数组的算法1//2012年9月10日晚上# include <stdio.h># include <malloc.h># include <stdlib.h>//使用exit( -1 );需要定义的头文件//定义了一个数据类型,没有定义变量struct Arr{int *pBase;//存储的是数组第一个元素的地址int len;//数组所能容纳的最大元素的个数int cnt;//当前所在有效的个数};void init_arr( struct Arr *pArr, int length );//分号不能省bool append_arr();//追加bool insert_arr();bool delete_arr();int get();bool is_empty( struct Arr *pArr );bool is_full();bool sort_arr();void sort_arr();void show_arr( struct Arr *pArr );void inversion_arr();int main( void ){struct Arr arr;init_arr( &arr, 6 );//地址只占四个字节show_arr( &arr );//printf( " %d\n ", arr.len );return 0;}void init_arr( struct Arr *pArr, int length ){pArr->pBase = ( int * )malloc( sizeof( int )*length );//pArr->len = 99;if( NULL == pArr->pBase ){printf( "动态内存分配失败!\n" );exit( -1 );//终止整个程序}else{pArr->len = length;pArr->cnt = 0;}return;}bool is_empty( struct Arr *pArr ){if( 0 == pArr->cnt ){return true;}elsereturn false;}void show_arr( struct Arr *pArr ){/*if( 数组为空)提示用户数组为空else输出数组有效内容*/if( is_empty( pArr ) )printf( "数组为空!\n" );}else{for( int i = 0; i < pArr->cnt; ++i )printf( " %d ", pArr->pBase[i] );printf( "\n" );}}连续存储数组的算法2//2012年9月11日早上# include <stdio.h># include <malloc.h># include <stdlib.h>//使用exit( -1 );需要定义的头文件//定义了一个数据类型,没有定义变量struct Arr{int *pBase;//存储的是数组第一个元素的地址int len;//数组所能容纳的最大元素的个数int cnt;//当前所在有效的个数};void init_arr( struct Arr *pArr, int length );//分号不能省bool append_arr( struct Arr *pArr, int val );//追加bool insert_arr( struct Arr *pArr, int pos, int val );//pos的值从1开始bool delete_arr( struct Arr *pArr, int pos, int *pVal );int get();bool is_empty( struct Arr *pArr );bool is_full( struct Arr *pArr );void sort_arr( struct Arr *pArr );void show_arr( struct Arr *pArr );void inversion_arr( struct Arr *pArr );//倒置int main( void ){struct Arr arr;int val;init_arr( &arr, 6 );//地址只占四个字节show_arr( &arr );append_arr( &arr, 1 );append_arr( &arr, 2 );append_arr( &arr, 3 );append_arr( &arr, 4 );if ( delete_arr( &arr, 1, &val ) ){printf("删除成功\n");printf("你删除的元素是:%d\n", val);}elseprintf("删除失败\n");/*append_arr( &arr, 2 );append_arr( &arr, 3 );append_arr( &arr, 4 );append_arr( &arr, 5 );insert_arr( &arr, 6, 99 );append_arr( &arr, 6 );append_arr( &arr, 7 );//这个也是追加不成功的,因为是只是分配了6个数if( append_arr( &arr, 8 ) ){printf( "追加成功\n" );}else{printf( "追加失败\n" );}*/show_arr( &arr );inversion_arr( &arr );printf("倒置之后的内容是:\n");show_arr( &arr );sort_arr( &arr ); //呜呜,这里哪里是出错了呢??printf("排列\n");show_arr( &arr );//printf( " %d\n ", arr.len );return 0;}void init_arr( struct Arr *pArr, int length ){pArr->pBase = ( int * )malloc( sizeof( int )*length );//pArr->len = 99;if( NULL == pArr->pBase ){printf( "动态内存分配失败!\n" );exit( -1 );//终止整个程序}else{pArr->len = length;pArr->cnt = 0;}return;}bool is_empty( struct Arr *pArr ){if( 0 == pArr->cnt ){return true;}elsereturn false;}bool is_full( struct Arr *pArr ){if( pArr->cnt == pArr->len )return true;elsereturn false;}void show_arr( struct Arr *pArr ){/*if( 数组为空)提示用户数组为空else输出数组有效内容*/if( is_empty( pArr ) ){printf( "数组为空!\n" );}else{for( int i = 0; i < pArr->cnt; ++i )printf( " %d ", pArr->pBase[i] );printf( "\n" );}}bool append_arr( struct Arr *pArr, int val ){//满时返回falseif( is_full( pArr ) )return false;//不满时追加elsepArr->pBase[pArr->cnt] = val;( pArr->cnt ) ++;return true;}bool insert_arr( struct Arr *pArr, int pos, int val ){int i;//第一中情况,数组里面全满时if( is_full( pArr ) )return false;//第二种情况,数组里面不为满时if( pos < 1 || pos > pArr->cnt + 1 )return false;//把原来的第pos的位置和之后的数都向前移动for( i = pArr->cnt - 1; i >= pos - 1; --i ){pArr->pBase[i + 1] = pArr->pBase[i];}//把第pos位置插入新的值pArr->pBase[pos - 1] = val;( pArr->cnt ) ++;//个数加一return true;}bool delete_arr( struct Arr *pArr, int pos, int *pVal ){int i;if( is_empty( pArr ) )return false;if( pos < 1 || pos > pArr->cnt )return false;*pVal = pArr->pBase[pos - 1];for( i = pos; i < pArr->cnt; ++i ) //cnt表示数组有效的个数{pArr->pBase[i - 1] = pArr->pBase[i]; //这里要前移一个数,所以上面的可以i <= pArr->cnt - 1}pArr->cnt --; //删除了一个数组个数,所以有效个数要相应的减一return true;}void inversion_arr( struct Arr *pArr ){int i = 0;int j = pArr->cnt - 1;int t;while( i < j ){t = pArr->pBase[i];pArr->pBase[i] = pArr->pBase[j];pArr->pBase[j] = t;++ i;-- j;}return;}void sort_arr( struct Arr *pArr ){int i, j, t;for( i = 0; i < pArr->cnt; ++i ){for( j = j + 1; j < pArr->cnt; ++ j ){if( pArr->pBase[i] > pArr->pBase[j] ){t = pArr->pBase[i];pArr->pBase[i] = pArr->pBase[j];pArr->pBase[j] = t;}}}}链表//2012年9月12日早上离散存储[链表]定义:n个节点离散分配彼此通过指针相连每个节点只有一个前驱节点,每个节点只有后续节点首节点没有前驱节点,尾节点没有后续节点术语:首节点:第一个有效的节点尾节点:最后一个有效的节点头节点:头节点额数据类型和首节点类型一样首节点前一个没有存放有效数据加头节点的目的主要是为了方便链表的操作头指针:指向头节点的指针变量尾指针:指向尾节点的指针如果希望通过一个函数来对链表进行处理,我们至少需要接受链表的哪些参数因为我们通过头指针可以推算出链表的其他信息分类:单链表双链表:每一个节点有两个指针域循环链表:能通过任何一个节点找到所有的节点非循环链表算法:遍历查找清空销毁求长度排序删除节点插入节点typedef struct node{int data;struct node *pNext;}NODE, *PNODE;PNODE p = ( PNODE )malloc( sizeof( NODE ) );free( p );//删除p指向节点所占的内存,不是删除p本身所占的内存p->pNext;//p所指向结构体变量中的pNext成员本身删除p所节点的后面节点先临时定义一个指向p后面节点的指针rr = p->pNext;//r所指向后面的那个节点p->pNext = r->pNext;free(r);链表创建和链表遍历算法的演示//2012年9月12日早上# include <stdio.h># include <malloc.h># include <stdlib.h>typedef struct Node{int data;struct Node *pNext;}NODE, *PNODE;//函数的声明PNODE create_list(void);void traverse_list( PNODE pHead );int main( void ){PNODE pHead = NULL;//等价于struct Node *pHead = NULL;pHead = create_list();//创建一个非循环单链表,并将该链表的头节点的头结点的地址赋给pHeadtraverse_list( pHead );return 0;}PNODE create_list(void){int len;//用来存放节点有效节点的个数int i;int val;//用来临时存放用户输入的节点的值//分配了一个不存放有效数据的头节点PNODE pHead = ( PNODE )malloc( sizeof( NODE ) );if( NULL == pHead ){printf( " 分配失败,程序终止" );exit( -1 );}PNODE pTail = pHead;//pTail指向链表的尾指针,刚开始时尾指针后头指针指向的相同pTail->pNext = NULL;printf( " 请输入你需要生成的链表节点的个数:len = " );//scanf( " %d ", &len ); //注意了,这里要先打一个空格,在输入一个数字,因为%d前面有个空格,所以......scanf( "%d", &len );for( i = 0; i < len; ++i ){printf(" 请输入第%d个节点的值: ", i + 1);scanf( "%d", &val );PNODE pNew = ( PNODE )malloc( sizeof( NODE ) );if( NULL == pNew ){printf( " 分配失败,程序终止" );exit( -1 );}//把新生成的节点挂在尾节点的后面pNew->data = val;pTail->pNext = pNew;pNew->pNext = NULL;pTail = pNew;//}return pHead;}void traverse_list( PNODE pHead ){PNODE p = pHead->pNext;while( NULL != p ){printf( " %d ", p->data );p = p->pNext;}printf( " \n " );return;//意思是告诉别人程序已经结束}每一个链表节点的数据类型该如何处理的问题//2012年9月11日晚上# include <stdio.h>typedef struct Node{int data;//数据域struct Node *pNext;//指针域}NODE, *PNODE;int main( void ){return 0;}排序:冒泡出入选择快速排序归并排序排序和查找的关系排序是查找的前提排序重点Typedef的用法1# include <stdio.h>//typedef int lisi; //为int再重新多取一个名字,lisi等价于inttypedef struct Student{int sid;char name[100];char sex;}ST;int main( void ){struct Student st; //等价于ST st;struct Student *ps = &st; //等价于ST *PS;ST st2;st2.sid = 200;printf("%d\n", st2.sid);return 0;}Typedef的用法2# include <stdio.h>typedef struct Student{int sid;char name[100];char sex;}*PST; //PST等价于struct Student *int main( void ){struct Student st;PST ps = &st;ps->sid = 99;printf( "%d\n", ps->sid );return 0;}Typedef的用法3# include <stdio.h>typedef struct Student{int sid;char name[100];char sex;}*PSTU, STU;//等价于STU代表struct Student, PSTU代表了struct Student *int main( void ){STU st;//struct Studtn st;PSTU ps = &st;//struct Student *ps = &st;ps->sid = 99;printf( "%d\n", ps->sid );return 0;}1到100相加# include <stdio.h>long sum( int n ){//用递归实现if( 1 == n )return 1;elsereturn sum( n - 1 ) + n;/*//用循环实现long s = 0;int i;for( i = 1; i <= n; ++i )s += i;return s;*/}int main( void ){printf( "%d\n", sum(100) );return 0;}//2012年9月17日早上到2012年9月18日早上递归定义:一个函数自己直接或间接调用自己举例:1)1 + 2 + 3 + ...100的和2)求阶乘3)汉诺塔4)走迷宫递归满足三个条件1)递归必须得有一个明确的中止条件2)该函数所处理的数据规模必须在递减3)这个转化必须是可解的递归和循环递归:1)易于理解2)速度慢3)存储空间大循环:1)不易理解2)速度快3)存储空间小递归的应用数和森林就是以递归的方式定义的数和图很多算法都是以递归来实现的很多数学公式就是以递归的方式定义的菲波拉契//2012年9月18日早上# include <stdio.h>void hannuota( int n, char A, char B, char C ){/*如果是1个盘子直接将A柱子上的盘子从A移动到C否则先将A柱子上的n-1个盘子借助于C移动到B直接将A柱子上的盘子从A移到C最后将B柱子上的n-1借助A移动到C*/if( 1 == n ){printf( "将编号为%d的盘子直接从%c柱子%c柱子\n", n, A, C );}else{hannuota( n-1, A, C, B );printf( "将编号为%d的盘子直接从%c柱子%c柱子\n", n, A, C );hannuota( n-1, B, A, C );}}int main( void ){char ch1 = 'A';char ch2 = 'B';char ch3 = 'C';int n;printf( "请输入要移动的盘子个数:" );scanf( "%d", &n );hannuota( n, 'A', 'B', 'C' );}阶乘的递归实现# include <stdio.h>//假定是1或大于1的值long f( long n ){if( 1 == n )return 1;elsereturn f( n - 1 ) * n;}int main( void ){printf( "%d\n", f(3) );return 0;} 阶乘的循环使用//程序实现不了功能噢# include <stdio.h>int main( void ){int val;int i, mult;printf( "请输入一个数字:" );printf( "val = " );scanf( "%d", &val );for( i = 1; i <= val; ++i )mult = mult * i;printf( "%d的阶乘是:%d\n", val, mult );return 0;}队和列//2012年9月14日早上到2012年9月17日早上队列定义:一种可以实现"先进先出"的存储结构分类链式队列用链表实现静态队列用数组实现静态队列通常都必须是循环队列循环队列1.静态队列为什么必须是循环队列2.循环队列需要几个参数确定及其含义两个参数来确定(front和rear)2个参数不同场合有不同的含义建议初学者先记住,然后慢慢体会1)队列初始化front和rear的值都是零2)队列非空front代表的是队列的第一个因素rear代表的是队列的最后一个有效元素的下一个元素3)队列空front和reard的值相等,单不是零3.循环队列各个参数含义建议初学者先记住,然后慢慢体会1)队列初始化front和rear的值都是零2)队列非空front代表的是队列的第一个因素rear代表的是队列的最后一个有效元素的下一个元素3)队列空front和reard的值相等,单不是零4.循环队列入队伪算法讲解两部完成:1)将值存入r所代表的位置2)rear = ( rear+1 ) % 数组的长度5.循环队列出队伪算法讲解front = ( front+1 ) % 数组的长度6.如何判断循环队列是否为空如果front与rear的值相等则该队列就一定为空7.如何判断循环队列是否已满预备知识:( front和rear没有任何关系)front的值可能比rear大也完全有可能比rear小当然也可能相等两种方式:1)多增加一个标志参数2)少用一个元素[通常使用第二种方式]用c伪算法表示就是:如果r和f的值紧挨着,则队列已满if( (r + 1)%数组长度== f )已满else不满队列算法入队出队队列的具体应用所有和时间有关的操作都与队列有关循环队列程序的演示/2012年9月17日早上# include <stdio.h># include <malloc.h>typedef struct Queue{int *pBase;int front;int rear;}QUEUE;void init( QUEUE * );bool en_queue( QUEUE *, int val );//入队void traverse_queue( QUEUE * );bool full_queue( QUEUE * );bool out_queue( QUEUE *, int * );//出队bool empty_queue( QUEUE * );int main( void ){QUEUE Q;int val;init( &Q );en_queue( &Q, 1 );en_queue( &Q, 2 );en_queue( &Q, 3 );en_queue( &Q, 4 );en_queue( &Q, 5 );en_queue( &Q, 6 );en_queue( &Q, 7 );en_queue( &Q, 8 );en_queue( &Q, 9 );traverse_queue( &Q );if ( out_queue( &Q, &val ) ){printf( "出队成功,队列出队的元素是%d\n", val );}else{printf( "出队失败!\n" );}traverse_queue( &Q );return 0;}void init( QUEUE *pQ ){pQ->pBase = ( int * )malloc( sizeof( int )*6 );pQ->front = 0;pQ->rear = 0;}bool full_queue( QUEUE *pQ ){if( ( pQ->rear + 1 ) % 6 == pQ->front ){return true;}elsereturn false;}bool en_queue( QUEUE *pQ, int val ){if( full_queue( pQ ) ){return false;}else{pQ->pBase[pQ->rear] = val;pQ->rear = ( pQ->rear + 1 ) % 6;return true;}}void traverse_queue( QUEUE *pQ ){int i = pQ->front;while( i != pQ->rear ){printf( "%d ", pQ->pBase[i] );i = ( i + 1 ) % 6;}printf("\n");return;}bool empty_queue( QUEUE *pQ ){if( pQ->front == pQ->rear )return true;elsereturn false;}bool out_queue( QUEUE *pQ, int *pVal ) {if( empty_queue( pQ ) ){return false;}else{*pVal = pQ->pBase[pQ->front];pQ->front = (pQ->front + 1) % 6;return true;}树测试# include <stdio.h># include <stdlib.h>int main ( void ){int i;charchar *ptr1[4]={"china","chengdu","sichuang","chongqin"};for( i = 0; i < 4; i ++ ){printf( "%s\n", ptr1[i] );}}树非线性结构数树定义专业定义:1)有且只有一个称为根的节点2)有若干个互不相交的子树,这些子树本身也是一课树通俗的定义:1)树是由节点和边组成2)每个节点只有一个父节点但可以有多个子节点3)但有一个节点例外,该节点没有放节点,此节点称为根节点专业术语节点父节点子节点子孙堂兄弟深度:从根节点到底层节点的层数称之为深度根节点是第一层叶子节点:没有子节点的节点非终端节点:实际就是非叶子节点(有子节点的节点)度:子节点的个数称为度树分类一般树任意一个子节点的个数都不受限制二叉树任意一个节点的子节点的个数最多两个,且子节点的个数不可以更改分类:一般二叉树满二叉树在不增加树的层数的前提下,无法再添加一个节点的二叉树就是满二叉树完全二叉树如果只是删除满二叉树最底层最右边的连续若干个节点,这样形成的二叉树就是完全二叉树森林n个互不相交的树的集合树的存储森林的存储二叉树的存储连续存储[完全二叉树](只能是这样子存储)优点:查找某个节点的父节点和子节点(也包括有没有子节点)很快缺点:耗用内存空间过大链式存储一般树的存储双亲表示法求父节点方遍孩子表示法求子节点方便双亲孩子表示法求父节点和子节点都很方便二叉树表示法把一个普通的树转化成二叉树来存储具体的转化方法:设法保证任意一个节点的左指针域指向它的第一个孩子右指针域指向他的下一个兄弟只要能满足此条件,就可以把一个普通树转化成二叉树一个普通树转化成的二叉树一定没有右子树森林的存储先把森林转化成二叉树,在存储二叉树操作遍历先序遍历[先访问根节点]先访问根节点再先序访问左子树再先序访问右子树中序遍历[中间访问根节点]中序遍历左子树再访问根节点再中序遍历右子树后续遍历[最后访问根节点]中序遍历左子树中序遍历右子树再访问根节点已知二中遍历序列求原始二叉树通过先序和中序或者中序和后序我们可以还原原始的二叉树但是通过先序和后序是无法还原原始的二叉树的换种说法:只有通过先序和中序,或者中序和后序我们才可以唯一的确定的二叉树应用树是数据库中数据组织一种重要形式操作系统子父进程的关系本身就是一颗树面向对象语言中类的继承关系本身就是一棵树赫夫曼树数操作链式二叉树遍历具体程序演示# include <stdio.h># include <malloc.h>struct BTNode{int data;struct BTNode *pLchild;//p是指针L是左child是孩子struct BTNode *pRchild;};void PreTraverseBTree( struct BTNode * pT );void InTraverseBTree( struct BTNode * pT );void PostTraverseBTree( struct BTNode * pT );struct BTNode *CreateBTree( void );int main( void ){struct BTNode *pT = CreateBTree();printf( "先序\n" );PreTraverseBTree( pT );//先序printf( "中序\n" );InTraverseBTree( pT );//中序printf( "后序\n" );PostTraverseBTree( pT );//后序return 0;}void PreTraverseBTree( struct BTNode * pT ) {/*伪算法先访问根节点再先序访问左子树再先序访问右子树*/if( pT != NULL ){printf( "%c\n", pT->data );if( NULL != pT->pLchild ){PreTraverseBTree( pT->pLchild );}if( NULL != pT->pRchild ){PreTraverseBTree( pT->pRchild );//pT->pLchild可以代表整个左子树}}}void InTraverseBTree( struct BTNode * pT ){if( pT != NULL ){if( NULL != pT->pLchild ){InTraverseBTree( pT->pLchild );}printf( "%c\n", pT->data );if( NULL != pT->pRchild ){InTraverseBTree( pT->pRchild );//pT->pLchild可以代表整个左子树}}}void PostTraverseBTree( struct BTNode * pT ) {if( pT != NULL ){if( NULL != pT->pLchild ){InTraverseBTree( pT->pLchild );}if( NULL != pT->pRchild ){InTraverseBTree( pT->pRchild );//pT->pLchild可以代表整个左子树}printf( "%c\n", pT->data );}}struct BTNode *CreateBTree( void ){struct BTNode *pA = ( struct BTNode * )malloc( sizeof( struct BTNode ) );struct BTNode *pB = ( struct BTNode * )malloc( sizeof( struct BTNode ) );struct BTNode *pC = ( struct BTNode * )malloc( sizeof( struct BTNode ) );struct BTNode *pD = ( struct BTNode * )malloc( sizeof( struct BTNode ) );struct BTNode *pE = ( struct BTNode * )malloc( sizeof( struct BTNode ) );pA->data = 'A';pB->data = 'B';pC->data = 'C';pD->data = 'D';pE->data = 'E';pA->pLchild = pB;pA->pRchild = pC;pB->pLchild = pB->pRchild = NULL;pC->pLchild = pD;pC->pRchild = NULL;pD->pLchild = NULL;pD->pRchild = pE;pE->pLchild = pE->pRchild = NULL;return pA;}线性结构和非线性结构逻辑结构线性数组链表栈和队列是一种特殊线性结构非线性树图物理结构快速排序图表链式二叉树二叉树表示方法后序遍历普通二叉树转化为二叉树普通二叉树转化为完全二叉树(正方形为要删除的)森林转化成二叉树先序遍历先序遍历2已知先序和中序求后序已知先序和中序求后序2已知中序和后序求先序中序遍历中序遍历2栈定义一种可以实现"先进后出"的存储结构栈类似于箱子分类静态栈动态栈算法出栈压栈应用函数调用中断表达式求值内存分配缓冲处理迷宫凡是静态分配的的都是栈里面分配(有操作系统帮你分配的)凡是动态分配的都是堆里面分配(由程序员手动分配的)//编译器编译成功,但是运行不是程序所需要的功能?# include <stdio.h># include <malloc.h># include <stdlib.h>typedef struct Node{int data;struct Node *pNext;}NODE, *PNODE;typedef struct Stack{PNODE pTop;PNODE pBottom;//pBottem是指向栈底下一个没有实际意义的元素}STACK, *PSTACK;void init( PSTACK );void push( PSTACK, int );void traverse( PSTACK );bool pop( PSTACK, int * );bool empty( PSTACK pS );int main( void ){STACK S;//STACK等价于struct Stackint val;init( &S );//目的是造出一个空栈push( &S, 1 );//压栈push( &S, 2 );push( &S, 3 );push( &S, 4 );push( &S, 5 );push( &S, 6 );push( &S, 7 );traverse( &S );//遍历输出clear( &S ); //清空数据traverse( &S );//遍历输出if( pop( &S, &val ) )printf( "出栈成功,出栈的元素是&d\n", val );}else{printf( "出栈失败" );}traverse( &S );//遍历输出return 0;}void init( PSTACK pS ){pS->pTop = ( PNODE )malloc( sizeof( NODE ) );if( NULL == pS->pTop ){printf( "动态内存分配失败!\n" );exit( -1 );}else{pS->pBottom = pS->pTop;pS->pTop = NULL;//或是pS->pBottom = NULL;}}void push( PSTACK pS, int val ){PNODE pNew = ( PNODE )malloc( sizeof( NODE ) );pNew->data = val;pNew->pNext = pS->pTop;//pS->Top不能改为pS->pBottom pS->pTop = pNew;return;}void traverse( PSTACK pS ){PNODE p = pS->pTop;while( p != pS->pBottom ){printf( "%d ", p->data );p = p->pNext;printf( "\n" );return;}bool empty( PSTACK pS ){if( pS->pTop == pS->pBottom )return true;elsereturn false;}//把pS所指向的栈出栈一次,并把出栈的元素存入pVal形参所指向的变量中,如果出栈失败,则返回false,否则truebool pop( PSTACK pS, int *pVal){if( empty( pS ) )//pS本身存放的就是S的地址{return false;}else{PNODE r = pS->pTop;*pVal = r->data;pS->pTop = r->pNext;free( r );r = NULL; //为什么要把r赋给NULL呢??return true;}}//clear清空void clear( PSTACK pS ){if( empty( pS ) ){return ;}else{PNODE p = pS->pTop;PNODE q = p->pNext;while( p != pS->pBottom ){q = p->pNext;free( p );p = q;}pS->pTop = pS->pBottom;}}。

数据结构14:队列(Queue),“先进先出”的数据结构

数据结构14:队列(Queue),“先进先出”的数据结构

数据结构14:队列(Queue),“先进先出”的数据结构队列是线性表的⼀种,在操作数据元素时,和栈⼀样,有⾃⼰的规则:使⽤队列存取数据元素时,数据元素只能从表的⼀端进⼊队列,另⼀端出队列,如图1。

图1 队列⽰意图称进⼊队列的⼀端为“队尾”;出队列的⼀端为“队头”。

数据元素全部由队尾陆续进队列,由队头陆续出队列。

队列的先进先出原则队列从⼀端存⼊数据,另⼀端调取数据的原则称为“先进先出”原则。

(first in first out,简称“FIFO”)图1中,根据队列的先进先出原则,(a1,a2,a3,…,a n)中,由于 a1 最先从队尾进⼊队列,所以可以最先从队头出队列,对于 a2来说,只有a1出队之后,a2才能出队。

类似于⽇常⽣活中排队买票,先排队(⼊队列),等⾃⼰前⾯的⼈逐个买完票,逐个出队列之后,才轮到你买票。

买完之后,你也出队列。

先进⼊队列的⼈先买票并先出队列(不存在插队)。

队列的实现⽅式队列的实现同样有两种⽅式:顺序存储和链式存储。

两者的区别同样在于数据元素在物理存储结构上的不同。

队列的顺序表⽰和实现使⽤顺序存储结构表⽰队列时,⾸先申请⾜够⼤的内存空间建⽴⼀个数组,除此之外,为了满⾜队列从队尾存⼊数据元素,从队头删除数据元素,还需要定义两个指针分别作为头指针和尾指针。

当有数据元素进⼊队列时,将数据元素存放到队尾指针指向的位置,然后队尾指针增加 1;当删除对头元素(即使想删除的是队列中的元素,也必须从队头开始⼀个个的删除)时,只需要移动头指针的位置就可以了。

顺序表⽰是在数组中操作数据元素,由于数组本⾝有下标,所以队列的头指针和尾指针可以⽤数组下标来代替,既实现了⽬的,⼜简化了程序。

例如,将队列(1,2,3,4)依次⼊队,然后依次出队并输出。

代码实现:#include <stdio.h>int enQueue(int *a, int rear, int data){a[rear] = data;rear++;return rear;}void deQueue(int *a, int front, int rear){//如果 front==rear,表⽰队列为空while (front != rear) {printf("%d", a[front]);front++;}}int main(){int a[100];int front, rear;//设置队头指针和队尾指针,当队列中没有元素时,队头和队尾指向同⼀块地址front = rear = 0;rear = enQueue(a, rear, 1);rear = enQueue(a, rear, 2);rear = enQueue(a, rear, 3);rear = enQueue(a, rear, 4);//出队deQueue(a, front, rear);return0;}顺序存储存在的问题当使⽤线性表的顺序表⽰实现队列时,由于按照先进先出的原则,队列的队尾⼀直不断的添加数据元素,队头不断的删除数据元素。

05《数据结构》第三章二叉树(下)

05《数据结构》第三章二叉树(下)

1
2 b
3
4 d
5
6
7
c
e
队头指针
队尾指针
假设在这之后又有两个元素 f 和 g 相继入队列,而队列中的元 素 b 和 c 又相继出队列,队列的变化?
22
循环队列

队头指针指向元素 d,队尾指针则指到数组"之外"的位置上
去了,致使下一个入队操作无法进行 (请注意此时队列空间
并未满 ) 。为此,设想这个数组的存储空间是个 " 环 " ,认定 "7"的下一个位置是"0"。
a1

an ^
Q.front
Q.rear
^
13
队列的存储表示和操作的实现
链队列的类型定义:

// 结构定义 typedef struct QNode {
QElemType data;
struct QNode *next; }QNode,*QueuePtr;
typedef struct
{ QueuePtr front; //队头指针 QueuePtr rear; //队尾指针 }LinkQueue; 14
16
队列的存储表示和操作的实现
以下给出其中4个函数的定义:

Status InitQueue (LinkQueue &Q)
{ // 构造一个空队列 Q Q.Front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if ( !Q.front ) exit(OVERFLOW);
GetHead(Q,&e) 初始条件:Q 为非空队列。 操作结果:用 e 返回Q的队头元素。 11

算法设计与分析动态顺序统计

算法设计与分析动态顺序统计
个元素,故所求即为以right[x]为根的子树中第(i-r) 小元素
例:i=17
OS-SELECT的运行时间为O(lg n)
确定元素的秩
y从x节点开始循环往上遍历:
y是个左孩子,r保持不动 y是个右孩子,r将加上size[left[p[y]]]+1 y=root[T]时, r就是key[x]的秩
选择操作(检索具有给定秩的元素)
元素的秩——它在集合的线性序中的位置 调用过程OS-SELECT(root[T],i),找出顺序统 计树T中的第i小关键字
以x为根的子树中x的秩为size[left[x]]+1
i=r,第i小元素为x; i<r,第i小元素在x的左子树中; i>r,第i小元素在x的右子树中, x的右子树前共有r
步骤4中,我们设计了操作OS-SELECT和OS-RANK。 对新操作的需要是我们对数据结构进行扩张的首要动 因。有时,我们并不设计新的操作,而是利用附加的 信息来加速已有操作的执行速度。
红黑树扩张定理
设域f对含n 个节点的红-黑进行了扩张, 且假设某节点x的域f的内容可以仅用节点 x,left[x],和right[x]中的信息(包括 f[left[x]]与f[right[x]])来计算。这样, 在插入和删除操作中我们可以不影响这 两个操作O(lgn)渐进性能的情况下对T的 所有节点的f值进行维护。
步骤2中,我们提供了size域,它可以存放各节点的 子树规模的信息。一般地,附加信息可使得各类操作 更加有效。例如,我们本可以仅用树中存储的关键字 来实现OS-SELECT和OS-RANK,但这种实现的运行就 不止是O(lgn)了。有些时候,附加信息是指针类信息, 而不是具体的数据。

02《数据结构》第二章(上)

02《数据结构》第二章(上)

6
线性表的类型定义
线 性 表 (Linear List) : 由 n(n≧0) 个 数 据 元 素 ( 结 点 )a1 ,
a2, …an组成的有限序列。

其中数据元素的个数n定义为表的长度。 (有n-1个有序对)

当n=0时,称为空表,常常将非空的线性表(n>0)记作:
(a1,a2,…an)

这里的数据元素 ai (1≦i≦n) 只是一个抽象的符号,其具体 含义在不同的情况下可以不同。i为ai 在线性表中的位序。
16
线性表类型的应用
算法2.1 Algo2-1.c void union ( List &LA,List LB ) { // 将所有在线性表LB中但不在LA中的数据元素插入到 LA 中, // 算法执行之后,线性表 LB 不再存在。 La_len = ListLength(LA);// 求得线性表LA的长度 Lb_len = ListLength(LB);// 求得线性表LB的长度 for(i=1;i<=Lb_len;i++) // 依次处理LB中元素直至LB为空 { GetElem(LB,i,e);//从LB中删除第i个数据元素并赋给 e // 当LA中不存在和 e 值相同的数据元素时进行插入 if (!LocateElem(LA,e,equal( )) ListInsert(LA,++La_len,e); } //for } // union 算法2.1的时间复杂度为O(ListLength(LA)*ListLength(LB))
依值在线性表 LA 中进行查询; 若不存在,则将它插入到 LA 中。
容易看出,上述的每一步恰好对应线性表的一个基本操作:
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第 29 卷第 1 期 2017 年 1 月
系统仿真学报 Journal of System Simulation
Vol. 29 No. 1 Jan., 2017
了 实 现 仿 真 环 境 下 的 三 维 模 型 LOD(Levels of Details)效果,文献[5]提出在模型简化过程中以边 的折叠代价为优先权构建优先队列, 每次从优先队 列中选取折叠代价最小的边对模型进行简化操作, 在保持高保真度简化效果的同时,确保了 QEM (Quadric Error Metric)算法的高效率;文献[6]提出 一种基于 “ 截止期最早最优先 ”(Earliest Deadline First)策略的动态调度方法,在相关优先队列结构 的支持下,提高了分布式数据库 HBase 中文件系 统的效率;文献[7]讨论了在采用优先队列的情况 下,4 种主要类型的移动网络通信的数据转发性 能,并在网络仿真软件 NS2 中对相关性能进行了 分析;文献[8]提出一种模拟待泊船只排队进港的 优先级调度算法,其中优先权由船舶的不同性质决 定, 研究结果表明良好的优先队列组织结构能够有 效地优化了港口调度算法的运行时间效率, 提高了 调度算法的实时性。 目前,优先队列的数据组织方式多种多样,不 同的数据组织形式对数据存取的性能影响明显。优 化的组织结构能够有效发挥优先队列存取效率, 有 助于提升相关算法的性能,进而提高仿真系统的实 时性;而不好的组织结构可能会使算法的时空效率 大打折扣[9]。因此,如何有效地组织优先队列的数 据结构就显得尤为重要。 本文给出一种高效的优先队列的数据组织结 构,该数据结构以堆式数据存取方法为基础, 通过 引入数据的动态维护和管理机制, 能够实现数据存 取的高效率。与现有主流优先队列数据结构相比, 不但拥有更低的时间复杂度, 而且还能确保数据动 态存取的高效性、存储空间的自适应性、以及实现 上的简单性。
Efficient Priority Queue Constructed on Heap-like Data Structure
Qi Binbin, Pang Mingyong
(Department of Educational Technology, Nanjing Normal University, Nanjing 210097, China)
http:∥ • 92 •
第 29 卷第 1 期 2017 年 1 月
Vol. 29 No. 1
祁彬斌, 等: 一种高效的动态优先队列数据结构
Jan., 2017
树中每个结点的孩子数目可能不同, 因此记录 孩子信息的结点空间大小也可能不一样。 为避免这 一情况,本文基于“左孩子-右兄弟”给出了堆式队列 的表示方法,如图 2 所示。该表示方法中,每个堆 式队列的结点除了包含用于排序的元素外, 还涉及 前驱结点、左孩子结点、右兄弟结点的信息。若一 个结点是其父结点的最左孩子结点,则它的父结点 就是前驱结点;否则,其前驱结点为该结点的左兄 弟。以图 2 为例,1 为 8 的前驱结点,8 是 2 的前驱 结点;8 是 1 的左孩子结点,2 是 8 的右兄弟结点。
Fig.4 (b) 权值 y ≥ x
图 4 出队环节中合并子操作 Sub-operation merging in dequeue operation
图 3 孩子兄弟表示的堆式队列的进队操作 Fig.3 Enqueue operation of heap-like queue
2.2 出队操作
出队是从堆式队列中移出权值最小的结点,即 根结点。当删除并返回根结点后,需从原根结点的 所有孩子结点中选取权值最小的成为新的根结点。 最简单的做法是:根结点删除后,原堆式队列会变 成若干棵子树,依次合并这些子树便能生成一个新 的堆式队列。该合并是由若干个合并子操作组成。 合并子操作主要是指比较两棵子树根结点权值的大 小,将根结点权值较大的子树作为另一棵子树最左 边的孩子,原先最左边的孩子变为其第 2 个孩子, 第 2 个孩子则变更为第 3 个孩子, 依次类推(见图 4)。
Fig.2
பைடு நூலகம்
图 2 堆式队列的孩子兄弟表示 Child-sibling representation of heap-like queue in Fig.1
2
基本操作
本文堆式优先队列有“进队”、“出队”、“动态
调整优先权”3 种基本操作,通过调用这些基本操 作, 可以保证优先队列中数据的快速存取和内部次 序的自动调整。
引言
1
度的扩展方法, 该方法在兼顾网络带宽分配均衡性 的基础上,有效地保证了系统的效率;文献[2]在 事件驱动的大规模碰撞检测仿真中, 考虑到算法中 存在的大量事件调度情况, 根据算法的特点引入了 相应的优先队列数据结构,改善了算法的时空效 率; 文献[3]提出一种基于 Kd-Tree 和优先队列的图 像特征匹配算法, 该算法利用优先队列搜索给定点 的近似最近邻近点,有效地提高了算法的搜索效 率,实现了特征的实时匹配;文献[4]给出了图像 分析领域中几种常见的优先队列组织结构, 对不同 结构在具体应用情形下的存取性能进行了评估; 为
在系统仿真领域中, 有大量的应用涉及到优先 队列结构的数据存取。快捷、高效、便利的优先队 列组织方式对相关系统的性能提高发挥着重要作 用,如文献[1]结合一种优先队列调度算法,给出 了网络仿真软件 NS2(Network Simulator 2)队列调
收稿日期:2015-04-24 修回日期:2015-07-07;
1.2 结构组织
本文给出一种高效的堆式优先队列数据结构 (以下简称为“堆式队列”),其具有可动态更新、自 动排序等特点, 能够动态地调整已排序队列中的元 素优先权,显著地改善数据存取的性能。 本文堆式队列的基础是一棵没有限制孩子结点 数目的树: 树中每一个结点的权值均不大于其孩子 结点的权值, 权值最小的结点为根结点, 同一层次 的兄弟之间没有固定的先后次序,如图 1 所示。
中每个元素被赋予不同优先权,出队列的顺序由元 素的优先权决定,最高优先权的元素最先被删除, 具有最高进先出(Largest-In,First-Out)的行为特性。 传统优先队列通常选用线性表、二叉查找树、 堆等多种数据组织方式来表示。 其中, 线性表作为 一种常用的组织结构, 按是否进行优先权排序又被 分为无序表和有序表两大类。 由于实际应用中很难 预先估计优先队列的最大长度, 故在具体实现上更 多地采用链表存储方式。 对于有序链表而言, 元素 进队需按照优先权进行预先排序, 排序过程的时间 复杂度为 O(n) 。出队时则可直接移出权值最小的 结点,时间复杂度为 O(1) 。对于无序链表而言,具 体情形与之相反,元素进队操作的时间复杂度为 O(1) , 出队则需遍历队列中的元素, 此时的时间复 杂度为 O(n) 。但总体而言,在传统的优先队列数 据组织结构中,无论是采用无序链表还是有序链 表, 均未能有效地降低队列操作的时间复杂度、 减 少算法的执行时间, 致使实际应用时易导致算法的 执行效率不高等问题,具有一定的局限性。
基金项目:国家自然科学基金(41271383, 60873175), 江苏省现代教育技术研究课题(2014-R-33356); 作者简介:祁彬斌(1991-),男,江苏盐城,博士生, 研究方向为数字几何处理;庞明勇(1968-),男,安徽 淮南, 博士, 教授,博导, 研究方向为数字几何处理。
http:∥ • 91 •
给出一种高效的堆式队列数据组织结构,阐明了该数据结构的基本操作,理论上分析了其时间和空 间性能,通过实验和应用实例验证了堆式队列数据结构动态存取效率的高效性、存储空间的自适应 实验结果表明, 堆式优先队列数据结构可有效地提高各类仿真系统的性能, 性以及实现上的简单性。
也可用于组合优化等多种应用问题的解决。 关键词:数据结构;优先队列;堆式队列;算法效率 中图分类号:TP391 文献标识码:A 文章编号:1004-731X (2017) 01-0091-08 DOI: 10.16182/j.issn1004731x.joss.201701013
第 29 卷第 1 期 2017 年 1 月
系统仿真学报© Journal of System Simulation
Vol. 29 No. 1 Jan., 2017
一种高效的动态优先队列数据结构
祁彬斌,庞明勇
(南京师范大学教育技术系,江苏 南京 210097)
摘要:数据结构的组织形式在算法实现中占有重要地位。探讨了优先队列中的数据结构组织问题,
Abstract: How to organize data structure is a very important issue for various algorithm implementations. In this paper, a novel and efficient priority-queue was presented based on a kind of heap-like data structure. The organization structure of the queue is first introduced and then a set of basic operators on the queue were elucidated, the time and space performances of the data structure was also theoretically analyzed. Our data structure has several advantages, including the adaptability of storage space as well as the simplicity and convenience of implementation. Experimental results showed that the heuristic priority queue data structure can effectively improve the performance of various types of simulation systems, relative to its traditional counterparts. Our data structure can be used to solve a variety of application problems such as combinatorial optimization. Keywords: data structure; priority queue; heap-like queue; efficiency of algorithm
相关文档
最新文档