数据结构之线性表1

news2024/12/17 6:29:53

2.1 线性表的定义和基本操作

1.线性结构的特点是:在数据元素的非空有限集中,
(1)存在惟一的一个被称做“第一个”的数据元素;
(2) 存在惟一的一个被称做“最后一个”的数据元素;
(3) 除第一个之外,集合中的每个数据元素均只有一个前驱; 
(4) 除最后一个之外,集合中每个数据元素均只有一个后继。

2.线性表是一种线性结构,在一个线性表中数据元素的类型是相同的,或者说线性表是
由同一类型的数据元素构成的线性结构

定义如下:
线性表是具有相同数据类型的 n(n≥0)个数据元素的有限序列,通常记为:
(a1,a2,… ai-1,ai,ai+1,…an)   

其中 n 为表长, n=0 时称为空表。
需要说明的是:ai 为序号为 i 的数据元素(i=1,2,…,n)

通常将它的数据类型抽象为   ElemType,ElemType 根据具体问题而定。
线性表是一个相当灵活的数据结构,它的长度可根据需要增长或缩短,即对线性表的数
据元素不仅可以进行访问,还可进行插入和删除等。

3.抽象数据类型线性表的定义如下:
ADT List{
数据对象:D = { ai | ai∈ElemSet, i=1,2,…,n, n≧0 }
数据关系:R = {<ai-1, ai> | ai-1, ai∈D, i=2,3,…,n }

基本操作:
InitList( &L )
操作结果:构造一个空的线性表 L;
GetElem( L, i, &e )
初始条件:线性表 L 已存在,1≦i≦ListLength(L)
操作结果:用 e 返回 L 中第 i 个数据元素的值;
ListInsert ( L, i, &e )
初始条件:线性表 L 已存在,1≦i≦ListLength(L)
操作结果:在线性表 L 中的第 i 个位置插入元素 e;
LocElem(LA, e, equal( ))
初始条件:线性表 L 已存在,1≦i≦ListLength(L)
操作结果:在线性表 L 中查找是否存在元素 e;
} ADT List

例、假设利用两个线性表 LA 和 LB 分别表示两个集合 A 和 B( 即线性表中的数据元素
即为集合中的成员) ,现要求一个新的集合 A=AUB 。这就要求对线性表作如下操作:扩大
线性表 LA , 将存在于线性表 LB 中而不存在于线性表 LA 中的数据元素插入到线性表 LA
中去。只要从线性表 LB 中依次取得每个数据元素,并依值在线性表 LA 中进行查访,若不
存在,则插入之。

