第二章 线性表
part1_顺序表
1.从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删元素的值。空出的位置由最后一个元素填补,若顺序表为空,则显示出错信息并退出运行。
bool Del_Min(sqList &L,ElemType &value)
{
if(L.length==0)
return false;
value = L.data[0];
int pos = 0;
for(int i=1;i<L.length;i++)
{
if(L.data[i]<value)
{
value =L.data[i];
pos = i;
}
}
L.data[pos]=L.data[L.length-1];
L.length--;
return true;
}
2.设计一个高效算法,将顺序表 L 的所有元素逆置,要求算法的空间复杂度为 O (1)。
void Reverse(SqList &L)
{
ElemType temp;
for(i=0;i<L.length/2;i++)
{
temp = L.data[i];
L.data[i]=L.data[L.length-i-1]
L.data[L.length-i-1]=temp;
}
}
3.对长度为 n 的顺序表 L ,编写一个时间复杂度为 O ( n )、空间复杂度为 O (1)的算法,该算法删除线性表中所有值为 x 的数据元素。
void Delete_X(SqList &L,ElemType x)
{
int k=0;
for(int i=0;i<L.length;i++)
{
if(L.data[i]==x)
{
L.data[k]=L.data[i];
k++;
}
}
L.length=k;
}
4.从有序顺序表中删除其值在给定值 s 与 t 之间(要求 s < t )的所有元素,若 s 或 t 不合理或顺序表为空,则显示出错信息并退出运行。
bool Delete_S_T(SqList &L,ElemType s,ElemType t)
{
if(s>=t||L.length==0)
return false;
if(s>L.data[L.length-1]||t<L.data[0])
return false;
for(int i=0;i<L.length;i++)
{
if(L.data[i]==s)
{
for(int j=i;j<L.length-1;j++)
{
if(L.data[j]==t)
{
for(j<L.length)
{
L.data[i]=L.data[j+1];
i++;
j++;
}
}
}
}
}
L.length = i;
return true;
}
5.从顺序表中删除其值在给定值 s 与t 之间(包含 s 和 t ,要求 s < t )的所有元素,若 s 或 t 不合理或顺序表为空,则显示出错信息并退出运行。
bool Delete_S_T(SqList &L,ElemType s,ElemType t)
{
if(s>=t||L.length==0)
return false;
int k=0;
for(int i=0;i<L.length;i++)
{
if(L.data[i]<=s||L.data[i]>=t)
{
L.data[k]=L.data[i];
k++
}
}
L.length = k;
return true;
}
6.从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
bool Delete_X(SqList &L)
{
if(L.length==0)
return false;
int k=0;
for(int i=0;i<L.length;i++)
{
if(L.data[i]!=L.data[i+1])
{
L.data[k]=L.data[i];
k++
}
}
L.length = k;
return true;
}
7.将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。
bool Merge(SqList A;SqList B;SqList &C)
{
if(A.length+B.length>C.Maxsize)
return false;
int i=0,j=0,k=0;
while(i<A.length&&j<B.length)
{
if(A.data[i]<B.data[j])
{
C.data[k]=A.data[i]
k++;
i++;
}
else
{
C.data[k]=B.data[j]
k++;
j++;
}
}
while(i<A.length)
{
C.data[k]=A.data[i]
k++;
i++;
}
while(j<B.length)
{
C.data[k]=B.data[j]
k++;
j++;
}
c.length=K;
return true;
}
8.已知在一维数组 A [ m + n ]中依次存放两个线性表( aj ,a2,a3,…, am )和(b1,b2, b3 ,…., bn )。试编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3,…, bn )放在(a1,a2,a3,…, am )的前面。
typedef int DataType;
void Reverse(DataType A[],int left,int right,int arraySize)
{
if(left>=right||right>=arraySize)
return ;
int mid=(left+right)/2;
for(int i=0;i<=mid-left;i++)
{
DataType temp = A[left+1];
A[left+i]=A[right-i];
A[right-i]=temp;
}
}
void Reverse(DataType A[],int m,int n,int arraySize)
{
Reverse(A,0,m+n-1,arraySize);
Reverse(A,0,n-1,arraySize);
Reverse(A,n,m+n-1,arraySize);
}
9线性表(a1,a2,a3,…, an )中的元素递增有序且按顺序存储于计算机内。要求设计一个算法,完成用最少时间在表中查找数值为 x 的元素,若找到,则将其与后继元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。
void SearchExchangeInsert(ElemType A[],ElemType x)
{
int low=0,high=n-1,mid;
while(low<=high)
{
mid=(low+high)/2;
if(A[mid]==x)
break;
else if(A[mid]<x)
low = mid+1;
else if(A[mid]>x)
high = mid-1;
}
if(A[mid]==x&mid!=n-1)
{
t=A[mid];
A[mid]=A[mid+1];
A[mid+1]=t;
}
if(low>high)
{
int i;
for(i = n-1;i>high;i--)
{
A[i+1]=A[i];
}
A[i+1]=x;
}
}
10.【2010统考真题】设将 n ( n >1)个整数存放到一维数组 R 中。设计一个在时间和空间两方面都尽可能高效的算法。将 R 中保存的序列循环左移 p ( O < p < n )个位置,即将 R 中的数据由( X0, X1,…, Xn-1)变换为( Xp , Xp + 1 …, Xn-1,X0,X1,…, Xp-1)。
typedef int DataType;
void Reverse(DataType A[],int left,int right,int arraySize)
{
if(left>=right||right>=arraySize)
return ;
int mid=(left+right)/2;
for(int i=0;i<=mid-left;i++)
{
DataType temp = A[left+1];
A[left+i]=A[right-i];
A[right-i]=temp;
}
}
void Reverse(DataType A[],int n,int p,int arraySize)
{
Reverse(A,0,n-1,arraySize);
Reverse(A,0,p-1,arraySize);
Reverse(A,p,n-1,arraySize);
}
11.【2011统考真题】一个长度为 L ( L ≥1)的升序序列 S ,处在第 L /2个位置的数称为 S 的中位数。例如,若序列 S =(11,13,15,17,19),则 S 的中位数是15,两个序列的中位数是含它们所有元素的升序序列的中位数。例如,若S2=(2,4,6,8,20),则 S 和 S 的中位数是11。现在有两个等长升序序列 A 和 B ,试设计一个在时间和空间两方面都尽可能高效的算法,找出两个序列 A 和 B 的中位数。
int M_Search(int A[],int B[],int n)
{
int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2;
while(s1!=d1||s2!=d2)
{
m1=(s1+d1)/2;
m2=(s2+d2)/2;
if(A[m1]==B[m2])
{
return A[m1];
}
if(A[m1]<B[m2])
{
if((s1+d1)%2==0)
{
s1=m1;
d2=m2;
}
else
{
s1=m1+1;
d2=m2;
}
}
else
{
if((s2+d2)%2==0)
{
d1=m1;
s2=m2;
}
else
{
d1=m1;
s2=m2+1;
}
}
}
return A[s1]<B[s2]?A[s1]:B[s2];
}
12【2013统考真题】已知一个整数序列 A =( ao ,a1,…, an-1),其中0≤ a < n (0≤i< n )。若存在 ap1 = ap2 =…= apm = x 且 m > n /2(0≤ pk < n ,1≤ k ≤ m ),则称 x 为 A 的主元素。例如 A =(0,5,5,3,5,7,5,5),则5为主元素;又如 A =(0,5,5,3,5,1,5,7),则 A 中没有主元素。假设 A 中的 n 个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出 A 的主元素。若存在主元素,则输出该元素;否则输出—1.
int Majority(int A[],int n)
{
int i,c,count=1;
c=A[0];
for(i=1;i<n;i++)
{
if((A[i]==c))
{
count++;
}
else
{
if(count>0)
{
count--;
}
else
{
c=A[i];
count=1;
}
}
}
if(count>0)
{
count = 0;
for(i=0;i<n;i++)
{
if(A[i]==c)
{
count++;
}
}
}
if(count>n/2)
return c;
else
return -1;
}
13.【2018统考真题】给定一个含 n ( n ≥1)个整数的数组,请设计一个在时间上尽可能高效的算法,找出数组中未出现的最小正整数。例如,数组{5,3,2,3}中未出现的最小正整数是1;数组{1,2,3}中未出现的最小正整数是4。
int findMissMin(int A[],int n)
{
int i,*B;
B = (int *)malloc(sizeof(int) *n);
memset(B,0,sizeof(int) *n);
for(i=0;i<n;i++)
{
if(A[i]>0&&A[i]<=n)
{
B[A[i]-1]=1;
}
}
for(i=0;i<n;i++)
{
if(B[i]==0)
break;
}
return i+1;
}
14.【2020统考真题】定义三元组( a , b , c )( a 、b 、c 均为正数)的距离 D = |a — b |+|b— c |+ |c — a|。给定3个非空整数集合 S1 、S2 和 S3 ,按升序分别存储在3个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组( a , b , c )( a∈S1 ,b∈S2,c∈S3)中的最小距离。例如 S1 = { -1,0,9} S 2={-25,-10,10,11}, S 3=,{2,9,17,30,41},则最小距离为2,相应的三元组为(9,10,9)。
#define INT_MAX 0x7fffffff
int abs_(int a)
{
if(a<0)
return -a;
else
return a;
}
bool xls_min(int a,int b,int c)
{
if(a<=b&&a<=c)
return true;
else
return false;
}
int findMinofTrip(int A[],int n,int B[],int m,int C[],int p)
{
int i=0;j=0;k=0;D_min=INT_MAX,D;
while(i<n&&j<m&&k<p&&D_min>0)
{
D=abs_(A[i]-B[j])+abs_(B[j]-C[k])+abs_(C[k]-A[i]);
if(D<D_min)
{
D_min=D;
}
else
{
if(xls_min(A[i],B[j],C[k]))
{
i++;
}
else if(xls_min(B[j],C[k],A[i]))
{
j++;
}
else if(xls_min(C[k],A[i],B[j]))
{
K++;
}
}
}
}
Part2_链表
1设计一个递归算法,删除不带头结点的单链表 L 中所有值为×的结点。
void Del_X(Linklist &L,ElemType x)
{
LNode *p;
if(L==null)
return;
if(L->data==x)
{
p=L;
free(p);
Del_X(L->next,x);
}
else
{
Del_X(L->next,x);
}
}
2.在带头结点的单链表 L 中,删除所有值为×的结点,并释放其空间,假设值为×的结点
不唯一,试编写算法以实现上述操作。
⭐法一:用p从头到尾扫描单链表,pre指向*p结点的前驱。若p所指结点的值为x,则删除,并让p移向像一个结点,否则让pre,p指针同步后移一个结点。
void Del_X(Linklist &L,ElemType x)
{
LNode *pre=L,*p=L->next,*q;
while(p!=null)
{
if(p->data==x)
{
q=p;
p=p->next;
pre->next=p;
free(q);
}
else
{
pre=p;
p=p->next;
}
}
}
⭐法二:采用尾插法建立单链表。用p指针扫描L的所有结点,当其值不为x时,将其链接到L之后,否则将其释放。
void Del_X(Linklist &L,ElemType x)
{
LNode *p=L->next,*re=L,*q;
while(p!=null)
{
if(p->data!=x)
{
r->next = p;
r=p;
p=p->next;
}
else
{
q=p;
p=p->next;
free(q);
}
}
}
3.设 L 为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
void R_Print(Linklist L)
{
if(L->next!=null)
{
R_Print(L->next);
}
else
{
print(L->data);
}
}
4.试编写在带头结点的单链表 L 中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。
Linklist Del_min(Linklist &L)
{
LNode *pre=L,*p = L->next;
LNode *minpre = pre,*minp = p;
while(p!=null)
{
if(p->data<minp-data)
{
minp=p;
minpre=pre;
}
pre=p;
p=p->next;
}
minpre->next=minp->next;
free(minp);
return L;
}
5.试编写算法将带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为 O (1)。
⭐法一:将头结点摘下,然后从第一个结点开始,依次插入到头结点的后面(头插法建立单链表),直到最后一个结点位置。
Linklist Reverse(Linklist L)
{
LNode *p,*r;
p=L->next;
L->next=null;
while(p!=null)
{
r=p->next;
p->next=L->next;
L->next=p;
p=r;
}
return L;
}
⭐法二:增设rear指针,调整rear与pre的指向关系,直接逆置。
Linklist Reverse(Linklist L)
{
LNode *pre,*p=L->next;*r=p->next;
p->next=null;
while(r!=null)
{
pre=p;
p=r;
r=r->next;
p->next=pre;
}
L->next = p;
return L;
}
7.设在一个带表头结点的单链表中所有元素结点的数据值无序,试编写一个函数,删除表中所有介于给定的两个值(作为函数参数给出)之间的元素的元素(若存在)。
void Del_range(LinkList &L,int min,int max)
{
LNode *pre = L;*p=L->next;
while(p!=null)
{
if(p->data>min&&p->data<max)
{
pre->next = p->next;
free(p);
p=pre->next;
}
else
{
pr=p;
p=p->next;
}
}
}
8.给定两个单链表,编写算法找出两个链表的公共结点。
LinkList Search_lst_common(LinkList L1,LinkList L2)
{
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->next)
return longlist;
else
{
longlist=longlist->next;
shortlist=shortlist->next;
}
}
return null;
}
9.给定一个带表头结点的单链表,设 head 为头指针,结点结构为( data , next ), data 为整型元素, next 为指针,试写出算法:按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间(要求:不允许使用数组作为辅助空间)。
void Min_Delete(Linklist &head)
{
while(head->next!=null)
{
LNode *pre=head;
LNode *p=pre->next;
while(p->next!=null)
{
if(p->next->data<pre->next->data)
{
pre = p;
p=p->next;
}
}
print(pre->next->data);
u=pre->next;
pre->next = u->next;
free(u);
}
free(head);
}
10.将一个带头结点的单链表 A 分解为两个带头结点的单链表 A 和 B ,使得 A 表中含有原表中序号为奇数的元素,而 B 表中含有原表中序号为偶数的元素,且保持其相对顺序不变。
LinkList discreat(LinkList &A)
{
int i=0;
B=(LinkList)malloc(sizeof(LNode));
B->next=null;
LNode *ra = A,*rb=B;
p=A->next;
A->next=null;
while(p!=null)
{
i++;
if(i%2==0)
{
rb->next=p;
rb=p;
}
else
{
ra->next=p;
ra=p;
}
p=p->next;
}
ra->next=null;
rb->next=null;
return B;
}
11.设 C = faj ,b1,a2,b2,…, an , bn 为线性表,采用带头结点的 hc 单链表存放,设计一个就地算法,将其拆分为两个线性表,使得 A =fa1,a2,…, an }, B =( bm …,b2, b }。
LinkList discreat(LinkList &A)
{
B=(LinkList)malloc(sizeof(LNode));
B->next=null;
LNode *p=A->next,*q;
LNode *ra = A;
while(p!=null)
{
ra->next=p;
ra=p;
p=p->next;
if(p!=null)
{
q=p->next;
}
p->next = B->next;
B->next = p;
p=q;
}
ra->next=null;
return B;
}
12.在一个递增有序的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再有重复的元素,例如(7,10,10,21,30,42,42,42,51,70)将变为(7,10,21,30,42,51,70)。
void Del_same(LinkList &L)
{
LNode *p=L->next,*q;
if(p==null)
{
return;
}
while(p->next!=null)
{
q=p->next;
if(p->data==Q->dat)
{
p->next=q->next;
free(q);
}
else{
p=p->next;
}
}
}
13.假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。
void MergeList(LinkList &La,LinkList &Lb)
{
LNode *r,*pa=La->next,*pb=Lb->next;
la->null;
while(pa&&pb)
{
if(pa->data<=pb->data)
{
r=pa->next;
pa->next=La->next;
La->next=pa;
pa=r;
}
else
{
r=pb->next;
pb->next=La->next;
La->next=pb;
pb=r;
}
if(pa)
{
pb=pa;
}
while(pb)
{
r=pb->next;
pb->next=La->next;
La->next=pb;
pb=r;
}
free(Lb);
}
}
14.设 A 和 B 是两个单链表(带头结点),其中元素递增有序。设计一个算法从 A 和 B 中的公共元素产生单链表 C ,要求不破坏 A 、 B 的结点。
void Getcommon(LinkList A,LinkList B)
{
LNode *p=A->next,*q=B->next,*r,*s;
LinkList c=(LinkList)malloc(sizeof(LNode));
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;
r->next=s;
r=s;
p=p->next;
q=q->next;
}
}
r->next = null;
}
15.已知两个链表 A 和 B 分别表示两个集合,其元素递增排列。编制函数,求 A 与 B 的交集,并存放于 A 链表中。
void Union(LinkList &la,LinkList &lb)
{
pa=la->next;
pb=lb->next;
pc=la;
while(pa&&pb)
{
if(pa->data==pb->data)
{
pc-next=pa;
pc=pa;
pa=pa->next;
u=pb;
pb=pb->next;
free(u);
else if(pa->data<pb->data)
{
u=pa;
pa=pa->next;
free(u);
}
else
{
u=pb;
pb=pb->next;
free(u);
}
}
while(pa)
{
u=pa;
pa=pa->next;
free(u);
}
pc->next=null;
free(lb);
return la;
}
}
16.两个整数序列 A =a1,a2,a3,…, am 和 B = b3 ,b2, b3 ,…, bn 已经存入两个单链表中,设计一个算法,判断序列 B 是否是序列 A 的连续子序列。
int Pattern(LinkList A,LinkList B)
{
LNode *p=A,*pre=p;*q=B;
while(p&&q)
{
if(p->data==q->data)
{
p=p->next;
q=q->next;
}
else
{
pre=pre->next;
q=pre;
q=B;
}
}
if(q==null)
{
return 1;
}
else
return 0;
}
17.设计一个算法用于判断带头结点的循环双链表是否对称。
int Symmetry(DLinkList L)
{
DNode *p=L->next,*q=L->prior;
while(p!=q&&q->next!=p)
{
if(p->data==q->data)
{
p=p->next;
q=q->prior;
}
else
{
return 0;
}
}
return 1;
}
18.有两个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表
h1之后,要求链接后的链表仍保持循环链表形式。
LinkList Link(LinkList &h1,LinkList &h2)
{
LNode *p,*q;
p=h1;
while(p->next!=h1)
{
p=p->next;
}
q=h2;
while(q->next!=h2)
{
q=q->next;
}
p->next=h2;
q->next=h1;
return h1;
}
19.设有一个带头结点的循环单链表,其结点值均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表空为止,再删除表头结点。
void Del_all(LinkList &L)
{
LNode *p,*pre,*minp,*minpre;
while(L->next!=L)
{
p=L->next;pre=L;
minp=p;minpre=pre;
while(p!=L)
{
if(p->data<minp->data)
{
minp=p;
minpre=pre;
}
pre=p;
p=p->next;
}
printf("%d",minp->data);
minpre->next=minp->next;
free(minp);
}
free(L);
}
20.设头指针为 L 的带有表头结点的非循环双向链表,其每个结点中除有 pred (前驱指针)data (数据)和 next (后继指针)域外,还有一个访问频度域 freq 。在链表被启用前,其值均初始化为零。每当在链表中进行一次 Locate ( L , x )运算时,令元素值为×的结点中 freq 域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的 Locate ( L , X )运算的算法,该运算为函数过程,返回找到
结点的地址,类型为指针型。
DLinkList Locatae(DLinkList &L,ElemType x)
{
DNode *p=L->next,*q;
while(p&&p->data!=x)
{
p=p->next;
}
if(!p)
{
printf("不存在值为x的结点\n");
}
else
{
p->freq++;
if(p->next!=null)
{
p->next-pred=p->pred;
}
p->pred->next=p->next;
q=p->pred;
while(q!=L&&q->freq<=p->freq)
{
q=q->pred;
}
p->next=q->next;
q->next->pred=p;
p->pred=q;
q->next=;
}
return p;
}
21.【2009统考真题】已知一个带有表头结点的单链表,假设该链表只给出了头指针 list。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第 k 个位置上的结点( k 为正整数)。若查找成功,算法输出该结点的 data 域的值,并返回1;否则,只返回0。
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *link;
}Lnode,*LinkList;
int Search-k(LinkList list,int k)
{
int count = 0;
while(p!=null)
{
if(count<k)
{
count++;
}
else
{
q=q->link;
}
p=p->link;
}
if(count<k)
{
return 0;
}
else
{
printf("%d",q->data);
return 1;
}
}
22.【2012统考真题】假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,可共享相同的后缀存储空间,设 strl 和str2分别指向两个单词所在单链表的头结点,链表结点结构为 data next ,请设计一个时间上尽可能高效的算法,找出由str1和str2所指向两个链表共同后缀的起始位置 。
typedef struct Node
{
char data;
struct Node *next;
}SNode;
int listlen(SNode *head)
{
int len =0;
while(head->next!=null)
{
len++;
head=head->next;
}
return len;
}
SNode* find_addr(SNode *str1,SNode *str2)
{
int m,n;
SNode *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;
}
23.【2015统考真题】用单链表保存 m 个整数,结点的结构为【data】【link】,且 |data| ≤ n ( n 为正整数)。现要求设计一个时间复杂度尽可能高效的算法,对于链表中 data 的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。
typedef struct Node
{
char data;
struct Node *linkt;
}Node;
typedef node *pnode
void func(pnode h,int n)
{
pnode p=h,r;
int *q,m'
q=(int *)mallo(sizeof(int)*(n+1));
for(int i=0;i<n+1;i++)
{
*(q+i)=0;
}
while(p->link!null)
{
m=p->link->data>0?p->link->data:-p->link->data;
if(*(q+m)==0)
{
*(q+m)=1;
p=p->link;
}
else
{
r=p->link;
p->link=r->link;
free(r);
}
}
free(q);
}
24.设计一个算法完成以下功能:判断一个链表是否有环,如果有,找出环的入口点并返回,否则返回 NULL 。
LNode* FindLoopStart(LNode *head)
{
LNode *fast=head,*slow=head;
while(slow!=null&&fast->next!=null)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
break;
}
}
if(slow==null||fast->next==null)
{
return null;
}
LNode *p1=head,*p2=slow;
while(p1!=p2)
{
p1=p1->next;
p2=p2->next;
}
return p1;
}
25.【2019统考真题】设线性表 L =( a ,a2, a3 ,…, am —2, an — i , an )采用带头结点的单链表保存,请设计一个空间复杂度为 O (1)且时间上尽可能高效的算法,重新排列 L 中的各结点,得到线性表 L ’=( a , an ,a2, an —1, a3 , an —2)。
void change_list(node *h)
{
node *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=p->next;
p->next=null;
while(q!=null)
{
r=q->next;
q->next=q;
p->next=q;
q=r;
}
s=h->next;
q=p->next;
p->next=null;
while(q!=null)
{
r=q->next;
q->next=s->next;
s->next=q;
s=q->next;
q=r;
}
}
第三章 栈和队列
Part1_栈
1.假设以 I 和0分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由 I 和0组成的序列,可以操作的序列称为合法序列,否则称为非法序列。写出一个算法,判定所给的操作序列是否合法。若合法,返回 true ,否则返回 false (假定被判定的操作序列已存入一维数组中)。
int Judge(char A[])
{
int i=0;
int j=k=0;
while(A[i]!='\0')
{
switch(A[j])
{
case 'I':
j++;
break;
case 'O':
k++;
if(k>j)
{
printf("序列非法\n");exit(0);}
}
i++;
}
if(j!=k)
{
printf("序列非法\n");
return false;
}
else
{
printf("序列合法\n");
return false;
}
}
}
2.设单链表的表头指针为 L ,结点结构由 data 和 next 两个域构成,其中 data 域为字符型。试设计算法判断该链表的全部 n 个字符是否中心对称。例如 xyx 、 Xyyx 都是中对称。
int dc(LinkList L,int n)
{
int i;
char s[n/2];
p=L->next;
for(i=0;i<n/2;i++)
{
s[i]=p->data;
p=p->next;
}
i--;
if(n%2==1)
{
p=p->next;
while(p!=null&&s[i]==p->data)
{
i--;
p=p->next;
}
}
if(i==-1)
{
return 1;
}
else
{
return 0;
}
}
3.设有两个栈s1、s2都采用顺序栈方式,并共享一个存储区[ O ,…, maxsize —1],为了尽量利用空间,减少溢出的可能,可采用栈顶相向、迎面增长的存储方式。试设计s1、s2有关入栈和出栈的操作算法。
#define maxsize 100
#define elemtp int
typedef struct
{
elemtp stack[maxsize];
int top[2];
}stk;
stk s;
//入栈
int push(int i,elemtp x)
{
if(i<0||i>1)
{
printf("栈号输入不对");
exit(0);
}
if(s.top[1]-s.top[0]==1)
{
printf("栈已满\n");
return 0;
}
switch(i)
{
case 0:
s.stack[++s.top[0]]=x;
return 1;
break;
case 1:
s.stack[00s.top[1]]=x;
return 1;
}
}
//出栈
elemtp pop(int i)
{
if(i<0||i>1)
{
printf("栈号输入不对");
exit(0);
}
switch(i)
{
case 0:
if(s.top[0]==-1)
{
printf("栈空\n");
return -1;
}
else
{
return s.stack[s.top[0]--]
}
case 1:
if(s.top[1]==maxsize)
{
printf("栈空\n");
return -1;
}
else
{
return s.stack[s.top[1]++]
}
}
}
Part2_队列
1.若希望循环队列中的元素都能得到利用,则需设置一个标志域 tag ,并以 tag 的值为0或1来区分队头指针 front 和队尾指针 rear 相同时的队列状态是“空”还是“满”。试编写与此结构相应的入队和出队算法。
// 初始化:tag=0、front=rear=0;
//队空条件:Q.front==Q.rear且Q.tag==0;
//队满条件:Q.front==Q.rear且Q.tag==1;
//入队
int EnQueuel(SqQueue &Q,ElemType x)
{
if(Q.front==Q.rear&&Q.tag==1)
return 0;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%Maxsize;
Q.tag=1;
return 1;
}
//出队
int DeQueuel(SqQueue &Q,ElemType x)
{
if(Q.front==Q.rear&&Q.tag==0)
return 0;
x=Q.data[Q.front];
Q.front=(Q.front+1)%Maxsize;
Q.tag=0;
return 1;
}
2.Q是一个队列, S 是一个空栈,实现将队列中的元素逆置的算法。
void Inverse(Stack &S,Queue &Q)
{
while(!QueueEmpty(Q))
{
x=DeQueue(Q);
Push(S,x);
}
while(!StackEmpty(S))
{
Pop(S,x);
EnQueue(Q,x);
}
}
3.利用两个栈S1,S2来模拟一个队列,已知栈的4个运算定义如下:
Push ( S ,×); //元素 x 入栈 S
Pop ( S , x ); // s 出栈并将出栈的值赋给 x
StackEmpty ( S ); //判断栈是否为空
StackOverfloW ( S ); 1/判断栈是否满
如何利用栈的运算来实现该队列的3个运算(形参由读者根据要求自己设计)?
Enqueue ; //将元素 x 入队
Dequeue ; //出队,并将出队元素存储在 x 中
QueueEmpty ; //判断队列是否为空
//入队
int EnQueue(Stack &S1,sTACK &S2,ElemType e)
{
if(!StackOverflow(S1))
{
Push(S1,e);
return 1;
}
if(StackOverflow(S1)&&!StackEmpty(S2))
{
printf("队列满");
return 0;
}
if(StackOverflow(S1)&&StackEmpty(S2))
{
while(!StackEmpty(S1))
{
Pop(S1,x);
Push(S2,x);
}
}
Push(S1,e);
return 1;
}
//出队
void DeQueue(Stack &S1,sTACK &S2,ElemType &x)
{
if(!StackEmpty(S2))
{
Pop(S2,x);
}
else if(StackEmpty(S1))
{
printf("队列为空");
}
else
{
while(!StackEmpty(S1))
{
Pop(S1,x);
Push(S2,x);
}
Pop(S2,x);
}
}
//判空
int QueueEmpty(Stack S1,Stack S2)
{
if(StackEmpty(S1)&&StackEmpty(S2))
return 1;
else
return 0;
}
Part3_栈和队列的应用
1.假设一个算术表达式中包含圆括号、方括号和花括号3种类型的括号,编写一个算法来判别表达式中的括号是否配对,以字符“ \n ”作为算术表达式的结束符。
bool BracketCheck(char *str)
{
InitStack(S);
int i=0;
while(str[i]!='\0')
{
switch(str[i])
{
case '(':Push(S,'(');break;
case '[':Push(S,'[');break;
case '{':Push(S,'{');break;
case ')':Pop(S,e);
if(e!='(') return false;break;
case ']':Pop(S,e);
if(e!='[') return false;break;
case '}':Pop(S,e);
if(e!='{') return false;break;
default:
break;
}
i++;
}
if(!IsEmpty(S))
{
printf("括号不匹配\n");
return false;
}
else
{
printf("括号匹配\n");
return true;
}
}
2按下图所示铁道进行车厢调度(注意,两侧铁道均为单向行驶道,火车调度站有一个用
于调度的“栈道”),火车调度站的入口处有 n 节硬座和软座车厢(分别用 H 和 S 表示)等待调度,试编写算法,输出对这 n 节车厢进行调度的操作(即入栈或出栈操作)序外以使所有的软座车厢都被调整到硬座车厢之前。
void Train_Arrange(char *train)
{
char *p =train,*q=train,c;
stack s;
InitStack(S);
while(*p)
{
if(*p=='H')
Push(s,*p);
else
*(q++)=*p;
p++;
}
while(!StackEmpty(S))
{
Pop(s,c);
*(q++)=c;
}
}
3.利用一个栈实现以下递归函数的非递归计算
double p(int n,double x)
{
struct stack
{
int no;
double val;
}st[MaxSize];
int top=-1,i;
double fvl =1;fv2=2*x;
for(i=n;i>=2;i--)
{
top++;
st[top].no=i;
}
while(top>0)
{
st[top].val=2*x*fv2-2*(st[top].no-1)*fv1;
fv1=fv2;
fv2=st[top].val;
top--;
}
if(n==0)
{
return fv1;
}
return fv2;
}
4.某汽车轮渡口,过江渡船每次能载10辆车过江。过江车辆公为客车类和货车类,上渡船有如下规定:同类车先到先上船;客车先于货车上船,且每上 4辆客车,才允许放上1辆货车;若等待客车不足4辆,则以货车代替。若无货车等待,允许客车都上船,设计一个算法模拟渡口管理。
Queue q;
Queue q1;
Queue q2;
void manager()
{
int i=0,j=0;
while(j<10)
{
if(!QueueEmpty(q1)&&i<4)
{
Dequeue(q1,x);
EnQueue(q,x);
i++;
j++;
}
else if(i==4&&QueueEmpty(q2))
{
Dequeue(q2,x);
EnQueue(q,x);
j++;
i=0;
}
else
{
while(j<10&&i<4&&!QueueEmpty(q2))
{
Dequeue(q2,x);
EnQueue(q,x);
i++;
j++;
}
i=0;
}
if(QueueEmpty(q1)&&QueueEmpty(q2))
{
j=11;
}
}
}
第五章 树与二叉树
1.已知一棵二叉树按顺序存储结构进行存储,设计一个算法,求编号分别为 i 和 j 的两个结点的最近的公共祖先结点的值。
ElemType Comm_Ancestor(SqTree T,int i,int j)
{
if(T[i]!='#'&&T[j]!='#')
{
while(i!=j)
{
if(i>j)
i=i/2;
else
j=j/2;
}
return T[i];
}
}
2.编写后序遍历二叉树的非递归算法。
void PostOrder(BiTree T)
{
InitStack(s);
p=T;
r=null;
while(p||!(IsEmpty(S)))
{
if(p)
{
push(S,p);
p=p->lchild;
}
else
{
GetTop(S,p);
if(p->rchild&&p->child!=r)
{
p=p->child;
}
else
{
pop(S,p);
visit(p->data);
r=p;
p=null;
}
}
}
}
3.试给出二叉树的自下而上、从右到左的层次遍历算法。
void InvertLevel(BiTree bt)
{
if(bt1=null)
{
InitStack(s);
InitQueue(Q);
EnQueue(Q,bt);
while(IsEmpty
while(IsEmpty(Q)==false)
{
DeQueue(Q,p);
Push(s,p);
if(p->child)
{
EnQueue(Q,p->lchild);
}
if(p->rchild)
{
EnQueue(Q,p->rchild)
}
}
while(IsEmpty(s)==false)
{
Pop(s,p);
visit(p->data);
}
}
}
4.假设二叉树采用二叉链表存储结构,设计一个非递归算法求二叉树的高度。
int Btdepth(BiTree T)
{
if(!T)
return 0;
int front=-1,rear=-1;
int last=0,level=0;
BiTree Q[Maxsize];
Q[++rear]=T;
BiTree p;
while(front<rear)
{
p=Q[++front];
if(p->lchild)
Q[++rear]=p->lchild;
if(p->rchild)
Q[++rear]=p->rchild;
if(front==last)
{
level++;
last=rear;
}
}
return level;
}
5.设一棵二叉树中各结点的值互不相同,其先序遍历序列和中序遍历序列分别存于两个一维数组 A [1…n]和 B [1…n]中,试编写算法建立该二叉树的二叉链表。
BiTree PreInCreat(ElemType A[],ElemType B[],int l1,int h2,int l2,int h2)
{
root(BiTNode*)malloc(sizeof(BiTNode));
root->data=A[l1];
for(i=l2;B[i]!=root->data;i++);
llen=i-l2;
rlen=h2-i;
if(llen)
{
root->lchild=PreInCreat(A,B,l1+1,l1+llen,l2,l2+llen-1);
}
else
{
root->lchild=null;
}
if(rlen)
{
root->rchild=PreInCreat(A,B,h1-rlen+1,h1,h2-rlen+1,h2);
}
else
{
root->rchild=null;
}
return root;
}
6.二叉树按二叉链表形式存储,写一个判别给定二叉树是否是完全二叉树的算法。
bool IsComplete(BiTree T)
{
InitQueue(Q);
if (!T)
{
return 1;
}
EnQueue(Q,T);
while (!IsEmpty(Q))
{
DeQueue(Q,p);
if(p)
{
EnQueue(Q,p->lchild);
EnQueue(Q,p->rchild);
}
else
{
while(!IsEmpty(Q))
{
DeQueue(Q,p)
if(p)
return 0;
}
}
}
return 1;
}
7.假设二叉树采用二叉链表存储结构存储,试设计一个算法,计算一棵给定二叉树的所有双分支结点个数。
int DsonNodes(BiTree b)
{
if (b == NULL)
{
return 0;
}
else if (b->lchild != NULL && b->rchild != NULL)
return DsonNodes(b->lchild) + DsonNodes(b->rchild) + 1;
else return DsonNodes(b->lchild) + DsonNodes(b->rchild);
}
8.设树 B 是一棵采用链式结构存储的二叉树,编写一个把树 B 中所有结点的左、右子树进行交换的函数。
void swap(BiTree b)
{
if (b) {
swap(b->lchild);
swap(b->rchild);
BiTree temp = b->lchild;
b->lchild = b->rchild;
b->rchild = temp;
}
}
9.假设二叉树采用二叉链存储结构存储,设计一个算法,求先序遍历序列中第 k (1≤ k ≤二叉树中结点个数)个结点的值。
int i = 1;
ElemType PreNode(BiTree b, int k)
{
if (b == NULL)
{
return '#';
}
if (i == k)
{
return b->data;
}
i++;
ch = PreNode(b->lchild, k);
if (ch != '#')
{
return ch;
}
ch = PreNode(b->rchild, k);
return ch;
}
10.已知二叉树以二叉链表存储,编写算法完成:对于树中每个元素值为×的结点,删去以它为根的子树,并释放相应的空间。
void DeleteXTree(BiTree &bt)
{
if (bt) {
DeleteXTree(bt->lchild);
DeleteXTree(bt->rchild);
free(bt);
}
}
void Search(BiTree bt, ElemType x)
{
BiTree Q[];
if (bt) {
if (bt->data == x) {
DeleteXTree(bt);
exit(0);
}
Init Queue(Q);
EnQueue(Q,bt);
while (!IsEmpty(Q)) {
DeQueue(Q,p);
if (p->lchild) {
if (p->lchild->data == x) {
DeleteXTree(p->lchild);
p->lchild = NULL;
} else {
EnQueue(p->lchild);
}
}
if (p->rchild) {
if (p->rchild->data == x) {
DeleteXTree(p->rchild);
p->rchild = NULL;
} else {
EnQueue(p->rchild);
}
}
}
}
}
11.在二叉树中查找值为 x 的结点,试编写算法(用 C 语言)打印值为×的结点的所有祖
先,假设值为 x 的结点不多于一个。
typedef struct
{
BiTree t;
int tag;
}stack;
void Search(BiTree T, ElemType x)
{
stack s[];
top=0;
while (bt!=null || top>0 )
{
while(bt!=null&&bt->data!=x)
{
s[++top].t=bt;
s[top].tag=0;
bt=bt->lchild;
}
if(bt->data==x)
{
printf("所查结点的所有祖先结点的值为:\n");
for(i=1;i<=top;i++)
{
printf("%d",s[i].t->data);
exit(1);
}
}
while(top!=0&&s[top].tag==1)
{
top--;
}
if(top!=0)
{
s[top].tag=1;
bt=s[top].t->rchild;
}
}
}
12.设一棵二叉树的结点结构为( LLINK , INFO , RLINK ), ROOT 为指向该二叉树根结点的指针, p 和 q 分别为指向该二叉树中任意两个结点的指针,试编写算法 ANCESTOR ( ROo Т, P , q , r ),找到 p 和 q 的最近公共祖先结点 r 。
typedef struct
{
BiTree t;
int tag;
}stack;
stack s[],s1[];
BiTree Ancestor(BiTree ROOT,BiNode *p,BiTNode *q)
{
top=0;bt=ROOT;
while(bt!=null||top>0)
{
while(bt!=null)
{
s[++top].t=bt;
s[top].tag=0;
bt=bt->lchild;
}
while(top!=0&&s[top].tag==1)
{
if(s[top].t==p)
{
for(i=1;i<=top;i++)
{
s1[i]=s[i];
top1=top;
}
}
if(s[top].t==q)
{
for(i=top;i>0;i--)
{
for(j=top1;j>0;j--)
{
if(s1[j].t==s[i].t)
return s[i].t
}
}
top--;
}
if(top!=)
{
s[top].tag=1;
bt=s[top].t->rchild;
}
}
return null;
}
}
13.假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树 b 的宽度(即具有结点数最多的那一层的结点个数)。
typedef struct {
BiTree data[MaxSize];
int level[MaxSize];
int front, rear;
} Qu;
Qu q;
int BTWidth(BiTree b)
{
BiTree p;
int k, maxn, i, n;
q.front = q.rear = -1;
q.rear++;
q.data[q.rear] = b;
q.level[q.rear] = 1;
while (q.front < q.rear) {
q.front++;
p = q.data[q.front];
k = q.level[q.front];
if (p->lchild != NULL) {
q.rear++;
q.data[q.rear] = p->lchild;
q.level[q.rear] = k + 1;
}
if (p->rchild != NULL) {
q.rear++;
q.data[q.rear] = p->rchild;
q.level[q.rear] = k + 1;
}
}
maxn = 0; i = 0;
k = 1;
while (i <= q.rear) {
n = 0;
while (i <= q.rear && q.level[i] == k) {
n++;
i++;
}
k = q.level[i];
if (n > maxn) maxn = n;
}
return maxn;
}
14.设有一棵满二叉树(所有结点值均不同),已知其先序序列为 pre ,设计一个算法求其后序序列 post 。
void PreToPost(char pre[], int l1, int r1, char post[], int l2, int r2)
{
int mid;
if (r1 >= l1) {
post[r2] = pre[l1];
mid = (r1 - l1) / 2;
PreToPost(pre, l1 + 1, l1 + mid, post, l2, l2 + mid - 1);
PreToPost(pre, l1 + mid + 1, r1, post, l2 + mid, r2 - 1);
}
}
15.设计一个算法将二叉树的叶结点按从左到右的顺序连成一个单链表,表头指针为 head 。二叉树按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表指针。
LinkedList head, pre = NULL;
LinkedList InOrder(BiTree bt)
{
if (bt) {
InOrder(bt->lchild);
if (bt->lchild == NULL && bt->rchild == NULL) {
if (pre == NULL) {
head = bt;
pre = bt;
} else {
pre->rchild = bt;
pre = bt;
}
}
InOrder(bt->rchild);
pre->rchild = NULL;
}
return head;
}
17.试设计判断两棵二叉树是否相似的算法。所谓二叉树 T 和T2相似,指的是 T 和T2都是空的二叉树或都只有一个根结点;或 T 的左子树和T2的左子树是相似的,且 T 的右子树和T2的右子树是相似的。
int similar(BiTree T1, BiTree T2)
{
int leftS, rightS;
if (T1 == NULL && T2 == NULL) return 1;
else if (T1 == NULL || T2 == NULL) return 0;
else {
leftS = similar(T1->lchild, T2->lchild);
rightS = similar(T1->rchild, T2->rchild);
return leftS && rightS;
}
}
18.写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法。
ThreadTree InPostPre(ThreadTree t, ThreadTree p)
{
ThreadTree q;
if (p->rtag == 0) q = p->rchild;
else if (p->ltag == 0) q = p->lchild;
else if (p->lchild == NULL) q = NULL;
else {
while (p->ltag == 1 && p->lchild != NULL) p = p->lchild;
if (p->ltag == 0) q = p->lchild;
else q = NULL;
}
return q;
}
19.【2014统考真题】二叉树的带权路径长度( WPL )是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树 T ,采用二叉链表存储,结点结构为
left weight right 其中叶结点的 weight 域保存该结点的非负权值。设 root 为指向 T 的根结点的指针,请设计求 T 的 WPL 的算法
int wpl_PreOrder(BiTree root, int deep)
{
static int wpl = 0;
if (root->left == NULL && root->right == NULL)
wpl += deep * root->weight;
if (root->left != NULL)
wpl_PreOrder(root->left, deep + 1);
if (root->right != NULL)
wpl_PreOrder(root->right, deep + 1);
return wpl;
}
20.【2017统考真题】请设计一个算法,将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。
void BtreeToE(BTree *root)
{
BtreeToExp(root,1);
}
void BtreeToExp(BTree root, int deep)
{
if (root == NULL) return;
else if (root->left == NULL && root->right == NULL)
cout << root->data;
else {
if (deep > 1) cout << "(";
BtreeToExp(root->left, deep + 1);
cout << root->data;
BtreeToExp(root->right, deep + 1);
if (deep > 1) cout << ")";
}
}
21.编程求以孩子兄弟表示法存储的森林的叶子结点数。
typedef struct node
{
ElemType data;
struct node *fch,*nsib;
}*Tree;
int Leaves(Tree t)
{
if(t=null) return 0;
if(t->fch==bull)
{
return 1+Leaves(t->nsib);
}
else
{
return Leaves(t->fch)+Leaves(t->nsib);
}
}
22.以孩子兄弟链表为存储结构,请设计递归算法求树的深度。
int Height(CSTree bt)
{
int hc.hs;
if(bt==null) return 0;
else
{
hc=height(bt->firstchild);
hs=height(bt->nextsibling);
if(hc+1>hs) return hc+1;
else return hs;
}
}
23.已知一棵树的层次序列及每个结点的度,编写算法构造此树的孩子—兄弟链表。
#define maxNode 15
void creatCSTree_Degree(CSTree &T,DataType e[],int degree[],int n)
{
CSNode *point=new CSNode[maxNode];
int i,j,d,k=0;e
for(i=0;i<n;i++)
{
d=degree[i];
if(d)
{
k++;
pointer[i]->child=pointer[k];
for(j=2;j<=d;j++)
{
k++;pointer[k-1]->rsibling=pointer[k];
}
}
}
T=pointer[0];
delete []pointer;
}
24.试编写一个算法 判断给定的二叉树是否是二叉排序树。
keyType predt=-32767;
int JudgeBST(BiTree bt)
{
int b1,b2;
if(bt==null)
{
return 1;
}
else
{
b1=JudgeBST(bt->lchild);
if(b1==0||predt>=bt->data) return 0;
predt=bt->data;
b2=JudgeBST(bt->rchild);
return b2;
}
}
25.设计一个算法,求出指定结点在给定二叉排序树中的层次。
int level(BiTree bt, BSTNode *p)
{
int n = 0;
BiTree t=bt;
if(bt!=null)
{
n++;
while(t->data!=p->data)
{
if(p->data<t->data)
{
t=t->lchild;
}
else
{
t=t->rchild;
}
n++;
}
}
return n;
}
26.利用二叉树遍历的思想编写一个判断二叉树是否是平衡二叉树的算法。
void Judge_AVL(BiTree bt, int &balance, int &h)
{
int bl = 0, br = 0, hl = 0, hr = 0;
if (bt == NULL) {
h = 0;
balance = 1;
} else if (bt->lchild == NULL && bt->rchild == NULL) {
h = 1;
balance = 1;
} else {
Judge_AVL(bt->lchild, bl, hl);
Judge_AVL(bt->rchild, br, hr);
h = (hl > hr ? hl : hr) + 1;
if (abs(hl - hr) < 2) balance = bl && br;
else balance = 0;
}
}
27.设计一个算法,求出给定二叉排序树中最小和最大的关键宇。
KeyType MinKey(BSTNode *bt)
{
while(bt->lchild!=null)
{
bt=bt->lchild;
}
return bt->data;
}
KeyType MaxKey(BSTNode *bt)
{
while(bt->rchild!=null)
{
bt=bt->rchild;
}
return bt->data;
}
28.设计一个算法,从大到小输 二叉排序树中所有值不小于 k 的关键字。
void OutPut(BSTNode *bt,KeyType k)
{
if(bt==null)
return;
if(bt->rchild!null)
OutPut(bt->rchild,k);
if(bt->data>=k)
printf("%d",bt->data);
if(bt->lchild!=null)
OutPut(bt->lchild,k);
}
29.编写一个递归算法 在一棵有 n 个结点的、随机建立起来的二叉排序树上查找第 k ( 1≤ k ≤ n )小的元素,并返回指 结点的指针。要求算法的平均时间复杂度为 O ( logzn )。二叉排序树的每个结点中除 data , lchild , rchild 等数据成员外,增加一个 count 成员,保存以该结点为根的子树上的结点个数。
BSTNode *Search_Small(BSTNode *t,int k)
{
if(k<1||k>t->count) return null;
if(t->lchild==null)
{
if(k==1) return t;
else return Search_Small(t->rchild,k-1);
}
else
{
if(t->lchild->count==k-1) return t;
if(t->lchild->count>k-1) return Search_Small(t->lchild,k);
if(t->lchild->count<k-1) return Search_Small(t-rchild,k-(t->lchild->count+1));
}
}
第六章 图
1.写出从图的邻接表表示转换成邻接矩阵表示的算法。
void Convert(ALGraph &G,int arcs[M][N])
{
for(i=0;i<n;i++)
{
p=(G->v[i]).firstarc;
while(p!=null)
{
arcs[i][p->data]=1;
p=p->nextarc;
}
}
}
2.试设计一个算法,判断一个无向图 G 是否为一棵树。若是一棵树,则算法返回 true ,
否则返回 false 。
bool isTree(Graph& G)
{
for(i=1;i<G.vexnum;i++)
{
visited[i]=FALSE;
}
int Vnum=0,Enum=0;
DFS(G,1,Vnum,Enum,visited);
if(Vnum==G.vexnum&&Enum==2*(G.vexnum-1))
return true;
else
return FALSE;
}
void DFS(Graph &G,int v,int &Vnum,int &Enum,int visited[])
{
visited[v]=true;Vnum++;
int w=FirsrNeighbor(G,v);
while(w!=-1)
{
Enum++;
if(!visited[w])
DFS(G,w,Vnum,Enum,visited);
w=NextNeighbor(G,v,w);
}
}
3.写出图的深度优先搜索 DFS 算法的非递归算法(图采用邻接表形式)
void DFS_Non_RC(AGraph &G,int v)
{
int w;
InitStack(s);
for(i=0;i<G.vexnum;i++)
{
visited[i]=FALSE;
}
Push(S,v);visited[v]=TRUE;
while(!IsEmpty(s))
{
k=Pop(S);
visited(k);
for(w=FirstNeighbor(G,k);w>=0;w=NextNeighbor(G,k,w))
{
if(!visited[w])
{
Push(S,w);
visited[w]=TRUE;
}
}
}
}
4.分别采用基于深度优先遍历和广度优先遍历算法判别以邻接表方式存储的有向图中是否存在由顶点 y 到顶点 y 的路径( i ≠ j )。注意,算法中涉及的图的基本操作必须在此存储结构上实现。
//深度遍历
int visited[MaxSize]={0};
void DFS(ALGraph G,int i,int j,bool &can_reach)
{
if(i==j)
{
can_reach=true;
return;
}
visited[i]=1;
for(int p=FirstNeighbor(G,i);p>0;p=NextNeighbor(G,i,p))
{
if(!visited[p]&&!can_reach)
DFS(G,p,j,can_reach);
}
}
//广度遍历
int visited[MaxSize]={0}
int BFS(ALGraph G,int i,int j)
{
InitQueue(Q);EnQueue(Q,i);
while(!isEmpty(Q))
{
DeQueue(Q,u);
visited[u]=1;
if(u==j) return 1;
for(int p=FirstNeighbor(G,i);p;p=NextNeighbor(G,u,p))
{
if(p==j)
return 1;
if(!visited[p])
{
EnQueue(Q,p);
visited[p]=1;
}
}
}
}
5.假设图用邻接表表示,设计一个算法,输出从顶点 V 到顶点 V 的所有简单路径。
void FindPath(AGraph *G,int u,int v,int path[],int d)
{
int w,i;
ArcNode *p;
d++;
path[d]=u;
visited[u]=1;
if(u==v)
{
print(path[]);
}
p=G->adjlist[u].firstarc;
while(p!=null)
{
w=p->adjvex;
if(visited[w]==0)
{
FindPath(G,w,V,path,d);
}
p=p->nextarc;
}
visited[u]=0;
}
6.试说明利用 DFS 如何实现有向无环图拓扑排序。
bool visited[Max_VERTEX_NUM];
void DFSTraverse(Graph G)
{
for(v=0;v<G.vexnum;++v)
{
visited[v]=FALSE;
}
time=0;
for(v=0;v<G.vexnum;++v)
{
if(!visited[v]) DFS(G,v);
}
}
void DFS(Graph G,int v)
{
visited[v]=true;
visit(v);
for(w=FirstNeighbor(G,v);w>=;w=NextNeighbor(G,v,w))
{
if(!visited[w])
{
DFS(G,w);
}
}
time=time+1;finishIme[v]=time;
}
7.一连通无向图,边非负权值,问用 Dijkstra 最短路径算法能否给出一棵生成树,该树是
否一定是最小生成树?说明理由。
bool closed[N]={false};
int Min[N]={INF};
closed[start]=true;Min[start]=0;
for(int i=1;i<N;i++)
{
int k=-1;
for(int j=0;j<N;j++)
{
if(!closed[j]&&(k==-1||Min[k]>Min[j])) k=j;
}
closed[k]=true;
for(int j=0;j<N;j++)
{
//Dijkstra算法
if(Min[j]>Min[k]+G[k][j])
Min[j]=Min[k]+G[k][j];
//Prim算法
if(Min[j]>G[k][j])
Min[j]=G[k][j];
}
}