数据结构之线性表1

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.mzph.cn/news/890111.shtml

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

相关文章

信息安全实训室网络攻防靶场实战核心平台解决方案

一、引言 网络安全靶场&#xff0c;作为一种融合了虚拟与现实环境的综合性平台&#xff0c;专为基础设施、应用程序及物理系统等目标设计&#xff0c;旨在向系统用户提供全方位的安全服务&#xff0c;涵盖教学、研究、训练及测试等多个维度。随着网络空间对抗态势的日益复杂化…

关于分页的样式问题

在最近写网页的时候遇到了一个关于样式的问题&#xff0c;今天我来跟大家来说一下。像是分页中的颜色效果&#xff0c;斑马纹颜色要注意颜色不要过于深。 这种的颜色就有一点深看着很不舒服&#xff0c;应将当前的颜色改为淡一点的&#xff0c;也可以利用rgba调整透明度&#x…

一分钟快速了解什么是AEO海关认证

一分钟快速了解什么是AEO海关认证——这一术语&#xff0c;对于国际贸易领域的从业者而言&#xff0c;无疑是一个充满分量与价值的标签。AEO&#xff0c;即“Authorized Economic Operator”&#xff0c;中文译为“经认证的经营者”&#xff0c;是海关对信用状况、守法程度和安…

Python图注意力神经网络GAT与蛋白质相互作用数据模型构建、可视化及熵直方图分析...

全文链接&#xff1a;https://tecdat.cn/?p38617 本文聚焦于图注意力网络GAT在蛋白质 - 蛋白质相互作用数据集中的应用。首先介绍了研究背景与目的&#xff0c;阐述了相关概念如归纳设置与转导设置的差异。接着详细描述了数据加载与可视化的过程&#xff0c;包括代码实现与分析…

Java学习笔记(13)——面向对象编程

面向对象基础 目录 面向对象基础 方法重载 练习&#xff1a; 继承 继承树 protected super 阻止继承 向上转型 向下转型 区分继承和组合 练习 小结&#xff1a; 方法重载 如果有一系列方法&#xff0c;功能类似&#xff0c;只是参数有所不同&#xff0c;就可以把…

Facebook 与数字社交的未来走向

随着数字技术的飞速发展&#xff0c;社交平台的角色和形式也在不断演变。作为全球最大社交平台之一&#xff0c;Facebook&#xff08;现Meta&#xff09;在推动数字社交的进程中扮演了至关重要的角色。然而&#xff0c;随着互联网的去中心化趋势和新技术的崛起&#xff0c;Face…

QT:QDEBUG输出重定向和命令行参数QCommandLineParser

qInstallMessageHandler函数简介 QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) qInstallMessageHandler 是 Qt 框架中的一个函数&#xff0c;用于安装一个全局的消息处理函数&#xff0c;以替代默认的消息输出机制。这个函数允许开发者自定义 Qt 应用…

穷举vs暴搜vs深搜vs回溯vs剪枝专题一>全排列II

题目&#xff1a; 解析&#xff1a; 这题设计递归函数&#xff0c;主要把看如何剪枝 代码&#xff1a; class Solution {private List<List<Integer>> ret;private List<Integer> path;private boolean[] check;public List<List<Integer>> p…

Python如何正确解决reCaptcha验证码(9)

前言 本文是该专栏的第73篇,后面会持续分享python爬虫干货知识,记得关注。 我们在处理某些国内外平台项目的时候,相信很多同学或多或少都见过,如下图所示的reCaptcha验证码。 而本文,笔者将重点来介绍在实战项目中,遇到上述中的“reCaptcha验证码”,如何正确去处理并解…

java_零钱通项目