上述操作过程可用下列算法描述之。
void union(List &.La, List Lb) {
/扩将所有在线性表 Lb 中但不在 La 中的数据元素插入到 La 中
La _len= ListLength(La); Lb_ len= ListLength(Lb); //求线性表的长度
for (i = 1; i <= Lb_len; i++) {
GetElem( Lb, i , e); //取 Lb 中第 i 个数据元素赋给 e
if (! LocateElem(La,e, equal)) ListInsert( La, + + La_ 1en, e);
// La 中不存在和 e 相同的数据元素,则插入之
} // union


例、 已知线性表 LA 和 LB 中的数据元素按值非递减有序排列,现要求将 LA 和 LB 归
并为一个新的线性表 LC , 且 LC 中的数据元素仍按值非递减有序排列。例如,设
LA = (3 , 5, 8, 11)
LB = (2 , 6, 8, 9, 11 , 15 , 20)
则 LC = (2 , 3, 5, 6, 8, 8, 9, 11 , 11 , 15 , 20)
从上述问题要求可知, LC 中的数据元素或是 LA 中的数据元素,或是 LB 中的数据元
素,则只要先设 LC 为空表,然后将 LA 或 LB 中的元素逐个插入到 LC 中即可。为使 LC 中
元素按值非递减有序排列,可设两个指针 i 和 j 分别指向 LA 和 LB 中某个元素,若设 i 当
前所指的元素为 a,j 当前所指的元素为 b, 则当前应插入到 LC 中的元素 c 为

显然,指针 i 和 j 的初值均为 1. 在所指元素插入 LC 之后, 在 LA 或 LB 中顺序后移。
void MergeList( List La, List Lb, List &.Lc) {
//已知线性表 La 和 Lb 中的数据元素按值非递减排列. //归并 La 和 Lb 得到新的线位表 Lc,Lc 的数据元素也按值非递减排列. InìtList(Lc);
i = j = 1,k = 0, La_ 1en = ListLength(La); Lb_1en= ListLength(Lb), while ((i <=La _len) && (j <= Lb_ 1en)) {//La 和 Lb 均非空
GetElem(La,i , aì); GetE1em(Lb,j , bj);
if (ai <= bj) {Listlnsert(Lc, ++k, ai); ++ i , }
else {Listlnsert(Lc, ++k, bj) , ++ j , }
while (i <= La_ 1en) {
GetElem(La, i++ , ai); ListInsert(Lc, ++k, ai);
while (j <= Lb_ 1en) {
GetElem(Lb,j ++ , bj); LìstInsert(Lc,++ k , bj);
} // MergeList


上述两个算法的时间复杂度取决于抽象数据类型 List 定义中基本操作的执行时间.

假如
GetElem 和 Listlnsert 这两个操作的执行时间和表长无关. LocateElem 的执行时间和表长成
正比,则第一个算法的时间复杂度为 O (ListLength(LA) *ListLength(LB)) ,第二个算法的时
间复杂度则为 O( ListLength(LA) + ListLength(LB))。虽然第二个算法中含 3 个(while)循环语
句,但只有当 i 和 j 均指向表中实际存在的数据元素时, 才能取得数据元素的值并进行相
互比较;并且当其中一个线性表的数据元素均已插入到线性表 LC 中后,只要将另外一个线性
表中的剩余元素依次插入即可。因此,对于每一组具体的输入(LA 和 LB) ,后两个(while ) 循
环语句只执行一个循环体。

2.2 线性表的实现


2.2.1 顺序存储


1.顺序表的定义
线性表的顺序存储是指在内存中用地址连续的一块存储空间顺序存放线性表的各元素,用这种存储形式存储的线性表称其为顺序表。

因为内存中的地址空间是线性的,因此,用物理上的相邻实现数据元素之间的逻辑相邻关系是既简单又自然的。

设 a1 的存储地址为 Loc(a1),每个数据元素占 d 个存储地址,则第 i 个数据元素的地址
为:

Loc(ai)=Loc(a1)+(i-1)*d1≤i≤n
这就是说只要知道顺序表首地址和每个数据元素所占地址单元的个数就可求出第 i 个数
据元素的地址来,这也是顺序表具有按数据元素的序号随机存取的特点
2.顺序表上基本运算的实现
由于高级程序设计语言中的数组类型也有随机存取的特性,因此,通常都用数组来描述
数据结构中的顺序存储结构。

#define MaxSize 100
typedef int ElemType
typedef struct
{ ElemType data[MaxSize];
int length;
} SqList; /*顺序表类型*/

(1) 初始化线性表 InitList(L)
该运算的结果是构造一个空的线性表 L。实际上只需将 length 成员设置为 0 即可。

void InitList(SqList *&L) //引用型指针
{
L=(SqList *)malloc(sizeof(SqList));
/*分配存放线性表的空间*/
L->length=0; }

本算法的时间复杂度为 O(1)。

(2) 销毁线性表 DestroyList(L)
该运算的结果是释放线性表 L 占用的内存空间。

void DestroyList(SqList *&L)
{
free(L); }

本算法的时间复杂度为 O(1)。
(3) 判定是否为空表 ListEmpty(L)
该运算返回一个值表示 L 是否为空表。若 L 为空表,则返回 1,否则返回 0。

int ListEmpty(SqList *L)
{
return(L->length==0); }

本算法的时间复杂度为 O(1)。
(4) 求线性表的长度 ListLength(L)
该运算返回顺序表 L 的长度。实际上只需返回 length 成员的值即可。
int ListLength(SqList *L)
{
return(L->length); }
本算法的时间复杂度为 O(1)。
(5) 输出线性表 DispList(L)
该运算当线性表 L 不为空时,顺序显示 L 中各元素的值。

void DispList(SqList *L)
{
int i;
if (ListEmpty(L))
return;
for (i=0;i<L->length;i++)
printf("%c",L->data[i]);
printf("\n"); }

(6) 求某个数据元素值 GetElem(L,i,e)
该运算返回 L 中第 i(1≤i≤ListLength(L))个元素的值,存放在 e 中。

int GetElem(SqList *L,int i,ElemType &e)
{
if (i<1 || i>L->length)
return 0;
e=L->data[i-1];
return 1; }

本算法的时间复杂度为 O(1)。
(7) 按元素值查找 LocateElem(L,e)
该运算顺序查找第 1 个值域与 e 相等的元素的位序。若这样的元素不存在,则返回值为 0。

int LocateElem(SqList *L, ElemType e)
{
int i=0;
while (i<L->length && L->data[i]!=e)
i++;
if (i>=L->length)
return 0;
else
return i+1; }

(8) 插入数据元素 ListInsert(L,i,e)
该运算在顺序表 L 的第 i 个位置(1≤i≤ListLength(L)+1)上插入新的元素 e。
思路:如果 i 值不正确,则显示相应错误信息;否则将顺序表原来第 i 个元素及以后元素
均后移一个位置,腾出一个空位置插入新元素,顺序表长度增 1。

int ListInsert(SqList *&L,int i,ElemType e)
{
int j;
if (i<1 || i>L->length+1)
return 0;
i--; /*将顺序表逻辑位序转化为 elem 下标即物理位序*/
for (j=L->length;j>i;j--)
L->data[j]=L->data[j-1];
/*将 data[i]及后面元素后移一个位置*/
L->data[i]=e;
L->length++; /*顺序表长度增 1*/
return 1; }


(9) 删除数据元素 ListDelete(L,i,e)
删除顺序表 L 中的第 i(1≤i≤ListLength(L))个元素。
思路:如果 i 值不正确,则显示相应错误信息;否则将线性表第 i 个元素以后元素均向前
移动一个位置,这样覆盖了原来的第 i 个元素,达到删除该元素的目的,最后顺序表长度减 1。

int ListDelete(SqList *&L,int i,ElemType &e)
{
int j;
if (i<1 || i>L->length)
return 0;
i--; /*将顺序表逻辑位序转化为 elem 下标即物理位序*/
e=L->data[i];
for (j=i;j<L->length-1;j++)
L->data[j]=L->data[j+1];
/*将 data[i]之后的元素前移一个位置*/
L->length--; /*顺序表长度减 1*/
return 1;}


3.顺序表的合并问题
对于有序顺序表 La 和 Lb 而言, 合并算法的时间复杂度为 O( La. length +Lb. length-1) 。

int MergeList( SqList La, SqList Lb, SqList *Lc){
pa=La.elem; pb=La.elem;
Lc->listsize = Lc.length=La.length+ Lb.length;
Pc=Lc->elem =( ElemType*)malloc(Lc.listsize*sizeof(ElemType));
If(!Lc.elem) exit(overfiow);
pa_last= La.elem+La.length-1;
pb_last= Lb.elem+Lb.length-1;
while(pa<=pa_last&&pb<=pb_last){
if(*pa<=*pb) *pc++=*pa++;
else
*pc++=*pb++; }
while(pa<=pa_last) *pc++=*pa++;
while(pb<=pb_last) *pc++=*pb++;}


2.2.2 链式存储


(一)单链表
1.基本概念
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。

因此,为了表示每个数据元素 ai 与其直接后继数据元素 ai-1 之间的逻辑关系,对数据元素 ai 来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素 ai 的存储映像,称为结点(node).

它包括两个域: 其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域. 指针域中存储的信息称做指针或链。

n 结点(ai (1<=i<=n)的存储影像)链接成链表,即为线性表(a1 ,a2 …,an)的链式存储结构. 又由于此链表的每个结点中只包含一个指针域,故又称线性链表或单链表。
单链表可由头指针惟一确定,在 C 语言中可用"结构指针"来描述。

typedef struct LinkList /*定义单链表结点类型*/
{ ElemType data;
struct LinkList *next; /*指向后继结点*/
} LinkList;


假设 L 是 LinkList 型的变量,则 L 为单链表的头指针,它指向表中第一个结点。若 L
为"空"(L==NULL) ,则所表示的线性表为"空"表,其长度 n 为"零"。有时,我们在单链表的
第一个结点之前附设一个结点,称之为头结点。头结点的数据域可以不存储任何信息,也可
存储如线性表的长度等类的附加信息,头结点的指针域存储指向第一个结点的指针(即第一个
元素结点的存储位置)。如图 (a) 所示,此时,单链表的头指针指向头结点。若线性表为空表,
则头结点的指针域为"空",如图(b) 所示。

C 语言中的两个标准函数 malloc 和 free 。通常,在设有"指针"数据类型的高级语言中
均存在与其相应的过程或函数。假设 p 和 q 是 LinkList 型的变量,执行 p=(LinkList * ) malloc
( sizeof (LinkList)) 的作用是由系统生成一个 LinkList 型的结点,同时将该结点的起始位置赋
给指针变量 p; 反之,执行 free(q) 的作用是由系统回收一个 LinkList 型的结点。
2.单链表上基本运算的实现
1)建立单链表
●头插法——在链表的头部插入结点建立单链表
链表与顺序表不同,它是一种动态管理的存储结构,链表中的每个结点占用的存储空间
不是预先分配,而是运行时系统根据需求而生成的,因此建立单链表从空表开始,每读入一
个数据元素则申请一个结点,然后插在链表的头部。

void CreateListF(LinkList *&L,ElemType a[],int n)
{ LinkList *s;int i;
L=(LinkList *)malloc(sizeof(LinkList)); /*创建头结点*/
L->next=NULL;
for (i=0;i<n;i++)
{ s=(LinkList *)malloc(sizeof(LinkList));
/*创建新结点*/
s->data=a[i]; s->next=L->next;
/*将*s 插在原开始结点之前,头结点之后*/
L->next=s;
}
}



●尾插法——在单链表的尾部插入结点建立单链表 头插入建立单链表简单,但读入的数
据元素的顺序与生成的链表中元素的顺序是相反的,若希望次序一致,则用尾插入的方法。
因为每次是将新结点插入到链表的尾部,所以需加入 一个指针 r 用来始终指向链表中的尾
结点,以便能够将新结点插入到链表的尾部。
初始状态,头指针 L=NULL,尾指针 r =NULL; 按线性表中元素的顺序依次读入数据元
素, 不是结束标志时,申请结点,将新结点插入到 r 所指结点的后面,然后 r 指向新结点
(注意 第一个结点有所不同)。

void CreateListR(LinkList *&L,ElemType a[],int n)
{ LinkList *s,*r;int i;
L=(LinkList *)malloc(sizeof(LinkList));
/*创建头结点*/
r=L; /*r 始终指向终端结点,开始时指向头结点*/
for (i=0;i<n;i++)
{ s=(LinkList *)malloc(sizeof(LinkList));
/*创建新结点*/
s->data=a[i];r->next=s; /*将*s 插入*r 之后*/
r=s;
}
r->next=NULL; /*终端结点 next 域置为 NULL*/
}



单链表的基本运算
(1) 初始化线性表 InitList(L)
该运算建立一个空的单链表,即创建一个头结点。

void InitList(LinkList *&L)
{
L=(LinkList *)malloc(sizeof(LinkList)); /*创建头结点*/
L->next=NULL; }


(2) 销毁线性表 DestroyList(L)
释放单链表 L 占用的内存空间。即逐一释放全部结点的空间。

void DestroyList(LinkList *&L)
{ LinkList *p=L,*q=p->next;
while (q!=NULL)
{ free(p);
p=q;q=p->next;
}
free(p); }


(3) 判线性表是否为空表 ListEmpty(L)
若单链表 L 没有数据结点,则返回真,否则返回假。

int ListEmpty(LinkList *L)
{
return(L->next==NULL); }


(4) 求线性表的长度 ListLength(L)
返回单链表 L 中数据结点的个数。

int ListLength(LinkList *L)
{ LinkList *p=L;int i=0;
while (p->next!=NULL)
{ i++;
p=p->next;
}
return(i); }


(5) 输出线性表 DispList(L)
逐一扫描单链表 L 的每个数据结点,并显示各结点的 data 域值。

void DispList(LinkList *L)
{ LinkList *p=L->next;
while (p!=NULL)
{ printf("%c",p->data);
p=p->next;
}
printf("\n"); }


(6) 求线性表 L 中指定位置的某个数据元素 GetElem(L,i,&e)
思路:在单链表 L 中从头开始找到第 i 个结点,若存在第 i 个数据结点,则将其 data 域值
赋给变量 e。

int GetElem(LinkList *L,int i,ElemType &e)
{ int j=0;
LinkList *p=L;
while (j<i && p!=NULL)
{ j++;
p=p->next;
}
if (p==NULL)
return 0; /*不存在第 i 个数据结点*/
else /*存在第 i 个数据结点*/
{ e=p->data;
return 1;
}
}


(7) 按元素值查找 LocateElem(L,e)
思路:在单链表 L 中从头开始找第 1 个值域与 e 相等的结点,若存在这样的结点,则返回
位置,否则返回 0。

int LocateElem(LinkList *L,ElemType e)
{LinkList *p=L->next;int n=1;

while (p!=NULL && p->data!=e)
{ p=p->next;
n++; }
if (p==NULL)
return(0);
else
return(n);}


(8) 插入数据元素 ListInsert(&L,i,e)
思路:先在单链表 L 中找到第 i-1 个结点*p,若存在这样的结点,将值为 e 的结点*s 插入到
其后。

int ListInsert(LinkList *&L,int i,ElemType e)
{ int j=0;
LinkList *p=L,*s;
while (j<i-1 && p!=NULL) /*查找第 i-1 个结点*/
{ j++;
p=p->next;
}
if (p==NULL)
return 0; /*未找到位序为 i-1 的结点*/
else /*找到位序为 i-1 的结点*p*/
{ s=(LinkList *)malloc(sizeof(LinkList));
/*创建新结点*s*/
s->data=e;
s->next=p->next; /*将*s 插入到*p 之后*/
p->next=s;
return 1;
}
}


(9) 删除数据元素 ListDelete(&L,i,&e)
思路:先在单链表 L 中找到第 i-1 个结点*p,若存在这样的结点,且也存在后继结点,则删
除该后继结点。

int ListDelete(LinkList *&L,int i,ElemType &e)
{ int j=0;
LinkList *p=L,*q;
while (j<i-1 && p!=NULL) /*查找第 i-1 个结点*/
{ j++;
p=p->next;
}
if (p==NULL) return 0; /*未找到位序为 i-1 的结点*/
else /*找到位序为 i-1 的结点*p*/
{ q=p->next; /*q 指向要删除的结点*/
if (q==NULL)
return 0;
/*若不存在第 i 个结点,返回 0*/
p->next=q->next; /*从单链表中删除*q 结点*/
free(q); /*释放*q 结点*/
return 1;
}
}


变形之一:
删除单链表中值为 key 的所有结点。
基本思想:从单链表的第一个结点开始,对每个结点进行检查,若结点的值为 key,则
删除之,然后检查下一个结点,直到所有的结点都检查。
算法描述:

void Delete_LinkList_Node(LinkList *L,int key)
/* 删除以 L 为头结点的单链表中值为 key 的第一个结点 */
{ LinkList *p=L, *q=L–>next;
while ( q!=NULL)
{ if (q–>data==key)
{ p->next=q->next; free(q);
q=p->next; }
else
{ p=q; q=q–>next; }
}
}


变形之二:
删除单链表中所有值重复的结点,使得所有结点的值都不相同。
基本思想:从单链表的第一个结点开始,对每个结点进行检查:检查链表中该结点的所
有后继结点,只要有值和该结点的值相同,则删除之;然后检查下一个结点,直到所有的结
点都检查。
算法描述:

void Delete_Node_value(LinkList *L)
/* 删除以 L 为头结点的单链表中所有值相同的结点 */
{ LinkList *p=L->next, *q, *ptr;
while ( p!=NULL) /* 检查链表中所有结点 */
{ q=p, ptr=p–>next;
/* 检查结点 p 的所有后继结点 ptr */
while (ptr!=NULL)
{if (ptr–>data==p->data)
{ q->next=ptr->next;
free(ptr); ptr=q->next; }
else { q=ptr; ptr=ptr–>next;}
}
p=p->next ;
}
}


3.单链表的合并
设有两个有序的单链表,它们的头指针分别是 La 、 Lb,将它们合并为以 Lc 为头指针
的有序链表。
算法描述

LinkList *Merge_LinkList(LinkList *La, LinkList *Lb)
/*合并以 La, Lb 为头结点的两个有序单链表*/
{ LinkList *Lc, *pa , *pb , *pc, *ptr ;
Lc=La ; pc=La ;
pa=La->next ;
pb=Lb->next ;
while (pa!=NULL && pb!=NULL)
{ if (pa->data<pb->data)
{ pc->next=pa ;
pc=pa ; pa=pa->next ; }
/*将 pa 所指的结点合并,pa 指向下一个结点 */
if (pa->data>pb->data)
{ pc->next=pb ;
pc=pb ; pb=pb->next ; }
/* 将 pa 所指的结点合并,pa 指向下一个结点 */
if (pa->data==pb->data)
{ pc->next=pa ; pc=pa ;
pa=pa->next ;
ptr=pb ; pb=pb->next ; free(ptr) ; }
/* 将 pa 所指的结点合并,pb 所指结点删除 */
}
if (pa!=NULL)
pc->next=pa ;
else
pc->next=pb ; /*将剩余的结点链上*/
free(Lb) ;
return(Lc) ;}


算法分析:若 La ,Lb 两个链表的长度分别是 m,n,则链表合并的时间复杂度为 O(m+n) 。
(二)循环链表
对于单链表而言,最后一个结点的指针域是空指针,如果将该链表头指针置入该指针域,
则使得链表头尾结点相连,就构成了单循环链表。
在单循环链表上的操作基本上与非循环链表相同,只是将原来判断指针是否为 NULL
变为是否是头指针而已,没有其它较大的变化。

对于单链表只能从头结点开始遍历整个链表,而对于单循环链表则可以从表中任意结点
开始遍历整个链表,不仅如此,有时对链表常做的操作是在表尾、表头进行,此时可以改变
一下链表的标识方法,不用头指针而用一个指向尾结点的指针 R 来标识,可以使得操作效
率得以提高。
(三)双向链表
单链表的结点中只有一个指向其后继结点的指针域 next,因此若已知某结点的指针 p,
其后继结点的指针则为 p->next ,而找其前驱则只能从该链表的头指针开始,顺着各结点的
next 域进行,也就是说找后继的时间性能是 O(1),找前驱的时间性能是 O(n),如果也希
望找前驱的时间性能达到 O(1),则只能付出空间的代价:每个结点再加一个指向前驱的指
针域, 结点的结构为如图所示,用这种结点组成的链表称为双向链表。

双向链表的结点及其类型定义

typedef struct DuLinkList
{ ElemType data ;
struct DuLinkList *prior , *next ;
}DuLinkList ;


双向链表结构具有对称性,设 p 指向双向链表中的某一结点,则其对称性可用下式描述:
(p->prior)->next=p=(p->next)->prior ;
结点 p 的存储位置存放在其直接前趋结点 p->prior 的直接后继指针域中,同时也存放在
其直接后继结点 p->next 的直接前趋指针域中。
(1)双向链表中结点的插入:

① 插入时仅仅指出直接前驱结点,钩链时必须注意先后次序是: “先右后左” 。

S=(DuLinkList *)malloc(sizeof(DuLinkList));
S->data=e;
S->next=p->next; p->next->prior=S;
p->next=S; S->prior=p;/* 钩链次序非常重要 */
② 插入时同时指出直接前驱结点 p 和直接后继结点 q,钩链时无须注意先后次序。
S=(DuLinkList *)malloc(sizeof(DuLinkList));
S->data=e;
p->next=S; S->next=q;
S->prior=p; q->prior=S;


(2)双向链表中结点的删除:

设要删除的结点为 p ,删除时可以不引入新的辅助指针变量,可以直接先断链,再释放
结点。

p->prior->next=p->next;
p->next->prior=p->prior;
free(p);


注意:与单链表的插入和删除操作不同的是,在双向链表中插入和删除必须同时修改两
个方向上的指针域的指向。
(四)静态链表
有时,也可借用一维数组来描述线性链表,其类型说明如下所示:
// - - - - -线性表的静态单链表存储结构- - - - -

#define MAXSlZE 1 000 //链表的最大长度
typedef struct {
Elemtype data, int cur, }component, SLinkList[MAXSlZE];


这种描述方法便于在不设"指针"类型的高级程序设计语言中使用链表结构。在如上描述
的链表中,数组的一个分量表示一个结点,同时用游标(指示器 cur) 代替指针指示结点在数
组中的相对位置。数组的第零分量可看成头结点,其指针域指示链表的第一个结点。

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

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

相关文章

Tomcat原理(5)——tomcat最终实现

目录 一、什么是Servlet容器 二、ServletConfigMapping构建实现容器 ServletConfigMapping MyTomcat 三、优化server Server MyTomcat 四、匹配 代码如下&#xff1a; 测试如下&#xff1a; 上一篇博客已经为介绍了servelet的实现 &#xff0c;这篇对上一篇博客进行补…

echarts 常见组件合集

仪表盘组件 <template><div class"w100 h100" ref"chart"></div> </template><script> import resize from "./mixins/resize"; export default {mixins: [resize],props: ["list"],watch: {list: {// …

C/C++代码性能优化技巧的书籍及资料

使用C/C开发的场景&#xff0c;大多对代码的执行的速度&#xff0c;实时性有较高的要求&#xff0c;像嵌入式系统的开发&#xff0c;资源还受限。在算力存储空间有限的MCU上写出简洁又高效的代码实际是一种艺术。软件工程师在代码设计上的这种差距&#xff0c;会反映在产品的性…

FlightAD 解读

一 文章相关信息 出处&#xff1a;ICPADS CCF C 会议&#xff08;代码未开源&#xff09;&#xff0c;COUTA 研究团队的另一研究 二 Methodology 1. 整体架构&#xff1a; 2. Multi-Scale Sampling&#xff08;多尺度&#xff09; 实际上&#xff0c;就是对每个单通道作 “多…

20241216软考架构-------软考案例23答案

每日打卡题案例23 23.【2015年真题】 难度&#xff1a;一般 阅读以下关于系统设计建模的说明&#xff0c;回答下列问题。&#xff08;共25分&#xff09; 【说明】 某公司拟研制一款高空监视无人直升机&#xff0c;该无人机采用遥控一自主复合型控制实现垂直升降。该直升机飞行…

170页ppt解读如何进行大型集团信息安全管理体系优化咨询

文档为甲方集团信息安全管理体系优化咨询项目的信息安全建设规划报告&#xff0c;重点围绕信息安全建设的规划与设计展开。报告首先进行了信息安全建设需求分析&#xff0c;明确了当前信息安全现况存在的问题、信息安全发展趋势及具体需求汇整&#xff0c;为后续建设提供了坚实…

RK3576 Android14,内存大于4G时UVC应用无法申请内存

最近有个项目需要将Linux虚拟成UVC摄像头&#xff0c;开发过程中遇到一个奇怪的事情&#xff0c;通过V4l2框架接口申请内存时&#xff0c;相同的板子&#xff0c;只是内存一个4G一个8G。4G的内存可以申请成功&#xff0c;8G就不行。提示“内存不足” 内存更大反而内存不足&…

TimesFM(Time Series Foundation Model)时间序列预测股市价格的数据研究(4)

TimesFM&#xff08;Time Series Foundation Model&#xff09;时间序列预测的数据研究(3)-CSDN博客文章浏览阅读846次&#xff0c;点赞19次&#xff0c;收藏12次。1. **表示预测区间**&#xff1a;在很多预测任务中&#xff0c;模型给出的不只是一个单一的预测值&#xff08;比…

opencv所有常见函数

一、opencv图像操作 二、opencv图像的数值运算 三、opencv图像的放射变换 四、opencv空间域图像滤波 五、图像灰度化与直方图 六、形态学图像处理 七、阈值处理与边缘检测 八、轮廓和模式匹配

常见漏洞—SSRF_FastCGI

FastCGI协议 简介 Fast CGI源自旧版本的CGI 路由/结构图 # 访问url --> 浏览器生成HTTP请求报文 --> web server解析请求&#xff08;例如nginx&#xff09; web server 是内容的分发者 当访问静态页面时&#xff0c;web server 会直接返回资源&#xff0c;例如index.htm…

【游戏设计原理】10 - 科斯特的游戏理论

科斯特的游戏理论强调了游戏与学习之间的关系&#xff0c;认为“玩得开心”与“学习”是紧密相连的。换句话说&#xff0c;游戏的核心魅力在于通过适当的挑战和不断的学习进程激发玩家的内啡肽循环&#xff0c;这让玩家在不断的探索和进步中找到乐趣。 科斯特的理论通过游戏是…

ES-IndexTemplate和DynamicTemplate

IndexTemplate 什么是IndexTemplate 索引模板&#xff0c;帮助你设定Mappings和Settings&#xff0c;并按照一定的规则&#xff0c;自动匹配到新创建的索引之上 模板仅在一个索引被新建的时候&#xff0c;才会产生应用&#xff0c;索引被修改不会影响已创建的索引可以设定多…

【大语言模型】ACL2024论文-27 Mementos:一个全面的多模态大型语言模型在图像序列推理上的基准测试

【大语言模型】ACL2024论文-27 Mementos&#xff1a;一个全面的多模态大型语言模型在图像序列推理上的基准测试 目录 文章目录 【大语言模型】ACL2024论文-27 Mementos&#xff1a;一个全面的多模态大型语言模型在图像序列推理上的基准测试目录文章摘要研究背景问题与挑战如何…

CSS基础与应用详解

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Css篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Css篇专栏内容:CSS基础与应用详解 前言 CSS&#xff08;层叠样式表&#xff09;是网页设计中不可或缺的一部分&am…

C/S软件授权注册系统(Winform+WebApi+.NET8+EFCore版)

适用软件&#xff1a;C/S系统、Winform桌面应用软件。 运行平台&#xff1a;Windows .NETCore&#xff0c;.NET8 开发工具&#xff1a;Visual Studio 2022&#xff0c;C#语言 数据库&#xff1a;Microsoft SQLServer 2012&#xff0c;Oracle 21c&#xff0c;MySQL8&#xf…

国标GB28181网页直播平台EasyGBS国标EasyGBD对讲音频demo

近年来&#xff0c;随着信息技术的飞速发展&#xff0c;视频监控领域正经历从传统安防向智能化、网络化安防的深刻转变。在此过程中&#xff0c;GB28181标准凭借其强大的功能和灵活性&#xff0c;成为了推动视频监控系统互联互通和高效管理的重要一环。通过支持GB28181协议&…

session 共享服务器

1.安装 kryo-3.0.3.jar asm-5.2.jar objenesis-2.6.jar reflectasm-1.11.9.jar minlog-1.3.1.jar kryo-serializers-0.45.jar msm-kryo-serializer-2.3.2.jar memcached-session-manager-tc9-2.3.2.jar spymemcached-2.12.3.jar memcached-session-manager-2.3.2.jar …

【蓝桥杯国赛真题15】python质因数个数 蓝桥杯青少年组python编程国赛真题详细解析

目录 python质因数个数 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python质因数个数 第十二届蓝桥杯青少年组python比赛国赛真题详细解析 …

智能硬件「百团大战」:AI驱动的周期来了吗?

要想在竞争激烈的市场中打造出真正的AI硬件“爆款”&#xff0c;并非简单地在现有硬件上堆砌AI功能就能实现&#xff0c;而是需要深刻理解AI的本质&#xff0c;用AI技术从底层逻辑出发&#xff0c;彻底重塑硬件产品的设计、功能与用户体验。 作者|斗斗 编辑|皮爷 出品|产…

Linux核心概念与常用命令

文章目录 一、Linux概述1、常见的操作系统2、Linux发展史3、Linux目录结构 二、文件和目录操作1、pwd - 显示当前目录2、cd - 切换目录3、ls - 列出目录内容4、mkdir - 创建目录5、touch - 创建空文件6、cp - 复制文件或目录7、mv - 移动或重命名文件8、rm - 删除文件或目录9、…