1.1线性表定义
线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列。
线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构,两者属于不同层面,不要混淆。
1.2顺序表
线性表的顺序存储又称顺序表,用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素物理地址上也相邻。
静态分配
define MaxSize 50;
typedef struct{
ElemType data[MaxSize];
int length;
}SqList;
动态分配
define InitSize 100
typedef struct{
ElemType *data;
int MaxsSze,legth;
}sqList;
L.data=(ElemType *)malloc(sizeof( ElemTpye)*InitSize);
动态分配并不是链式存储,她同样属于存储结构,物理结构没有变化,依然是随机存取方式,只是分配的空间大小可以在运行的时候动态决定。
顺序表存储的优点是存储密度大,能随机存取。
顺序表练习题
1.从顺序表中删除具有最小值的元素(假设唯一)并且函数返回被删元素的值。空出位置由最后一个元素填补,若顺序表为空,显示出错误信息并退出运行。
bool DeleteMinNum(SqList &L, int &x){
if(L.length==0)
return false; //表空
int Min=L.data[0] , pos=0;
for(int i=1;i<=L.length;i++){
if(Min>L.data[i])
Min=L.data[i];
pos=i; //标记最小值的位置
}
L.data[pos]=L.data[length-1]; //直接将最后一个元素替代最小值
L.length--;
return true;
}
2.设计一个高效算法,将顺序表L的所有元素逆置,要求算法空间复杂度为O(1)
bool Reverse(SqList &L){
if(L.length==0) return false;
Elemtype temp;
for(int i=0;i<L.length/2;i++){
temp=L.data[i];
L.data[i]=L.data[length-1-i];
L.data[length-1-i]=temp;
}
3.对长度为n的顺序表L,编写一个时间复杂度为 O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有的值为x的数据元素。
bool Del_x(SqList &L,int x){
if(L.length==0) return false;
int i,j=0;
for(i=0;i<L.length;i++){
if(L.data[i] != x){
L.data[j]=L.data[i]; //符合条件的留下
j++;
}
L.length=j;
}
4.从有序顺序表中删除其值在给定值s与t之间(要求s<t)所有元素,若s或t不合理或顺序表为空,则显示出错误信息并退出。
bool Del_StoT(SqList &L,int s,int t){
int i, j;
if(s>=t||L,length==0) return false;
for(i=0;i<length&&L.data[i]<s;i++); //定位在第一个大于或等于s的元素
if(i>=L.length) return false;
for(j=i;j<=L.length&&L.data[j]<=t;j++); //定位在第一个大于或等于t的元素
for( ;j<L.length-1;i++,j++){
L.data[i]=L.data[j];
}
L.length=i;
return true;
}
5.从顺序表中 删除其值在给定值s与t之间(要求s<t)所有元素,若s或t不合理或顺序表为空,则显示出错误信息并退出。
bool Del_StoT(SqList &L,int s,int t){
if(s>=t||L,length==0) return false;
int i,j=0;
for(i=0;i<L.length;i++){
if(L.data[i]<s||L,data[i]>t){
L.data[j]=L.data[i];
j++;
}
L.length=j;
return true;
}
6.从有序顺序表删除所有其值重复的元素,使表中所有的元素的值均不同。
bool Del_Same(SqList &L){
int i,j;
for(i=0,j=1;j<L.length;j++){
if(L.data[i] != L.data[j])
L.data[++i]=L.data[j];
}
L.length=i+1;
return true;
}
7.将两个有序顺序表合并为一个新的有序顺序表
bool Merge(SqList &L, SqList A, SqList B){
if(A.length + B.length >L.length) return false;
int i ,j ,k;
i=j=k=0 ;
while(i<A.length&&j<B.length){
if(A.data[i]<=B.data[j]){
L.data[k++]=A.data[i];
i++;
}
else{
L.data[k++]=B.data[j];
j++;
}
}
while(i<A.length)
L.data[k++]=A.data[i++];
while(j<B.length)
L.data[k++]=B.data[j++];
L.length=k;
return true;
}
8.已知在一维数组A[m+n]中依次存放两个线性表(a1,a2,a3,...,am)和(b1,b2,b3,...,bn)。编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3,...,bn)放在(a1,a2,a3,...,am)的前面。
void Reverse(int A[ ],int left, int right, int ListSize){
if(left >= right || right>=ListSize)
return false;
int mid,temp;
mid=(left+right)/2;
for(int i=0; i<mid-left ; i++){
temp=A[left];
A[left + i]=A[right - i];
A[right - i]=temp;
}
}
void Exchange( int A[ ], int m, int n, int listSize){
Reverse(A,0,m+n-1,listSize);
Reverse(A,0,n-1,listSize);
Reverse(A,n,m+n-1,listSize);
}
9.线性表(a1,a2,a3,...,an)中的元素递增有序且按顺序存储于计算机内,设计一个算法,完成用最少的时间在表中查找数值为x的元素,若找到,则将其与后继元素位置互换,若找不到,使其插入表中元素仍递增有序。
void SearchExchangeInsert( int A[ ], int x){
int low=0, high=A.length-1;
int mid;
while(low<=high){
mid=(low+high)/2;
if(A[mid]>x)
high=mid-1;
else if(A[mid]<x)
left=mid+1;
else if(A[mid]==x)
break;
if(A[mid]==x&&mid<A.length-1){
int temp = A[mid]; A[mid]=A[mid+1]; A[mid+1]=temp;
}
if(low>high){
for(i=A.length-1 ;i>high; i--)
A[i+1]=A[i];
A[i+1]=x;
}
}
10.将R中保存的序列循环左移p(0<p<n)个位置,即由(x0,x1,x2,...,xn)变换成(Xp,Xp+1,...,Xn-1,X0,X1,...Xp-1)。
思路:将问题看成把数组ab变成ba,例如原数组{1,2,3,4,5},设循环左移为2,循环后应得{3,4,5,1,2}:
可以先把原数组拆开{1,2}和{3,4,5};分别逆置成{2,1}和{5,4,3};形成新的数组{2,1,5,4,3},然后再逆置{3,4,5,1,2},就可以了。
void Reserve( int A[ ], int a ,int b){
int mid=(a+b)/2;
for(int i=0; i<mid; i++){
int temp=A[a];
A[a+1]=A[b-i];
A[b-i]=temp;
}
}
void Change( int A[ ],int n ,int p){
Reserve(A,0,p-1);
Reserve(A,p,n-1);
Reserve(A,0,n-1);
}
1.3链式
1.3.1线性表链式存储又称单链表
typedef sturct LNode{
Elemtype data;
struct LNode *next;
}LNode, * LinkList;
通常用 头指针 来标识一个单链表,为了方便操作,在单链表第一个结点之前附加一个结点,称为 头结点。
1.3.2.双链表
typedef struct DNode{
ElemType data;
Struct DNode *prior , *next;
}DNode , *DLinkList;
1.3.3 循环单链表
尾指针r,r->next为头指针。
1.3.4 循环双链表
当循环双链表为空表时,其表头结点的prior和next都指向自己。
1.3.5 静态链表
借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,但这里的指针是结点的相对地址(数组下标),又称游标。和顺序表一样,静态分配也要预先分配一块连续的内存空间。
1.3.6 链表练习
1.设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。
void Del_x(LinkList &L , int x){
if(L==NULL) return 0;
LNode * p ;
if(p->data==x){
p=L;
L=L->next;
free(p);
Del_x(L,x);
}else{
Del_x(L->next,x);
}
}
2.在带头结点的单链表L中,删除所有的值为x的结点,并释放其空间,假设值为x的结点不唯一,试编写算法以实现上诉操作。
bool Del_x(LinList &L, int x){
if(L->next==NULL) retrun false;
LNode *pre=L, *p=L->next , *q;
while(p){
if(p->data==x){
q=p
pre->next=p->next;
p=p->next;
free(q);
} else{
pre=p;
p=p->next;
}
}
return true;
}
3.设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。采用递归
void Ptint_R(LinkList L)[
if(L->next != NULL)
Ptint_R(L->next);
if(L!= NULL) print(L->data);
}
void Reverse(LinkList L){
if(L->next != NULL){
Ptint_R(L->next)
}
}
4.试编写带头结点的单链表L 中删除一个最小值结点的高效算法(最小值唯一)
void DelMinNum(LinkLIst &L ){
if(L==NULL) return 0;
LNode *pre=L , *p=L->next, *pre-Min=L, *q;
q=p;
while(p){
if(q->data>p->data){
q=p;
pre-Min=pre;
}
pre=p;
p=p->next;
}
pre-Min->next=q->next;
free(q);
}
5.编写算法将带头结点的单链表就地逆置。
LinkList ReverseList(LinkList &L){
if(L==NULL) return 0;
LNode *p=L->next , *pre=L , *r=p->next;
p->next=NULL;
while(r){
pre=p;
p=r;
r=r->next;
p->next=pre;
}
L->next=p; //处理最后一个结点
return L;
}
6.有一个带头结点的单链表L,设计一个算法使其元素递增有序。
viod sort(LinkList &L){
LNode *p=L->next,*pre;
LNode *r=p->next;
p->next=NULL;
p=r;
while(q){
pre=L;
r=p->next;
while(pre->next!=NULL&&pre->next->data < p->data)
pre=pre->next;
p->next=pre->next;
pre->next=p;
p=r;
}
}
7.设在一个带表头结点的单链表中所有的元素结点的数据值无序,试编写一个函数,删除表中所有介于给定的两个值(作为函数参数给出)之间的元素的元素。
void Delete(LinkList &L, int s, int t ){
LNode *p=L->next , *pre=L ,*s;
while(p){
if(p->data > s && p->data < t){
s=p;
pre->next=p->next;
p=p->next;
free(s);
} else {
pre=p;
p=p->next;
}
}
}
8.给定两个单链表,编写算法找出两个链表的公共结点。
LinkList Search_List( LinkList L1, LinkList L2){
int dist;
int len1=Length(L1) , len2=Length(L2); //计算两个表长
LinkList longList , shortList;
if(len1>len2){
longList = L1->next;
shortList = L2->next;
dist=len1-len2;
} else {
longList = L2->next;
shortList = L1->next;
dist=len2-len1;
}
while(dist--)
longList=longList->next;
while(longList != NULL){
if(longList == shortList)
return longList;
else{
longList=longList->next;
shortList=shortList->next;
}
}
return NULL;
}
9.给定一个带表头结点的单链表,设head为头结点,试写算法:按递增次序输出单链表中各结点元素并释放结点所占空间。
void Min_Del(LinkList &L){
while(head->next ! = NULL){
LNode *p=head->next, *Min=head , *s;
while(p->next != NULL){
if(p->next->data < Min->next->data)
Min=p;
p=p->next;
}
print(Min->next->data);
s=Min->next;
Min->next=s->next;
free(s);
}
free(head);
}
10.将一个带头结点单链表A分解为两个带头结点的单链表A,B,使得A表存放原表中序号为奇数的元素,B中存放序号为偶数的元素,且保持其相对顺序不变。
LinkList Creat(LinkList &A){
int i=0;
LinkList B = (LinkList ) malloc (sizeof(LNode));
B->next=NULL;
LNode *a=A . *b=B,*p;
p=A->next;
A->next=NULL;
while(p){
i++;
if(i%2 != 0){
a->next=p;
a=p;
}else{
b->next=p;
b=p;
}
p=p->next;
}
a->next=NULL;
b->next=NULL;
return B;
}
11.设C={a1,b1,a2,b2,...,an,bn}为线性表,采用带头结点的点单链表存放,使用一个就地算法,将其拆分为两个线性表,使得A={a1,a2,...,an},B={bn,bn-1,...,b1}
LinkList Creat (LinkList &A){
LinkList B = (LinkList )malloc (sizeof(LNode));
B->next=NULL;
LNode *a=A, *p=A->next, *q;
while(p){
a->next=p;
a=p;
p=p->next;
if(p!=NULL){
q=p->next;
p->next=B->next;
B->next=p; //头插法
p=q;
}
}
a->next=NULL;
return B;
}
12.在一个递增的有序线性表中,有数值相同的元素存在,若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再有重复的元素
viod del_Same(LinkList &L){
LNode * p=L->next, * q;
while(p){
q=p->next;
if(p->data==q->data){
p->next=q->next;
free(q);
}
else
p=p->next;
}
}
13. 假设有两个按元素值递增次序排列的线性表,均以单链表形式存储,请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。
LinkList Merge(LinlList A, LinkList B){
LNode *r;
LNode *p=A->next , *q=B->next;
A->next=NULL;
while(p&&q){
if(p->data<q->data){
r=p->next;
p->next=A->next;
A->next=p;
p=r;
}else{
r=q->next;
q->next=A->next;
A->next=q;
q=r;
}
}
if(p)
q=p;
while(q){
r=q->next;
q->next=A->next;
A->next=q;
q=r;
}
free(B);
return A;
}
14.设A和B是两个单链表(带头结点),其中元素递增有序(无重复),设计一个算法从A和B中公共元素产生单链表C,要求不破坏A,B的结点。
LinkList Same(LinkList A, LinkList B){
LinkList C= (LinkList) malloc(sizeof(LNode));
C->next=NULL;
LNode *p=A->next , *q=B->next , *s ,*r=C;
while(p!=NULL&&q!=NULL){
if(p->data < q->data)
p=p->next;
else if(p->data > q->data)
q=q->next;
else{
s=(LNode *)malloc(sizeof(LNode));
s->data=p->data;
s->next=r->next;
r->next=s;
r=s;
p=p->next;
q=q->next;
}
}
return C;
}
15.已知两个链表A和B分别表示两个集合,其元素递增排列。编写函数,求A与B的交集,并存放在 A链表中。
viod MergeSame(LinkList &A, LinkList &B){
LNode *ra=A , *p=A->next , *q=B->next , *s;
whlle(q&&p){
if(p->data < q->data){
s=q;
p=p->next;
free(s);
}else if(p->data > q->data){
s=q;
q=q->next;
free(s);
else{
ra->next=p;
ra=p;
s=q;
p=p->next;
q=q->next;
free(s);
}
}
ra->next=NULL;
while(p){
s=p;
p=p->next;
free(s);
}
while(q){
s=q;
q=q->next;
free(s);
}
free(B);
}
16.两个整数序列A=a1,a2,a3,...,am,和B=b1,b2,b3,...,bn,已经存入两个单链表中,设计一个算法,判断序列B是否序列A的连续子序列。
bool Pattern(LinkList A, LinkList B){
LNode *p=A->next , *q=B->next , *pre=p;
while(p&&q){
if(p->data != q->data){
pre=pre->next;
p=pre;
q=B->next;
}
else{
p=p->next;
q=q->next;
}
}
if(q==NULL)
return true;
else
return false;
}
17.设计一个算法用于判断带头结点的循环双链表是否对称。
bool symmetry(LinkList L){
LNode *p=L->next , *q=L->prior;
while(p!=q&&q->next != q){
if(p->data==q->data){
p=p->next;
q=q->next;
}else
return false;
}
return true;
}
18.有两个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之后,要求链接后的链表仍保持循环链表形式。
LinkList Merge(LinkList &h1 , LinkList &h2){
LNond *p=h1 , *q=h2 , s=h2->next;
while(p->next != h1)
p=p->next;
while(q->next != h2)
q=q->next;
h2->next=NULL;
p->next=s;
q->next=h1;
free(h2);
return h1;
}
19.设有一个带头结点的循环单链表,其结点值均为正整数,设计一个算法,反复找出单链表中结点值最小的结点输出,然后将该结点删除,直到单链表为空为止,在删除头结点。
viod Delete (LinkList L){
LNode *pre , *p , *q , *minpre;
while(L->next != L){
p=L->next; pre=L; q=p , minpre=L;
while(p!=L){
if(p->data < q->data){
Minpre=pre;
q=p;
}
pre=p;
p=p->next;
}
printf("%d" , q->data);
Minpre->data=q->data;
free(q);
}
free(L);
}
20.设头指针为L的带头结点的非循环双向链表,其每个结点有pre,data,next外,还有一个访问频度域freg。每当在链表中进行一次Locate(L,x),会令元素为x的结点中freg域的增值1,并使此链表中结点保持按访问频度递减排序,同时最近访问结点排在频度相同结点前面。
DLinkList Lcoate(LinkList &L , ElemType x){
LNode *p=L->next, *q;
while(p&&p->next != x)
p=p->next;
if(!p)
exit(0);
else{
p->freg++;
if(p->pre==L || p->freg < p->pre->freg)
return p;
if(p->next != NULL)
p->next->pre = p->pre;
p->pre->next = p->next;
q=p->pre;
while(q != L && q->freg <= q->freg)
q=q->pre;
p->pre=q->next;
q->next->pre = p;
p->pre = q;
q->next = p;
}
return p;
}
21.设计算法,判断单链表的最后一个结点的指针是否指向了链表中某个结点 。
LNode *FindLoopStart(LNode *head){
*LNode *fast=head, *slow=head;
while(fast!=NULL && fast->next != NULL){
slow=slow->next;
fast=fast->next->next;
if(slow==fast) break;
}
if(fast==NULL || fast->next==NULL)
return NULL;
LNode *p1=head, *p2=slow;
whlie(p1!=p2){
p1=p1->next;
p2=p2->next;
}
return p1;
}
22.请设计一个尽可能高效的算法,查找链表中倒数第 k个位置上的结点 (k为正整数)。若查找成功,算法输出该结点的 data域的值,并返回1;否则,只返回0。
typedef int ElemType;
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
int Search_k(LinkList List, int k){
int count = 0;
LNode *p=List->next , *q=list->next;
while(p!=NULL){
if(count<k)
count++;
else
q=q->next;
p=p->next;
}
if(count<k)
return 0;
else {
printf("%d",q->data);
return 1;
}
}
23,假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,可共享相同的后缀存储空间 ,设 strl和 str2分别指向两个单词所在单链表的头结点,请设计一个时间上尽可能高效的算法,找出由 str1 和 str2 所指向两个链表共同后缀的起始位置。
typedef struct Node{
char data;
srtuct LNode *next;
}LinkList;
int ListLen(LinkList *head){
int len=0;
while(head ->next != NULL){
len ++;
head=head->next;
}
return len;
}
LinkList *find_addr(LinkList *str1 , LinkList *str2){
int m,n;
LNode *p,*q;
m=ListLen(str1);
n=ListLen(str2);
for(p=str1;m>n;m--)
p=p->next;
for(q=str2;m<n;n--)
q=q->next;
while(p->next!=NULL&&p->next!=q->next){
p=p->next;
q=q->next;
}
return p->next;
}
24.用单链表保存 m个整数,现要求设计一个时间复杂度尽可能高效的算法,对于链表中 data 的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。
typedef struct Node{
int data;
srtuct LNode *next;
}LNode;
void funC(LNode h,int n){
LNode p=h , r;
int *q,m;
q=(int *)malloc(sizeof(int) *(n+1));
for(int i=0;i<n+1;i++)
*(q+i)=0;
while(p->next != NULL){
m=p->next->data > 0? p->next->data : - p->next->data;
if(*(q+m)==0){
*(q+m)=1;
p=p->next;
}
else{
r=p->next;
p->next=r->next;
free(r);
}
}
free(q);
}
25.设线性表L=(a1,a2,a3,a4,...,an-1,an)采用带头结点的单链表保存,请设计一个空间复杂度为 O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表L=(a1,an,a2,an-1,...)。
①先找出链表 L的中间结点,为此设置两个指针p和q,指针p 每次走一步,指针q 每次走两步,当指针q 到达链尾时,指针p正好在链表的中间结点:②然后将L的后半段结点原地逆置。③从单链表前后两段中依次各取一个结点,按要求重排。
void change_List(LNode *h){
LNode *p, *q, *r, *s;
p=q=h;
while(q->next != NULL){
p=p->next;
q=q->next;
if(q->next != NULL)
q=q->next;
}
q=q->next;
p->next=NULL;
while(q!=NULL){ //后半段结点原地逆置
r=q->next;
q->next = p->next;
p->next=q;
q=r;
}
s=h->next;
q=p->next;
while(q){ //按要求重排
r=q->next;
s->next=q;
s=s->next;
q->next=s;
q=r;
}