SmallChangeSysOOP.java package com.hspedu.smallchange.oop;import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner;/*** 该类是完成零钱通的各个功能的类* 使用OOP(面向对象编程&#xff09;*/ public class SmallChangeSysOOP {// 定义相关…

Mamba安装环境和使用,anaconda环境打包

什么是mamba Mamba是一个极速版本的conda&#xff0c;它是conda的C重新实现&#xff0c;使用多线程并行处理来加速包和依赖项的下载。 Mamba旨在提高安装、更新和卸载Python包的速度&#xff0c;同时保持与conda相同的兼容性和命令行接口。 Mamba的核心部分使用C实现&#xff…

网络多层的协议详述

网络层 1&#xff09;地址管理&#xff1a;制定一系列的规则&#xff0c;通过地址&#xff0c;在网络上描述出一个设备的位置 2&#xff09;路由选择&#xff1a;网络环境比较复杂&#xff0c;从一个节点到另一个节点&#xff0c;存在很多条不同的路径&#xff0c;需要规划出…

《算法ZUC》题目

判断题 ZUC算法LFSR部分产生的二元序列具有很低的线性复杂度。 A.正确 B.错误 正确答案A 单项选择题 ZUC算法驱动部分LFSR的抽头位置不包括&#xff08; &#xff09;。 A.s15 B.s10 C.s7 D.s0 正确答案C 单项选择题 ZUC算法比特重组BR层主要使用了软件实现友好的…

Flink SQL 从一个SOURCE 写入多个Sink端实例

一. 背景 FLINK 任务从一个数据源读取数据, 写入多个sink端. 二. 官方实例 写入多个Sink语句时&#xff0c;需要以BEGIN STATEMENT SET;开头&#xff0c;以END;结尾。--源表 CREATE TEMPORARY TABLE datagen_source (name VARCHAR,score BIGINT ) WITH (connector datagen …

.vscode配置文件备份

vscode插件 位于&#xff1a;C:\Users\用户名\AppData\Roaming\Code\User\settings.json settings.json {// "C_Cpp.intelliSenseEngine": "default",//智能查找默认值"C_Cpp.intelliSenseEngineFallback": "enabled", //需要添加的…

关于Buildroot如何配置qtwebengine [未能成功编译]

目录 前言 下载Buildroot 如何添加qtwebengine 开始make编译 编译过程中到了这些问题 前言 问题的开始就在于学习QT的过程中遇到了一个问题… Unknown module(s) in QT: webenginewidgets 我想要把qt的一个项目编译并发送到我的开发板上&#xff0c;但是qmake识别不到这…

SNP与Scheer合作助力Warsteiner Brauerei成功升级至SAP S/4HANA

德国软件和咨询公司SNP是SAP环境中数字化转型、自动化数据迁移和数据管理软件的知名提供商&#xff0c;再次与德国Scheer公司合作&#xff0c;Scheer公司是一家专门从事业务流程管理和SAP咨询的咨询公司。他们为家族企业Warsteiner Brauerei Haus Cramer KG向SAP S/4HANA升级转…

【Super Tilemap Editor使用详解】(五):图块调色板

1、图块调色板&#xff08;Tile Palette&#xff09;可以在以下位置找到&#xff1a; Tileset Inspector检视面板 STETilemap Inspector检视面板&#xff0c;并选择 "Paint" 选项卡 Tile Palette 窗口&#xff1a;"SuperTilemapEditor/Window/Tile Palette Win…

LNMP+discuz论坛

0.准备 文章目录 0.准备1.nginx2.mysql2.1 mysql82.2 mysql5.7 3.php4.测试php访问mysql5.部署 Discuz6.其他 yum源&#xff1a; # 没有wget&#xff0c;用这个 # curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo[rootlocalhost ~]#…

TRELLIS,一键生成3D模型,图像转3D,微软开源

大家好&#xff01;今天给大家分享微软最近开源的一个3D模型生成项目——TRELLIS。简单来说就是输入一张图片&#xff0c;它就能自动帮你生成3D模型。这与之前分享的TripoSR项目类似&#xff0c;但是精度和贴图细节比TripoSR要高很多。 好久没关注AI生成3D模型这块&#xff0c;…