9.1
(1)无序表:顺序查找不成功时,查找长度为n+1;成功时,平均查找长度为1/(n+1)*(1+2+…+(n+1))=(n+2)/2;两者不相同。
(2)表中只有一个关键字等于给定值k的记录,无序表、有序表:顺序查找成功时,平均查找长度均为1/(n)*(1+2+…+n)=(n+1)/2;两者相同。
(3)表中只有m个关键字等于给定值k的记录,无序表:ASL=n+1;有序表:ASL=(n+1)/2+m;两者不相同。
9.3
ASL=1/10(1+2*2+4*3+3*4)=2.9
9.11
9.14
删除50后
删除68后
9.19
22
67
41
30

53
46

13

01
0
1
2
3
4
5
6
7
8
9
10

ASL=(4*1+2*2+3+6)/8=17/8
9.25
int Search-Seq(SSTable ST,KeyType key){
//在顺序表ST中顺序查找其关键字等于key的数据元素,ST按关键字自大至小有序,
//若找到,则函数值为该元素在表中的位置,否则为0
ST.elem[ST.length+1].key=key;
for (i=1; ST.elem[i].key>key; ++i);
if (ST.elem[i].key==key)&&(i<=ST.length) return i
else return 0 ;
}//Search-Seq
9.31
TelemType Maxv(Bitree T){
//返回二叉排序树T中所有结点的最大值
for (p=T; p->rchild; p=p->rchild);
return p->data;
}//Maxv
TelemType Minv(Bitree T){
//返回二叉排序树T中所有结点的最小值
for (p=T; p->lchild; p=p->lchild);
return p->data;
}//Minv
Status IsBST(Bitree T){
//判别T是否为二叉排序树
if (!T) return OK;
else if ((!T->lchild)||((T->lchild)&&(IsBST(T->lchild)&&(Maxv(T->lchild)<T->data)))
&&((!T->rchild)||((T->rchild)&&(IsBST(T->rchild)&&(Minv(T->rchild)>T->data)))
return OK
else return ERROR;
}//IsBST
9.33
Status OutputGEx(Bitree T,TelemType x){
//从大到小输出给定二叉排序树T中所有值不小于x的数据元素
if (T) {
if (OutputGEx(T->rchild,x))
if (T->data>=x) {
print(T->data);
if (OutputGEx(T->lchild,x)) return OK;
}
else return OK;
}
else return OK;
}//OutputGEx
第九章 查找
9.25
int Search_Sq(SSTable ST,int key)//在有序表上顺序查找的算法,监视哨设在高下标端
{
ST.elem[ST.length+1].key=key;
for(i=1;ST.elem[i].key>key;i++);
if(i>ST.length||ST.elem[i].key<key) return ERROR;
return i;
}//Search_Sq
分析:本算法查找成功情况下的平均查找长度为ST.length/2,不成功情况下为ST.length,
9.26
int Search_Bin_Digui(SSTable ST,int key,int low,int high)//折半查找的递归算法
{
if(low>high) return 0; //查找不到时返回0
mid=(low+high)/2;
if(ST.elem[mid].key==key) return mid;
else if(ST.elem[mid].key>key)
return Search_Bin_Digui(ST,key,low,mid-1);
else return Search_Bin_Digui(ST,key,mid+1,high);
}
}//Search_Bin_Digui
9.27
int Locate_Bin(SSTable ST,int key)//折半查找,返回小于或等于待查元素的最后一个结点号
{
int *r;
r=ST.elem;
if(key<r.key) return 0;
else if(key>=r[ST.length].key) return ST.length;
low=1;high=ST.length;
while(low<=high)
{
mid=(low+high)/2;
if(key>=r[mid].key&&key<r[mid+1].key) //查找结束的条件
return mid;
else if(key<r[mid].key) high=mid;
else low=mid;
} //本算法不存在查找失败的情况,不需要return 0;
}//Locate_Bin
9.28
typedef struct {
int maxkey;
int firstloc;
} Index;
typedef struct {
int *elem;
int length;
Index idx[MAXBLOCK]; //每块起始位置和最大元素,其中idx[ 0 ]不利用,其内容初始化为{0,0}以利于折半查找
int blknum; //块的数目
} IdxSqList; //索引顺序表类型
int Search_IdxSeq(IdxSqList L,int key)//分块查找,用折半查找法确定记录所在块,块内采用顺序查找法
{
if(key>L.idx[L.blknum].maxkey) return ERROR; //超过最大元素
low=1;high=L.blknum;
found=0;
while(low<=high&&!found) //折半查找记录所在块号mid
{
mid=(low+high)/2;
if(key<=L.idx[mid].maxkey&&key>L.idx[mid-1].maxkey)
found=1;
else if(key>L.idx[mid].maxkey)
low=mid+1;
else high=mid-1;
}
i=L.idx[mid].firstloc; //块的下界
j=i+blksize-1; //块的上界
temp=L.elem[i-1]; //保存相邻元素
L.elem[i-1]=key; //设置监视哨
for(k=j;L.elem[k]!=key;k--); //顺序查找
L.elem[i-1]=temp; //恢复元素
if(k<i) return ERROR; //未找到
return k;
}//Search_IdxSeq
分析:在块内进行顺序查找时,如果需要设置监视哨,则必须先保存相邻块的相邻元素,以免数据丢失,
9.29
typedef struct {
LNode *h; //h指向最小元素
LNode *t; //t指向上次查找的结点
} CSList;
LNode *Search_CSList(CSList &L,int key)//在有序单循环链表存储结构上的查找算法,假定每次查找都成功
{
if(L.t->data==key) return L.t;
else if(L.t->data>key)
for(p=L.h,i=1;p->data!=key;p=p->next,i++);
else
for(p=L.t,i=L.tpos;p->data!=key;p=p->next,i++);
L.t=p; //更新t指针
return p;
}//Search_CSList
分析:由于题目中假定每次查找都是成功的,所以本算法中没有关于查找失败的处理.由微积分可得,在等概率情况下,平均查找长度约为n/3,
9.30
typedef struct {
DLNode *pre;
int data;
DLNode *next;
} DLNode;
typedef struct {
DLNode *sp;
int length;
} DSList; //供查找的双向循环链表类型
DLNode *Search_DSList(DSList &L,int key)//在有序双向循环链表存储结构上的查找算法,假定每次查找都成功
{
p=L.sp;
if(p->data>key)
{
while(p->data>key) p=p->pre;
L.sp=p;
}
else if(p->data<key)
{
while(p->data<key) p=p->next;
L.sp=p;
}
return p;
}//Search_DSList
分析:本题的平均查找长度与上一题相同,也是n/3,
9.31
int last=0,flag=1;
int Is_BSTree(Bitree T)//判断二叉树T是否二叉排序树,是则返回1,否则返回0
{
if(T->lchild&&flag) Is_BSTree(T->lchild);
if(T->data<last) flag=0; //与其中序前驱相比较
last=T->data;
if(T->rchild&&flag) Is_BSTree(T->rchild);
return flag;
}//Is_BSTree
9.32
int last=0;
void MaxLT_MinGT(BiTree T,int x)//找到二叉排序树T中小于x的最大元素和大于x的最小元素
{
if(T->lchild) MaxLT_MinGT(T->lchild,x); //本算法仍是借助中序遍历来实现
if(last<x&&T->data>=x) //找到了小于x的最大元素
printf("a=%d\n",last);
if(last<=x&&T->data>x) //找到了大于x的最小元素
printf("b=%d\n",T->data);
last=T->data;
if(T->rchild) MaxLT_MinGT(T->rchild,x);
}//MaxLT_MinGT
9.33
void Print_NLT(BiTree T,int x)//从大到小输出二叉排序树T中所有不小于x的元素
{
if(T->rchild) Print_NLT(T->rchild,x);
if(T->data<x) exit(); //当遇到小于x的元素时立即结束运行
printf("%d\n",T->data);
if(T->lchild) Print_NLT(T->lchild,x); //先右后左的中序遍历
}//Print_NLT
9.34
void Delete_NLT(BiTree &T,int x)//删除二叉排序树T中所有不小于x元素结点,并释放空间
{
if(T->rchild) Delete_NLT(T->rchild,x);
if(T->data<x) exit(); //当遇到小于x的元素时立即结束运行
q=T;
T=T->lchild;
free(q); //如果树根不小于x,则删除树根,并以左子树的根作为新的树根
if(T) Delete_NLT(T,x); //继续在左子树中执行算法
}//Delete_NLT
9.35
void Print_Between(BiThrTree T,int a,int b)//打印输出后继线索二叉排序树T中所有大于a且小于b的元素
{
p=T;
while(!p->ltag) p=p->lchild; //找到最小元素
while(p&&p->data<b)
{
if(p->data>a) printf("%d\n",p->data); //输出符合条件的元素
if(p->rtag) p=p->rtag;
else
{
p=p->rchild;
while(!p->ltag) p=p->lchild;
} //转到中序后继
}//while
}//Print_Between
9.36
void BSTree_Insert_Key(BiThrTree &T,int x)//在后继线索二叉排序树T中插入元素x
{
if(T->data<x) //插入到右侧
{
if(T->rtag) //T没有右子树时,作为右孩子插入
{
p=T->rchild;
q=(BiThrNode*)malloc(sizeof(BiThrNode));
q->data=x;
T->rchild=q;T->rtag=0;
q->rtag=1;q->rchild=p; //修改原线索
}
else BSTree_Insert_Key(T->rchild,x);//T有右子树时,插入右子树中
}//if
else if(T->data>x) //插入到左子树中
{
if(!T->lchild) //T没有左子树时,作为左孩子插入
{
q=(BiThrNode*)malloc(sizeof(BiThrNode));
q->data=x;
T->lchild=q;
q->rtag=1;q->rchild=T; //修改自身的线索
}
else BSTree_Insert_Key(T->lchild,x);//T有左子树时,插入左子树中
}//if
}//BSTree_Insert_Key
9.37
Status BSTree_Delete_key(BiThrTree &T,int x)//在后继线索二叉排序树T中删除元素x
{
BTNode *pre,*ptr,*suc;//ptr为x所在结点,pre和suc分别指向ptr的前驱和后继
p=T;last=NULL; //last始终指向当前结点p的前一个(前驱)
while(!p->ltag) p=p->lchild; //找到中序起始元素
while(p)
{
if(p->data==x) //找到了元素x结点
{
pre=last;
ptr=p;
}
else if(last&&last->data==x) suc=p; //找到了x的后继
if(p->rtag) p=p->rtag;
else
{
p=p->rchild;
while(!p->ltag) p=p->lchild;
} //转到中序后继
last=p;
}//while //借助中序遍历找到元素x及其前驱和后继结点
if(!ptr) return ERROR; //未找到待删结点
Delete_BSTree(ptr); //删除x结点
if(pre&&pre->rtag)
pre->rchild=suc; //修改线索
return OK;
}//BSTree_Delete_key
void Delete_BSTree(BiThrTree &T)//课本上给出的删除二叉排序树的子树T的算法,按照线索二叉树的结构作了一些改动
{
q=T;
if(!T->ltag&&T->rtag) //结点无右子树,此时只需重接其左子树
T=T->lchild;
else if(T->ltag&&!T->rtag) //结点无左子树,此时只需重接其右子树
T=T->rchild;
else if(!T->ltag&&!T->rtag) //结点既有左子树又有右子树
{
p=T;r=T->lchild;
while(!r->rtag)
{
s=r;
r=r->rchild; //找到结点的前驱r和r的双亲s
}
T->data=r->data; //用r代替T结点
if(s!=T)
s->rchild=r->lchild;
else s->lchild=r->lchild; //重接r的左子树到其双亲结点上
q=r;
}//else
free(q); //删除结点
}//Delete_BSTree
分析:本算法采用了先求出x结点的前驱和后继,再删除x结点的办法,这样修改线索时会比较简单,直接让前驱的线索指向后继就行了.如果试图在删除x结点的同时修改线索,则问题反而复杂化了,
9.38
void BSTree_Merge(BiTree &T,BiTree &S)//把二叉排序树S合并到T中
{
if(S->lchild) BSTree_Merge(T,S->lchild);
if(S->rchild) BSTree_Merge(T,S->rchild); //合并子树
Insert_Key(T,S); //插入元素
}//BSTree_Merge
void Insert_Key(Bitree &T,BTNode *S)//把树结点S插入到T的合适位置上
{
if(S->data>T->data)
{
if(!T->rchild) T->rchild=S;
else Insert_Key(T->rchild,S);
}
else if(S->data<T->data)
{
if(!T->lchild) T->lchild=S;
else Insert_Key(T->lchild,S);
}
S->lchild=NULL; //插入的新结点必须和原来的左右子树断绝关系
S->rchild=NULL; //否则会导致树结构的混乱
}//Insert_Key
分析:这是一个与课本上不同的插入算法.在合并过程中,并不释放或新建任何结点,而是采取修改指针的方式来完成合并.这样,就必须按照后序序列把一棵树中的元素逐个连接到另一棵树上,否则将会导致树的结构的混乱,
9.39
void BSTree_Split(BiTree &T,BiTree &A,BiTree &B,int x)//把二叉排序树T分裂为两棵二叉排序树A和B,其中A的元素全部小于等于x,B的元素全部大于x
{
if(T->lchild) BSTree_Split(T->lchild,A,B,x);
if(T->rchild) BSTree_Split(T->rchild,A,B,x); //分裂左右子树
if(T->data<=x) Insert_Key(A,T);
else Insert_Key(B,T); //将元素结点插入合适的树中
}//BSTree_Split
void Insert_Key(Bitree &T,BTNode *S)//把树结点S插入到T的合适位置上
{
if(!T) T=S; //考虑到刚开始分裂时树A和树B为空的情况
else if(S->data>T->data) //其余部分与上一题同
{
if(!T->rchild) T->rchild=S;
else Insert_Key(T->rchild,S);
}
else if(S->data<T->data)
{
if(!T->lchild) T->lchild=S;
else Insert_Key(T->lchild,S);
}
S->lchild=NULL;
S->rchild=NULL;
}//Insert_Key
9.40
typedef struct {
int data;
int bf;
int lsize; //lsize域表示该结点的左子树的结点总数加1
BlcNode *lchild,*rchild;
} BlcNode,*BlcTree; //含lsize域的平衡二叉排序树类型
BTNode *Locate_BlcTree(BlcTree T,int k)//在含lsize域的平衡二叉排序树T中确定第k小的结点指针
{
if(!T) return NULL; //k小于1或大于树结点总数
if(T->lsize==k) return T; //就是这个结点
else if(T->lsize>k)
return Locate_BlcTree(T->lchild,k); //在左子树中寻找
else return Locate_BlcTree(T->rchild,k-T->lsize); //在右子树中寻找,注意要修改k的值
}//Locate_BlcTree
9.41
typedef struct {
enum {LEAF,BRANCH} tag; //结点类型标识
int keynum;
BPLink parent; //双亲指针
int key[MAXCHILD]; //关键字
union {
BPLink child[MAXCHILD];//非叶结点的孩子指针
struct {
rectype *info[MAXCHILD];//叶子结点的信息指针
BPNode *next; //指向下一个叶子结点的链接
} leaf;
}
} BPNode,*BPLink,*BPTree;//B+树及其结点类型
Status BPTree_Search(BPTree T,int key,BPNode *ptr,int pos)//B+树中按关键字随机查找的算法,返回包含关键字的叶子结点的指针ptr以及关键字在叶子结点中的位置pos
{
p=T;
while(p.tag==BRANCH) //沿分支向下查找
{
for(i=0;i<p->keynum&&key>p->key[i];i++); //确定关键字所在子树
if(i==p->keynum) return ERROR; //关键字太大
p=p->child[i];
}
for(i=0;i<p->keynum&&key!=p->key[i];i++); //在叶子结点中查找
if(i==p->keynum) return ERROR; //找不到关键字
ptr=p;pos=i;
return OK;
}//BPTree_Search
9.42
void TrieTree_Insert_Key(TrieTree &T,StringType key)//在Trie树T中插入字符串key,StringType的结构见第四章
{
q=(TrieNode*)malloc(sizeof(TrieNode));
q->kind=LEAF;
q->lf.k=key; //建叶子结点
klen=key[ 0 ];
p=T;i=1;
while(p&&i<=klen&&p->bh.ptr[ord(key[i])])
{
last=p;
p=p->bh.ptr[ord(key[i])];
i++;
} //自上而下查找
if(p->kind==BRANCH) //如果最后落到分支结点(无同义词):
{
p->bh.ptr[ord(key[i])]=q; //直接连上叶子
p->bh.num++;
}
else //如果最后落到叶子结点(有同义词):
{
r=(TrieNode*)malloc(sizeof(TrieNode)); //建立新的分支结点
last->bh.ptr[ord(key[i-1])]=r; //用新分支结点取代老叶子结点和上一层的联系
r->kind=BRANCH;r->bh.num=2;
r->bh.ptr[ord(key[i])]=q;
r->bh.ptr[ord(p->lf.k[i])]=p; //新分支结点与新老两个叶子结点相连
}
}//TrieTree_Insert_Key
分析:当自上而下的查找结束时,存在两种情况.一种情况,树中没有待插入关键字的同义词,此时只要新建一个叶子结点并连到分支结点上即可.另一种情况,有同义词,此时要把同义词的叶子结点与树断开,在断开的部位新建一个下一层的分支结点,再把同义词和新关键字的叶子结点连到新分支结点的下一层,
9.43
Status TrieTree_Delete_Key(TrieTree &T,StringType key)//在Trie树T中删除字符串key
{
p=T;i=1;
while(p&&p->kind==BRANCH&&i<=key[ 0 ]) //查找待删除元素
{
last=p;
p=p->bh.ptr[ord(key[i])];
i++;
}
if(p&&p->kind==LEAF&&p->lf.k=key) //找到了待删除元素
{
last->bh.ptr[ord(key[i-1])]=NULL;
free(p);
return OK;
}
else return ERROR; //没找到待删除元素
}//TrieTree_Delete_Key
9.44
void Print_Hash(HashTable H)//按第一个字母顺序输出Hash表中的所有关键字,其中处理冲突采用线性探测开放定址法
{
for(i=1;i<=26;i++)
for(j=i;H.elem[j].key;j=(j+1)%hashsize[sizeindex]) //线性探测
if(H(H.elem[j].key)==i) printf("%s\n",H.elem[j]);
}//Print_Hash
int H(char *s)//求Hash函数
{
if(s) return s[ 0 ]-96; //求关键字第一个字母的字母序号(小写)
else return 0;
}//H
9.45
typedef *LNode[MAXSIZE] CHashTable; //链地址Hash表类型
Status Build_Hash(CHashTable &T,int m)//输入一组关键字,建立Hash表,表长为m,用链地址法处理冲突.
{
if(m<1) return ERROR;
T=malloc(m*sizeof(WORD)); //建立表头指针向量
for(i=0;i<m;i++) T[i]=NULL;
while((key=Inputkey())!=NULL) //假定Inputkey函数用于从键盘输入关键字
{
q=(LNode*)malloc(sizeof(LNode));
q->data=key;q->next=NULL;
n=H(key);
if(!T[n]) T[n]=q; //作为链表的第一个结点
else
{
for(p=T[n];p->next;p=p->next);
p->next=q; //插入链表尾部.本算法不考虑排序问题.
}
}//while
return OK;
}//Build_Hash
9.46
Status Locate_Hash(HashTable H,int row,int col,KeyType key,int &k)//根据行列值在Hash表表示的稀疏矩阵中确定元素key的位置k
{
h=2*(100*(row/10)+col/10); //作者设计的Hash函数
while(H.elem[h].key&&!EQ(H.elem[h].key,key))
h=(h+1)%20000;
if(EQ(H.elem[h].key,key)) k=h;
else k=NULL;
}//Locate_Hash
分析:本算法所使用的Hash表长20000,装填因子为50%,Hash函数为行数前两位和列数前两位所组成的四位数再乘以二,用线性探测法处理冲突.当矩阵的元素是随机分布时,查找的时间复杂度为O(1).
10.23
void Insert_Sort1(SqList &L)//监视哨设在高下标端的插入排序算法
{
k=L.length;
for(i=k-1;i;--i) //从后向前逐个插入排序
if(L.r[i].key>L.r[i+1].key)
{
L.r[k+1].key=L.r[i].key; //监视哨
for(j=i+1;L.r[j].key>L.r[i].key;++j)
L.r[j-1].key=L.r[j].key; //前移
L.r[j-1].key=L.r[k+1].key; //插入
}
}//Insert_Sort1
10.24
void BiInsert_Sort(SqList &L)//二路插入排序的算法
{
int d[MAXSIZE]; //辅助存储
x=L.r.key;d=x;
first=1;final=1;
for(i=2;i<=L.length;i++)
{
if(L.r[i].key>=x) //插入前部
{
for(j=final;d[j]>L.r[i].key;j--)
d[j+1]=d[j];
d[j+1]=L.r[i].key;
final++;
}
else //插入后部
{
for(j=first;d[j]<L.r[i].key;j++)
d[j-1]=d[j];
d[(j-2)%MAXSIZE+1]=L.r[i].key;
first=(first-2)%MAXSIZE+1; //这种形式的表达式是为了兼顾first=1的情况
}
}//for
for(i=first,j=1;d[i];i=i%MAXSIZE+1,j++)//将序列复制回去
L.r[j].key=d[i];
}//BiInsert_Sort
10.25
void SLInsert_Sort(SLList &L)//静态链表的插入排序算法
{
L.r[0].key=0;L.r[0].next=1;
L.r[1].next=0; //建初始循环链表
for(i=2;i<=L.length;i++) //逐个插入
{
p=0;x=L.r[i].key;
while(L.r[L.r[p].next].key<x&&L.r[p].next)
p=L.r[p].next;
q=L.r[p].next;
L.r[p].next=i;
L.r[i].next=q;
}//for
p=L.r[0].next;
for(i=1;i<L.length;i++) //重排记录的位置
{
while(p<i) p=L.r[p].next;
q=L.r[p].next;
if(p!=i)
{
L.r[p]<->L.r[i];
L.r[i].next=p;
}
p=q;
}//for
}//SLInsert_Sort
10.26
void Bubble_Sort1(int a[ ],int n)//对包含n个元素的数组a进行改进的冒泡排序
{
change=n-1; //change指示上一趟冒泡中最后发生交换的元素
while(change)
{
for(c=0,i=0;i<change;i++)
if(a[i]>a[i+1])
{
a[i]<->a[i+1];
c=i+1; //c指示这一趟冒泡中发生交换的元素
}
change=c;
}//while
}//Bubble_Sort1
10.27
void Bubble_Sort2(int a[ ],int n)//相邻两趟是反方向起泡的冒泡排序算法
{
low=0;high=n-1; //冒泡的上下界
change=1;
while(low<high&&change)
{
change=0;
for(i=low;i<high;i++) //从上向下起泡
if(a[i]>a[i+1])
{
a[i]<->a[i+1];
change=1;
}
high--; //修改上界
for(i=high;i>low;i--) //从下向上起泡
if(a[i]<a[i-1])
{
a[i]<->a[i-1];
change=1;
}
low++; //修改下界
}//while
}//Bubble_Sort2
10.28
void Bubble_Sort3(int a[ ],int n)//对上一题的算法进行化简,循环体中只包含一次冒泡
{
int b[ 3 ]; //b[0]为冒泡的下界,b[ 2 ]为上界,b[1]无用
d=1;b[0]=0;b[ 2 ]=n-1; //d为冒泡方向的标识,1为向上,-1为向下
change=1;
while(b[0]<b[ 2 ]&&change)
{
change=0;
for(i=b[1-d];i!=b[1+d];i+=d) //统一的冒泡算法
if((a[i]-a[i+d])*d>0) //注意这个交换条件
{
a[i]<->a[i+d];
change=1;
}
b[1+d]-=d; //修改边界
d*=-1; //换个方向
}//while
}//Bubble_Sort3
10.29
void OE_Sort(int a[ ],int n)//奇偶交换排序的算法
{
change=1;
while(change)
{
change=0;
for(i=1;i<n-1;i+=2) //对所有奇数进行一趟比较
if(a[i]>a[i+1])
{
a[i]<->a[i+1];
change=1;
}
for(i=0;i<n-1;i+=2) //对所有偶数进行一趟比较
if(a[i]>a[i+1])
{
a[i]<->a[i+1];
change=1;
}
}//while
}//OE_Sort
分析:本算法的结束条件是连续两趟比较无交换发生
10.30
typedef struct {
int low;
int high;
} boundary; //子序列的上下界类型
void QSort_NotRecurve(int SQList &L)//快速排序的非递归算法
{
low=1;high=L.length;
InitStack(S); //S的元素为boundary类型
while(low<high&&!StackEmpty(S)) //注意排序结束的条件
{
if(high-low>2) //如果当前子序列长度大于3且尚未排好序
{
pivot=Partition(L,low,high); //进行一趟划分
if(high-pivot>pivot-low)
{
Push(S,{pivot+1,high}); //把长的子序列边界入栈
high=pivot-1; //短的子序列留待下次排序
}
else
{
Push(S,{low,pivot-1});
low=pivot+1;
}
}//if
else if(low<high&&high-low<3)//如果当前子序列长度小于3且尚未排好序
{
Easy_Sort(L,low,high); //直接进行比较排序
low=high; //当前子序列标志为已排好序
}
else //如果当前子序列已排好序但栈中还有未排序的子序列
{
Pop(S,a); //从栈中取出一个子序列
low=a.low;
high=a.high;
}
}//while
}//QSort_NotRecurve
int Partition(SQList &L,int low,int high)//一趟划分的算法,与书上相同
{
L.r[0]=L.r[low];
pivotkey=L.r[low].key;
while(low<high)
{
while(low<high&&L.r[high].key>=pivotkey)
high--;
L.r[low]=L.r[high];
while(low<high&&L.r[low].key<=pivotkey)
low++;
L.r[high]=L.r[low];
}//while
L.r[low]=L.r[0];
return low;
}//Partition
void Easy_Sort(SQList &L,int low,int high)//对长度小于3的子序列进行比较排序
{
if(high-low==1) //子序列只含两个元素
if(L.r[low].key>L.r[high].key) L.r[low]<->L.r[high];
else //子序列含有三个元素
{
if(L.r[low].key>L.r[low+1].key) L.r[low]<->L.r[low+1];
if(L.r[low+1].key>L.r[high].key) L.r[low+1]<->L.r[high];
if(L.r[low].key>L.r[low+1].key) L.r[low]<->L.r[low+1];
}
}//Easy_Sort
10.31
void Divide(int a[ ],int n)//把数组a中所有值为负的记录调到非负的记录之前
{
low=0;high=n-1;
while(low<high)
{
while(low<high&&a[high]>=0) high--; //以0作为虚拟的枢轴记录
a[low]<->a[high];
while(low<high&&a[low]<0) low++;
a[low]<->a[high];
}
}//Divide
10.32
typedef enum {RED,WHITE,BLUE} color; //三种颜色
void Flag_Arrange(color a[ ],int n)//把由三种颜色组成的序列重排为按照红,白,蓝的顺序排列
{
i=0;j=0;k=n-1;
while(j<=k)
switch(a[j])
{
case RED:
a[i]<->a[j];
i++;
j++;
break;
case WHITE:
j++;
break;
case BLUE:
a[j]<->a[k];
k--; //这里没有j++;语句是为了防止交换后a[j]仍为蓝色的情况
}
}//Flag_Arrange
分析:这个算法中设立了三个指针.其中,j表示当前元素;i以前的元素全部为红色;k以后的元素全部为蓝色.这样,就可以根据j的颜色,把其交换到序列的前部或者后部,
10.33
void LinkedList_Select_Sort(LinkedList &L)//单链表上的简单选择排序算法
{
for(p=L;p->next->next;p=p->next)
{
q=p->next;x=q->data;
for(r=q,s=q;r->next;r=r->next) //在q后面寻找元素值最小的结点
if(r->next->data<x)
{
x=r->next->data;
s=r;
}
if(s!=q) //找到了值比q->data更小的最小结点s->next
{
p->next=s->next;s->next=q;
t=q->next;q->next=p->next->next;
p->next->next=t;
} //交换q和s->next两个结点
}//for
}//LinkedList_Select_Sort
10.34
void Build_Heap(Heap &H,int n)//从低下标到高下标逐个插入建堆的算法
{
for(i=2;i<n;i++)
{ //此时从H.r[1]到H.r[i-1]已经是大顶堆
j=i;
while(j!=1) //把H.r[i]插入
{
k=j/2;
if(H.r[j].key>H.r[k].key)
H.r[j]<->H.r[k];
j=k;
}
}//for
}//Build_Heap
10.35
void TriHeap_Sort(Heap &H)//利用三叉树形式的堆进行排序的算法
{
for(i=H.length/3;i>0;i--)
Heap_Adjust(H,i,H.length);
for(i=H.length;i>1;i--)
{
H.r[1]<->H.r[i];
Heap_Adjust(H,1,i-1);
}
}//TriHeap_Sort
void Heap_Adjust(Heap &H,int s,int m)//顺序表H中,H.r[s+1]到H.r[m]已经是堆,把H.r[s]插入并调整成堆
{
rc=H.r[s];
for(j=3*s-1;j<=m;j=3*j-1)
{
if(j<m&&H.r[j].key<H.r[j+1].key) j++;
if(j<m&&H.r[j].key<H.r[j+1].key) j++;
H.r[s]=H.r[j];
s=j;
}
H.r[s]=rc;
}//Heap_Adjust
分析:本算法与课本上的堆排序算法相比,只有两处改动:1.建初始堆时,i的上限从H.length/3开始(为什么?) 2.调整堆的时候,要从结点的三个孩子结点中选择最大的那一个,最左边的孩子的序号的计算公式为j=3*s-1(为什么?)
10.36
void Merge_Sort(int a[ ],int n)//归并排序的非递归算法
{
for(l=1;l<n;l*=2) //l为一趟归并段的段长
for(i=0;(2*i-1)*l<n;i++) //i为本趟的归并段序号
{
start1=2*l*i; //求出待归并的两段的上下界
end1=start1+l-1;
start2=end1+1;
end2=(start2+l-1)>(n-1)?(n-1):(start2+l-1);//注意end2可能超出边界
Merge(a,start1,end1,start2,end2); //归并
}
}//Merge_Sort
void Merge(int a[ ],int s1,int e1,int s2,int e2)//将有序子序列a[s1]到a[e1]和a[s2]到a[e2]归并为有序序列a[s1]到a[e2]
{
int b[MAXSIZE]; //设立辅助存储数组b
for(i=s1,j=s2,k=s1;i<=e1&&j<=e2;k++)
{
if(a[i]<a[j]) b[k]=a[i++];
else b[k]=a[j++];
}
while(i<=e1) b[k++]=a[i++];
while(j<=e2) b[k++]=a[j++]; //归并到b中
for(i=s1;i<=e2;i++) //复制回去
a[i]=b[i];
}//Merge
10.37
void LinkedList_Merge_Sort1(LinkedList &L)//链表结构上的归并排序非递归算法
{
for(l=1;l<L.length;l*=2) //l为一趟归并段的段长
for(p=L->next,e2=p;p->next;p=e2)
{
for(i=1,q=p;i<=l&&q->next;i++,q=q->next);
e1=q;
for(i=1;i<=l&&q->next;i++,q=q->next);
e2=q; //求出两个待归并子序列的尾指针
if(e1!=e2) LinkedList_Merge(L,p,e1,e2); //归并
}
}//LinkedList_Merge_Sort1
void LinkedList_Merge(LinkedList &L,LNode *p,LNode *e1,LNode *e2)//对链表上的子序列进行归并,第一个子序列是从p->next到e1,第二个是从e1->next到e2
{
q=p->next;r=e1->next; //q和r为两个子序列的起始位置
while(q!=e1->next&&r!=e2->next)
{
if(q->data<r->data) //选择关键字较小的那个结点接在p的后面
{
p->next=q;p=q;
q=q->next;
}
else
{
p->next=r;p=r;
r=r->next;
}
}//while
while(q!=e1->next) //接上剩余部分
{
p->next=q;p=q;
q=q->next;
}
while(r!=e2->next)
{
p->next=r;p=r;
r=r->next;
}
}//LinkedList_Merge
10.38
void LinkedList_Merge_Sort2(LinkedList &L)//初始归并段为最大有序子序列的归并排序,采用链表存储结构
{
LNode *end[MAXSIZE]; //设立一个数组来存储各有序子序列的尾指针
for(p=L->next->next,i=0;p;p=p->next) //求各有序子序列的尾指针
if(!p->next||p->data>p->next->data) end[i++]=p;
while(end[0]->next) //当不止一个子序列时进行两两归并
{
j=0;k=0; //j:当前子序列尾指针存储位置;k:归并后的子序列尾指针存储位置
for(p=L->next,e2=p;p->next;p=e2) //两两归并所有子序列
{
e1=end[j];e2=end[j+1]; //确定两个子序列
if(e1->next) LinkedList_Merge(L,p,e1,e2); //归并
end[k++]=e2; //用新序列的尾指针取代原来的尾指针
j+=2; //转到后面两个子序列
}
}//while
}//LinkedList_Merge_Sort2
void LinkedList_Merge(LinkedList &L,LNode *p,LNode *e1,LNode *e2)//对链表上的子序列进行归并,第一个子序列是从p->next到e1,第二个是从e1->next到e2
{
q=p->next;r=e1->next;
while(q!=e1->next&&r!=e2->next)
{
if(q->data<r->data)
{
p->next=q;p=q;
q=q->next;
}
else
{
p->next=r;p=r;
r=r->next;
}
}//while
while(q!=e1->next)
{
p->next=q;p=q;
q=q->next;
}
while(r!=e2->next)
{
p->next=r;p=r;
r=r->next;
}
}//LinkedList_Merge,与上一题完全相同
10.39
void SL_Merge(int a[ ],int l1,int l2)//把长度分别为l1,l2且l1^2<(l1+l2)的两个有序子序列归并为有序序列
{
start1=0;start2=l1; //分别表示序列1和序列2的剩余未归并部分的起始位置
for(i=0;i<l1;i++) //插入第i个元素
{
for(j=start2;j<l1+l2&&a[j]<a[start1+i];j++); //寻找插入位置
k=j-start2; //k为要向右循环移动的位数
RSh(a,start1,j-1,k);//将a[start1]到a[j-1]之间的子序列循环右移k位
start1+=k+1;
start2=j; //修改两序列尚未归并部分的起始位置
}
}//SL_Merge
void RSh(int a[ ],int start,int end,int k)//将a[start]到a[end]之间的子序列循环右移k位,算法原理参见5.18
{
len=end-start+1;
for(i=1;i<=k;i++)
if(len%i==0&&k%i==0) p=i; //求len和k的最大公约数p
for(i=0;i<p;i++) //对p个循环链分别进行右移
{
j=start+i;l=start+(i+k)%len;temp=a[j];
while(l!=start+i)
{
a[j]=temp;
temp=a[l];
a[l]=a[j];
j=l;l=start+(j-start+k)%len; //依次向右移
}
a[start+i]=temp;
}//for
}//RSh
10.40
书后给出的解题思路在表述上存在问题,无法理解.比如说,"把第一个序列划分为两个子序列,使其中的第一个子序列含有s1个记录,0<=s1<s,第二个子序列有s个记录."可是题目中并没有说明,第一个序列的长度<2s.请会做的朋友提供解法,
10.41
void Hash_Sort(int a[ ])//对1000个关键字为四位整数的记录进行排序
{
int b[10000];
for(i=0;i<1000;i++) //直接按关键字散列
{
for(j=a[i];b[j];j=(j+1)%10000);
b[j]=a[i];
}
for(i=0,j=0;i<1000;j++) //将散列收回a中
if(b[j])
{
for(x=b[j],k=j;b[k];k=(k+1)%10000)
if(b[k]==x)
{
a[i++]=x;
b[k]=0;
}
}//if
}//Hash_Sort
10.42
typedef struct {
int gt; //大于该记录的个数
int lt; //小于该记录的个数
} place; //整个序列中比某个关键字大或小的记录个数
int Get_Mid(int a[ ],int n)//求一个序列的中值记录的位置
{
place b[MAXSIZE];
for(i=0;i<n;i++) //对每一个元素统计比它大和比它小的元素个数gt和lt
for(j=0;j<n;j++)
{
if(a[j]>a[i]) b[i].gt++;
else if(a[j]<a[i]) b[i].lt++;
}
mid=0;
min_dif=abs(b[0].gt-b[0].lt);
for(i=0;i<n;i++) //找出gt值与lt值最接近的元素,即为中值记录
if(abs(b[i].gt-b[i].lt)<min_dif) mid=i;
return mid;
}//Get_Mid
10.43
void Count_Sort(int a[ ],int n)//计数排序算法
{
int c[MAXSIZE];
for(i=0;i<n;i++) //对每一个元素
{
for(j=0,count=0;j<n;j++) //统计关键字比它小的元素个数
if(a[j]<a[i]) count++:
c[i]=count;
}
for(i=0;i<n;i++) //依次求出关键字最小,第二小,...,最大的记录
{
min=0;
for(j=0;j<n;j++)
if(c[j]<c[min]) min=j; //求出最小记录的下标min
a[i]<->a[min]; //与第i个记录交换
c[min]=INFINITY; //修改该记录的c值为无穷大以便下一次选取
}
}//Count_Sort
10.44
void Enum_Sort(int a[ ],int n)//对关键字只能取v到w之间任意整数的序列进行排序
{
int number[w+1],pos[w+1];
for(i=0;i<n;i++) number[a[i]]++; //计数
for(pos[0]=0,i=1;i<n;i++)
pos[i]=pos[i-1]+num[i]; //pos数组可以把关键字的值映射为元素在排好的序列中的位置
for(i=0;i<n;i++) //构造有序数组c
c[pos[a[i]]++]=a[i];
for(i=0;i<n;i++)
a[i]=c[i];
}//Enum_Sort
分析:本算法参考了第五章三元组稀疏矩阵转置的算法思想,其中的pos数组和那里的cpot数组起的是相类似的作用,
10.45
typedef enum {0,1,2,3,4,5,6,7,8,9} digit; //个位数类型
typedef digit[3] num; //3位自然数类型,假设低位存储在低下标,高位存储在高下标
void Enum_Radix_Sort(num a[ ],int n)//利用计数实现基数排序,其中关键字为3位自然数,共有n个自然数
{
int number,pos ;
num c[MAXSIZE];
for(j=0;j<3;j++) //依次对个位,十位和百位排序
{
for(i=0;i<n;i++) number[a[i][j]]++; //计数
for(pos[0]=0,i=1;i<n;i++)
pos[i]=pos[i-1]+num[i]; //把关键字的值映射为元素在排好的序列中的位置
for(i=0;i<n;i++) //构造有序数组c
c[pos[a[i][j]]++]=a[i];
for(i=0;i<n;i++)
a[i]=c[i];
}//for
}//Enum_Radix_Sort
分析:计数排序是一种稳定的排序方法.正因为如此,它才能够被用来实现基数排序,
10.46
typedef struct {
int key;
int pos;
} Shadow; //影子序列的记录类型
void Shadow_Sort(Rectype b[ ],Rectype &a[ ],int n)//对元素很大的记录序列b进行排序,结果放入a中,不移动元素
{
Shadow d[MAXSIZE];
for(i=0;i<n;i++) //生成影子序列
{
d[i].key=b[i].key;
d[i].pos=i;
}
for(i=n-1,change=1;i>1&&change;i--) //对影子序列执行冒泡排序
{
change=0;
for(j=0;j<i;j++)
if(d[j].key>d[j+1].key)
{
d[j]<->d[j+1];
change=1;
}
}//for
for(i=0;i<n;i++) //按照影子序列里记录的原来位置复制原序列
a[i]=b[d[i].pos];
}//Shadow_Sort
void RSh(int A[n],int k)//把数组A的元素循环右移k位,只用一个辅助存储空间
{
for(i=1;i<=k;i++)
if(n%i==0&&k%i==0) p=i;//求n和k的最大公约数p
for(i=0;i<p;i++)
{
j=i;l=(i+k)%n;temp=A[i];
while(l!=i)
{
A[j]=temp;
temp=A[l];
A[l]=A[j];
j=l;l=(j+k)%n;
}// 循环右移一步
A[i]=temp;
}//for
}//RSh
分析:要把A的元素循环右移k位,则A[0]移至A[k],A[k]移至A[2k]......直到最终回到A[0].然而这并没有全部解决问题,因为有可能有的元素在此过程中始终没有被访问过,而是被跳了过去.分析可知,当n和k的最大公约数为p时,只要分别以A[0],A[1],...A[p-1]为起点执行上述算法,就可以保证每一个元素都被且仅被右移一次,从而满足题目要求.也就是说,A的所有元素分别处在p个"循环链"上面.举例如下:
n=15,k=6,则p=3.
第一条链:A[0]->A[6],A[6]->A[12],A[12]->A[3],A[3]->A[9],A[9]->A[0].
第二条链:A[1]->A[7],A[7]->A[13],A[13]->A[4],A[4]->A[10],A[10]->A[1].
第三条链:A[2]->A[8],A[8]->A[14],A[14]->A[5],A[5]->A[11],A[11]->A[2].
恰好使所有元素都右移一次.
虽然未经数学证明,但作者相信上述规律应该是正确的,
5.19
void Get_Saddle(int A[m][n])//求矩阵A中的马鞍点
{
for(i=0;i<m;i++)
{
for(min=A[i][0],j=0;j<n;j++)
if(A[i][j]<min) min=A[i][j]; //求一行中的最小值
for(j=0;j<n;j++)
if(A[i][j]==min) //判断这个(些)最小值是否鞍点
{
for(flag=1,k=0;k<m;k++)
if(min<A[k][j]) flag=0;
if(flag)
printf("Found a saddle element!\nA[%d][%d]=%d",i,j,A[i][j]);
}
}//for
}//Get_Saddle
5.20
本题难度极大,故仅探讨一下思路.这一题的难点在于,在多项式的元数m未知的情况下,如何按照降幂次序输出各项.可以考虑采取类似于层序遍历的思想,从最高次的项开始,依次对其每一元的次数减一,入一个队列.附设访问标志visited以避免重复,
5.21
void TSMatrix_Add(TSMatrix A,TSMatrix B,TSMatrix &C)//三元组表示的稀疏矩阵加法
{
C.mu=A.mu;C.nu=A.nu;C.tu=0;
pa=1;pb=1;pc=1;
for(x=1;x<=A.mu;x++) //对矩阵的每一行进行加法
{
while(A.data[pa].i<x) pa++;
while(B.data[pb].i<x) pb++;
while(A.data[pa].i==x&&B.data[pb].i==x)//行列值都相等的元素
{
if(A.data[pa].j==B.data[pb].j)
{
ce=A.data[pa].e+B.data[pb].e;
if(ce) //和不为0
{
C.data[pc].i=x;
C.data[pc].j=A.data[pa].j;
C.data[pc].e=ce;
pa++;pb++;pc++;
}
}//if
else if(A.data[pa].j>B.data[pb].j)
{
C.data[pc].i=x;
C.data[pc].j=B.data[pb].j;
C.data[pc].e=B.data[pb].e;
pb++;pc++;
}
else
{
C.data[pc].i=x;
C.data[pc].j=A.data[pa].j;
C.data[pc].e=A.data[pa].e
pa++;pc++;
}
}//while
while(A.data[pa]==x) //插入A中剩余的元素(第x行)
{
C.data[pc].i=x;
C.data[pc].j=A.data[pa].j;
C.data[pc].e=A.data[pa].e
pa++;pc++;
}
while(B.data[pb]==x) //插入B中剩余的元素(第x行)
{
C.data[pc].i=x;
C.data[pc].j=B.data[pb].j;
C.data[pc].e=B.data[pb].e;
pb++;pc++;
}
}//for
C.tu=pc;
}//TSMatrix_Add
5.22
void TSMatrix_Addto(TSMatrix &A,TSMatrix B)//将三元组矩阵B加到A上
{
for(i=1;i<=A.tu;i++)
A.data[MAXSIZE-A.tu+i]=A.data[i];/把A的所有元素都移到尾部以腾出位置
pa=MAXSIZE-A.tu+1;pb=1;pc=1;
for(x=1;x<=A.mu;x++) //对矩阵的每一行进行加法
{
while(A.data[pa].i<x) pa++;
while(B.data[pb].i<x) pb++;
while(A.data[pa].i==x&&B.data[pb].i==x)//行列值都相等的元素
{
if(A.data[pa].j==B.data[pb].j)
{
ne=A.data[pa].e+B.data[pb].e;
if(ne) //和不为0
{
A.data[pc].i=x;
A.data[pc].j=A.data[pa].j;
A.data[pc].e=ne;
pa++;pb++;pc++;
}
}//if
else if(A.data[pa].j>B.data[pb].j)
{
A.data[pc].i=x;
A.data[pc].j=B.data[pb].j;
A.data[pc].e=B.data[pb].e;
pb++;pc++;
}
else
{
A.data[pc].i=x;
A.data[pc].j=A.data[pa].j;
A.data[pc].e=A.data[pa].e
pa++;pc++;
}
}//while
while(A.data[pa]==x) //插入A中剩余的元素(第x行)
{
A.data[pc].i=x;
A.data[pc].j=A.data[pa].j;
A.data[pc].e=A.data[pa].e
pa++;pc++;
}
while(B.data[pb]==x) //插入B中剩余的元素(第x行)
{
A.data[pc].i=x;
A.data[pc].j=B.data[pb].j;
A.data[pc].e=B.data[pb].e;
pb++;pc++;
}
}//for
A.tu=pc;
for(i=A.tu;i<MAXSIZE;i++) A.data[i]={0,0,0}; //清除原来的A中记录
}//TSMatrix_Addto
5.23
typedef struct{
int j;
int e;
} DSElem;
typedef struct{
DSElem data[MAXSIZE];
int cpot[MAXROW];//这个向量存储每一行在二元组中的起始位置
int mu,nu,tu;
} DSMatrix; //二元组矩阵类型
Status DSMatrix_Locate(DSMatrix A,int i,int j,int &e)//求二元组矩阵的元素A[i][j]的值e
{
for(s=cpot[i];s<cpot[i+1]&&A.data[s].j!=j;s++);//注意查找范围
if(A.data[s].i==i&&A.data[s].j==j) //找到了元素A[i][j]
{
e=A.data[s];
return OK;
}
return ERROR;
}//DSMatrix_Locate
5.24
typedef struct{
int seq; //该元素在以行为主序排列时的序号
int e;
} SElem;
typedef struct{
SElem data[MAXSIZE];
int mu,nu,tu;
} SMatrix; //单下标二元组矩阵类型
Status SMatrix_Locate(SMatrix A,int i,int j,int &e)//求单下标二元组矩阵的元素A[i][j]的值e
{
s=i*A.nu+j+1;p=1;
while(A.data[p].seq<s) p++; //利用各元素seq值逐渐递增的特点
if(A.data[p].seq==s) //找到了元素A[i][j]
{
e=A.data[p].e;
return OK;
}
return ERROR;
}//SMatrix_Locate
5.25
typedef enum{0,1} bool;
typedef struct{
int mu,nu;
int elem[MAXSIZE];
bool map[mu][nu];
} BMMatrix; //用位图表示的矩阵类型
void BMMatrix_Add(BMMatrix A,BMMatrix B,BMMatrix &C)//位图矩阵的加法
{
C.mu=A.mu;C.nu=A.nu;
pa=1;pb=1;pc=1;
for(i=0;i<A.mu;i++) //每一行的相加
for(j=0;j<A.nu;j++) //每一个元素的相加
{
if(A.map[i][j]&&B.map[i][j]&&(A.elem[pa]+B.elem[pb]))//结果不为0
{
C.elem[pc]=A.elem[pa]+B.elem[pb];
C.map[i][j]=1;
pa++;pb++;pc++;
}
else if(A.map[i][j]&&!B.map[i][j])
{
C.elem[pc]=A.elem[pa];
C.map[i][j]=1;
pa++;pc++;
}
else if(!A.map[i][j]&&B.map[i][j])
{
C.elem[pc]=B.elem[pb];
C.map[i][j]=1;
pb++;pc++;
}
}
}//BMMatrix_Add
5.26
void Print_OLMatrix(OLMatrix A)//以三元组格式输出十字链表表示的矩阵
{
for(i=0;i<A.mu;i++)
{
if(A.rhead[i])
for(p=A.rhead[i];p;p=p->right) //逐次遍历每一个行链表
printf("%d %d %d\n",i,p->j,p->e;
}
}//Print_OLMatrix
5.27
void OLMatrix_Add(OLMatrix &A,OLMatrix B)//把十字链表表示的矩阵B加到A上
{
for(j=1;j<=A.nu;j++) cp[j]=A.chead[j]; //向量cp存储每一列当前最后一个元素的指针
for(i=1;i<=A.mu;i++)
{
pa=A.rhead[i];pb=B.rhead[i];pre=NULL;
while(pb)
{
if(pa==NULL||pa->j>pb->j) //新插入一个结点
{
p=(OLNode*)malloc(sizeof(OLNode));
if(!pre) A.rhead[i]=p;
else pre->right=p;
p->right=pa;pre=p;
p->i=i;p->j=pb->j;p->e=pb->e; //插入行链表中
if(!A.chead[p->j])
{
A.chead[p->j]=p;
p->down=NULL;
}
else
{
while(cp[p->j]->down) cp[p->j]=cp[p->j]->down;
p->down=cp[p->j]->down;
cp[p->j]->down=p;
}
cp[p->j]=p; //插入列链表中
}//if
else if(pa->j<pb->j)
{
pre=pa;
pa=pa->right;
} //pa右移一步
else if(pa->e+pb->e)
{
pa->e+=pb->e;
pre=pa;pa=pa->right;
pb=pb->right;
} //直接相加
else
{
if(!pre) A.rhead[i]=pa->right;
else pre->right=pa->right;
p=pa;pa=pa->right; //从行链表中删除
if(A.chead[p->j]==p)
A.chead[p->j]=cp[p->j]=p->down;
else cp[p->j]->down=p->down; //从列链表中删除
free (p);
}//else
}//while
}//for
}//OLMatrix_Add
分析:本题的具体思想在课本中有详细的解释说明,
5.28
void MPList_PianDao(MPList &L)//对广义表存储结构的多元多项式求第一变元的偏导
{
for(p=L->hp->tp;p&&p->exp;pre=p,p=p->tp)
{
if(p->tag) Mul(p->hp,p->exp);
else p->coef*=p->exp; //把指数乘在本结点或其下属结点上
p->exp--;
}
pre->tp=NULL;
if(p) free (p); //删除可能存在的常数项
}//MPList_PianDao
void Mul(MPList &L,int x)//递归算法,对多元多项式L乘以x
{
for(p=L;p;p=p->tp)
{
if(!p->tag) p->coef*=x;
else Mul(p->hp,x);
}
}//Mul

5.29
void MPList_Add(MPList A,MPList B,MPList &C)//广义表存储结构的多项式相加的递归算法
{
C=(MPLNode*)malloc(sizeof(MPLNode));
if(!A->tag&&!B->tag) //原子项,可直接相加
{
C->coef=A->coef+B->coef;
if(!C->coef)
{
free(C);
C=NULL;
}
}//if
else if(A->tag&&B->tag) //两个多项式相加
{
p=A;q=B;pre=NULL;
while(p&&q)
{
if(p->exp==q->exp)
{
C=(MPLNode*)malloc(sizeof(MPLNode));
C->exp=p->exp;
MPList_Add(A->hp,B->hp,C->hp);
pre->tp=C;pre=C;
p=p->tp;q=q->tp;
}
else if(p->exp>q->exp)
{
C=(MPLNode*)malloc(sizeof(MPLNode));
C->exp=p->exp;
C->hp=A->hp;
pre->tp=C;pre=C;
p=p->tp;
}
else
{
C=(MPLNode*)malloc(sizeof(MPLNode));
C->exp=q->exp;
C->hp=B->hp;
pre->tp=C;pre=C;
q=q->tp;
}
}//while
while(p)
{
C=(MPLNode*)malloc(sizeof(MPLNode));
C->exp=p->exp;
C->hp=p->hp;
pre->tp=C;pre=C;
p=p->tp;
}
while(q)
{
C=(MPLNode*)malloc(sizeof(MPLNode));
C->exp=q->exp;
C->hp=q->hp;
pre->tp=C;pre=C;
q=q->tp;
} //将其同次项分别相加得到新的多项式,原理见第二章多项式相加一题
}//else if
else if(A->tag&&!B->tag) //多项式和常数项相加
{
x=B->coef;
for(p=A;p->tp->tp;p=p->tp);
if(p->tp->exp==0) p->tp->coef+=x; //当多项式中含有常数项时,加上常数项
if(!p->tp->coef)
{
free(p->tp);
p->tp=NULL;
}
else
{
q=(MPLNode*)malloc(sizeof(MPLNode));
q->coef=x;q->exp=0;
q->tag=0;q->tp=NULL;
p->tp=q;
} //否则新建常数项,下同
}//else if
else
{
x=A->coef;
for(p=B;p->tp->tp;p=p->tp);
if(p->tp->exp==0) p->tp->coef+=x;
if(!p->tp->coef)
{
free(p->tp);
p->tp=NULL;
}
else
{
q=(MPLNode*)malloc(sizeof(MPLNode));
q->coef=x;q->exp=0;
q->tag=0;q->tp=NULL;
p->tp=q;
}
}//else
}//MPList_Add
5.30
int GList_Getdeph(GList L)//求广义表深度的递归算法
{
if(!L->tag) return 0; //原子深度为0
else if(!L) return 1; //空表深度为1
m=GList_Getdeph(L->ptr.hp)+1;
n=GList_Getdeph(L->ptr.tp);
return m>n?m:n;
}//GList_Getdeph
5.31
void GList_Copy(GList A,GList &B)//复制广义表的递归算法
{
if(!A->tag) //当结点为原子时,直接复制
{
B->tag=0;
B->atom=A->atom;
}
else //当结点为子表时
{
B->tag=1;
if(A->ptr.hp)
{
B->ptr.hp=malloc(sizeof(GLNode));
GList_Copy(A->ptr.hp,B->ptr.hp);
} //复制表头
if(A->ptr.tp)
{
B->ptr.tp=malloc(sizeof(GLNode));
GList_Copy(A->ptr.tp,B->ptr.tp);
} //复制表尾
}//else
}//GList_Copy
5.32
int GList_Equal(GList A,GList B)//判断广义表A和B是否相等,是则返回1,否则返回0
{ //广义表相等可分三种情况:
if(!A&&!B) return 1; //空表是相等的
if(!A->tag&&!B->tag&&A->atom==B->atom) return 1;//原子的值相等
if(A->tag&&B->tag)
if(GList_Equal(A->ptr.hp,B->ptr.hp)&&GList_Equal(A->ptr.tp,B->ptr.tp))
return 1; //表头表尾都相等
return 0;
}//GList_Equal
5.33
void GList_PrintElem(GList A,int layer)//递归输出广义表的原子及其所在层次,layer表示当前层次
{
if(!A) return;
if(!A->tag) printf("%d %d\n",A->atom,layer);
else
{
GList_PrintElem(A->ptr.hp,layer+1);
GList_PrintElem(A->ptr.tp,layer); //注意尾表与原表是同一层次
}
}//GList_PrintElem
5.34
void GList_Reverse(GList A)//递归逆转广义表A
{
if(A->tag&&A->ptr.tp) //当A不为原子且表尾非空时才需逆转
{
D=C->ptr.hp;
A->ptr.tp->ptr.hp=A->ptr.hp;A->ptr.hp=D; //交换表头和表尾
GList_Reverse(A->ptr.hp);
GList_Reverse(A->ptr.tp); //逆转表头和表尾
}
}//GList_Reverse
5.35
Status Create_GList(GList &L)//根据输入创建广义表L,并返回指针
{
scanf("%c",&ch);
if(ch==' ')
{
L=NULL;
scanf("%c",&ch);
if(ch!=')') return ERROR;
return OK;
}
L=(GList)malloc(sizeof(GLNode));
L->tag=1;
if(isalphabet(ch)) //输入是字母
{
p=(GList)malloc(sizeof(GLNode)); //建原子型表头
p->tag=0;p->atom=ch;
L->ptr.hp=p;
}
else if(ch=='(') Create_GList(L->ptr.hp); //建子表型表头
else return ERROR;
scanf ("%c",&ch);
if(ch==')') L->ptr.tp=NULL;
else if(ch==',') Create_GList(L->ptr.tp); //建表尾
else return ERROR;
return OK;
}//Create_GList
分析:本题思路见书后解答,
5.36
void GList_PrintList(GList A)//按标准形式输出广义表
{
if(!A) printf("()"); //空表
else if(!A->tag) printf("%d",A->atom);//原子
else
{
printf("(");
GList_PrintList(A->ptr.hp);
if(A->ptr.tp)
{
printf(",");
GList_PrintList(A->ptr.tp);
} //只有当表尾非空时才需要打印逗号
printf(")");
}//else
}//GList_PrintList
5.37
void GList_DelElem(GList &A,int x)//从广义表A中删除所有值为x的原子
{
if(A->ptr.hp->tag) GList_DelElem(A->ptr.hp,x);
else if(!A->ptr.hp->tag&&A->ptr.hp->atom==x)
{
q=A;
A=A->ptr.tp;//删去元素值为x的表头
free(q);
GList_DelElem(A,x);
}
if(A->ptr.tp) GList_DelElem(A->ptr.tp,x);
}//GList_DelElem
5.39
void GList_PrintElem_LOrder(GList A)//按层序输出广义表A中的所有元素
{
InitQueue(Q);
for(p=L;p;p=p->ptr.tp) EnQueue(Q,p);
while(!QueueEmpty(Q))
{
DeQueue(Q,r);
if(!r->tag) printf("%d",r->atom);
else
for(r=r->ptr.hp;r;r=r->ptr.tp) EnQueue(Q,r);
}//while
}//GList_PrintElem_LOrder
分析:层序遍历的问题,一般都是借助队列来完成的,每次从队头取出一个元素的同时把它下一层的孩子插入队尾.这是层序遍历的基本思想.
.37
[问题]
试利用栈的基本操作写出先序遍历二叉树的非递归算法。
[解答提示]
改写教材p.130-131算法6.2或6.3。
将if (!visit(p->data)) return ERROR;提前。
6.43
[问题]
编写递归算法,将二叉树中所有结点的左、右子树相互交换。
Status Exchange-lr(Bitree bt){
//将bt所指二叉树中所有结点的左、右子树相互交换
if (bt && (bt->lchild || bt->rchild)) {
bt->lchild<->bt->rchild;
Exchange-lr(bt->lchild);
Exchange-lr(bt->rchild);
}
return OK;
}//Exchange-lr
6.45
[问题]
编写递归算法,对于二叉树中每一个元素值为x的结点,删去以它为根的子树,并释放相应的空间。
[解答]
Status Del-subtree(Bitree bt){
//删除bt所指二叉树,并释放相应的空间
if (bt) {
Del-subtree(bt->lchild);
Del-subtree(bt->rchild);
free(bt);
}
return OK;
}//Del-subtree
Status Search-del(Bitree bt,TelemType x){
//在bt所指的二叉树中,查找所有元素值为x的结点,并删除以它为根的子树
if (bt){
if (bt->data=x) Del-subtree(bt);
else {
Search-Del(bt->lchild,x);
Search-Del(bt->rchild,x);
}
}
return OK;
}//Search-Del
第六章 树和二叉树
6.33
int Is_Descendant_C(int u,int v)//在孩子存储结构上判断u是否v的子孙,是则返回1,否则返回0
{
if(u==v) return 1;
else
{
if(L[v])
if (Is_Descendant(u,L[v])) return 1;
if(R[v])
if (Is_Descendant(u,R[v])) return 1; //这是个递归算法
}
return 0;
}//Is_Descendant_C
6.34
int Is_Descendant_P(int u,int v)//在双亲存储结构上判断u是否v的子孙,是则返回1,否则返回0
{
for(p=u;p!=v&&p;p=T[p]);
if(p==v) return 1;
else return 0;
}//Is_Descendant_P
6.35
这一题根本不需要写什么算法,见书后注释:两个整数的值是相等的,
6.36
int Bitree_Sim(Bitree B1,Bitree B2)//判断两棵树是否相似的递归算法
{
if(!B1&&!B2) return 1;
else if(B1&&B2&&Bitree_Sim(B1->lchild,B2->lchild)&&Bitree_Sim(B1->rchild,B2->rchild))
return 1;
else return 0;
}//Bitree_Sim
6.37
void PreOrder_NotRecurve(Bitree T)//先序遍历二叉树的非递归算法
{
InitStack(S);
Push(S,T); //根指针进栈
while(!StackEmpty(S))
{
while(Gettop(S,p)&&p)
{
visit(p->data);
push(S,p->lchild);
} //向左走到尽头
pop(S,p);
if(!StackEmpty(S))
{
pop(S,p);
push(S,p->rchild); //向右一步
}
}//while
}//PreOrder_NotRecurve
6.38
typedef struct {
BTNode* ptr;
enum {0,1,2} mark;
} PMType; //有mark域的结点指针类型
void PostOrder_Stack(BiTree T)//后续遍历二叉树的非递归算法,用栈
{
PMType a;
InitStack(S); //S的元素为PMType类型
Push (S,{T,0}); //根结点入栈
while(!StackEmpty(S))
{
Pop(S,a);
switch(a.mark)
{
case 0:
Push(a.ptr,1); //修改mark域
if(a.ptr->lchild) Push({a.ptr->lchild,0}); //访问左子树
break;
case 1:
Push(a.ptr,2); //修改mark域
if(a.ptr->rchild) Push({a.ptr->rchild,0}); //访问右子树
break;
case 2:
visit(a.ptr); //访问结点,返回
}
}//while
}//PostOrder_Stack
分析:为了区分两次过栈的不同处理方式,在堆栈中增加一个mark域,mark=0表示刚刚访问此结点,mark=1表示左子树处理结束返回,mark=2表示右子树处理结束返回.每次根据栈顶元素的mark域值决定做何种动作,
6.39
typedef struct {
int data;
EBTNode *lchild;
EBTNode *rchild;
EBTNode *parent;
enum {0,1,2} mark;
} EBTNode,EBitree; //有mark域和双亲指针域的二叉树结点类型
void PostOrder_NotRecurve(EBitree T)//后序遍历二叉树的非递归算法,不用栈
{
p=T;
while(p)
switch(p->mark)
{
case 0:
p->mark=1;
if(p->lchild) p=p->lchild; //访问左子树
break;
case 1:
p->mark=2;
if(p->rchild) p=p->rchild; //访问右子树
break;
case 2:
visit(p);
p->mark=0; //恢复mark值
p=p->parent; //返回双亲结点
}
}//PostOrder_NotRecurve
分析:本题思路与上一题完全相同,只不过结点的mark值是储存在结点中的,而不是暂存在堆栈中,所以访问完毕后要将mark域恢复为0,以备下一次遍历,
6.40
typedef struct {
int data;
PBTNode *lchild;
PBTNode *rchild;
PBTNode *parent;
} PBTNode,PBitree; //有双亲指针域的二叉树结点类型
void Inorder_NotRecurve(PBitree T)//不设栈非递归遍历有双亲指针的二叉树
{
p=T;
while(p->lchild) p=p->lchild; //向左走到尽头
while(p)
{
visit(p);
if(p->rchild) //寻找中序后继:当有右子树时
{
p=p->rchild;
while(p->lchild) p=p->lchild; //后继就是在右子树中向左走到尽头
}
else if(p->parent->lchild==p) p=p->parent; //当自己是双亲的左孩子时后继就是双亲
else
{
p=p->parent;
while(p->parent&&p->parent->rchild==p) p=p->parent;
p=p->parent;
} //当自己是双亲的右孩子时后继就是向上返回直到遇到自己是在其左子树中的祖先
}//while
}//Inorder_NotRecurve
6.41
int c,k; //这里把k和计数器c作为全局变量处理
void Get_PreSeq(Bitree T)//求先序序列为k的结点的值
{
if(T)
{
c++; //每访问一个子树的根都会使前序序号计数器加1
if(c==k)
{
printf("Value is %d\n",T->data);
exit (1);
}
else
{
Get_PreSeq(T->lchild); //在左子树中查找
Get_PreSeq(T->rchild); //在右子树中查找
}
}//if
}//Get_PreSeq
main()
{
...
scanf("%d",&k);
c=0; //在主函数中调用前,要给计数器赋初值0
Get_PreSeq(T,k);
...
}//main
6.42
int LeafCount_BiTree(Bitree T)//求二叉树中叶子结点的数目
{
if(!T) return 0; //空树没有叶子
else if(!T->lchild&&!T->rchild) return 1; //叶子结点
else return Leaf_Count(T->lchild)+Leaf_Count(T->rchild);//左子树的叶子数加上右子树的叶子数
}//LeafCount_BiTree
6.43
void Bitree_Revolute(Bitree T)//交换所有结点的左右子树
{
T->lchild<->T->rchild; //交换左右子树
if(T->lchild) Bitree_Revolute(T->lchild);
if(T->rchild) Bitree_Revolute(T->rchild); //左右子树再分别交换各自的左右子树
}//Bitree_Revolute
6.44
int Get_Sub_Depth(Bitree T,int x)//求二叉树中以值为x的结点为根的子树深度
{
if(T->data==x)
{
printf("%d\n",Get_Depth(T)); //找到了值为x的结点,求其深度
exit 1;
}
else
{
if(T->lchild) Get_Sub_Depth(T->lchild,x);
if(T->rchild) Get_Sub_Depth(T->rchild,x); //在左右子树中继续寻找
}
}//Get_Sub_Depth
int Get_Depth(Bitree T)//求子树深度的递归算法
{
if(!T) return 0;
else
{
m=Get_Depth(T->lchild);
n=Get_Depth(T->rchild);
return (m>n?m:n)+1;
}
}//Get_Depth
6.45
void Del_Sub_x(Bitree T,int x)//删除所有以元素x为根的子树
{
if(T->data==x) Del_Sub(T); //删除该子树
else
{
if(T->lchild) Del_Sub_x(T->lchild,x);
if(T->rchild) Del_Sub_x(T->rchild,x); //在左右子树中继续查找
}//else
}//Del_Sub_x
void Del_Sub(Bitree T)//删除子树T
{
if(T->lchild) Del_Sub(T->lchild);
if(T->rchild) Del_Sub(T->rchild);
free(T);
}//Del_Sub
6.46
void Bitree_Copy_NotRecurve(Bitree T,Bitree &U)//非递归复制二叉树
{
InitStack(S1);InitStack(S2);
push(S1,T); //根指针进栈
U=(BTNode*)malloc(sizeof(BTNode));
U->data=T->data;
q=U;push(S2,U);
while(!StackEmpty(S))
{
while(Gettop(S1,p)&&p)
{
q->lchild=(BTNode*)malloc(sizeof(BTNode));
q=q->lchild;q->data=p->data;
push(S1,p->lchild);
push(S2,q);
} //向左走到尽头
pop(S1,p);
pop(S2,q);
if(!StackEmpty(S1))
{
pop(S1,p);pop(S2,q);
q->rchild=(BTNode*)malloc(sizeof(BTNode));
q=q->rchild;q->data=p->data;
push(S1,p->rchild); //向右一步
push(S2,q);
}
}//while
}//BiTree_Copy_NotRecurve
分析:本题的算法系从6.37改写而来,
6.47
void LayerOrder(Bitree T)//层序遍历二叉树
{
InitQueue(Q); //建立工作队列
EnQueue(Q,T);
while(!QueueEmpty(Q))
{
DeQueue(Q,p);
visit(p);
if(p->lchild) EnQueue(Q,p->lchild);
if(p->rchild) EnQueue(Q,p->rchild);
}
}//LayerOrder
6.48
int found=FALSE;
Bitree* Find_Near_Ancient(Bitree T,Bitree p,Bitree q)//求二叉树T中结点p和q的最近共同祖先
{
Bitree pathp[ 100 ],pathq[ 100 ] //设立两个辅助数组暂存从根到p,q的路径
Findpath(T,p,pathp,0);
found=FALSE;
Findpath(T,q,pathq,0); //求从根到p,q的路径放在pathp和pathq中
for(i=0;pathp[i]==pathq[i]&&pathp[i];i++); //查找两条路径上最后一个相同结点
return pathp[--i];
}//Find_Near_Ancient
void Findpath(Bitree T,Bitree p,Bitree path[ ],int i)//求从T到p路径的递归算法
{
if(T==p)
{
found=TRUE;
return; //找到
}
path[i]=T; //当前结点存入路径
if(T->lchild) Findpath(T->lchild,p,path,i+1); //在左子树中继续寻找
if(T->rchild&&!found) Findpath(T->rchild,p,path,i+1); //在右子树中继续寻找
if(!found) path[i]=NULL; //回溯
}//Findpath
6.49
int IsFull_Bitree(Bitree T)//判断二叉树是否完全二叉树,是则返回1,否则返回0
{
InitQueue(Q);
flag=0;
EnQueue(Q,T); //建立工作队列
while(!QueueEmpty(Q))
{
DeQueue(Q,p);
if(!p) flag=1;
else if(flag) return 0;
else
{
EnQueue(Q,p->lchild);
EnQueue(Q,p->rchild); //不管孩子是否为空,都入队列
}
}//while
return 1;
}//IsFull_Bitree
分析:该问题可以通过层序遍历的方法来解决.与6.47相比,作了一个修改,不管当前结点是否有左右孩子,都入队列.这样当树为完全二叉树时,遍历时得到是一个连续的不包含空指针的序列.反之,则序列中会含有空指针,
6.50
Status CreateBitree_Triplet(Bitree &T)//输入三元组建立二叉树
{
if(getchar()!='^') return ERROR;
T=(BTNode*)malloc(sizeof(BTNode));
p=T;p->data=getchar();
getchar(); //滤去多余字符
InitQueue(Q);
EnQueue(Q,T);
while((parent=getchar())!='^'&&(child=getchar())&&(side=getchar()))
{
while(QueueHead(Q)!=parent&&!QueueEmpty(Q)) DeQueue(Q,e);
if(QueueEmpty(Q)) return ERROR; //未按层序输入
p=QueueHead(Q);
q=(BTNode*)malloc(sizeof(BTNode));
if(side=='L') p->lchild=q;
else if(side=='R') p->rchild=q;
else return ERROR; //格式不正确
q->data=child;
EnQueue(Q,q);
}
return OK;
}//CreateBitree_Triplet
6.51
Status Print_Expression(Bitree T)//按标准形式输出以二叉树存储的表达式
{
if(T->data是字母) printf("%c",T->data);
else if(T->data是操作符)
{
if(!T->lchild||!T->rchild) return ERROR; //格式错误
if(T->lchild->data是操作符&&T->lchild->data优先级低于T->data)
{
printf("(");
if(!Print_Expression(T->lchild)) return ERROR;
printf(")");
} //注意在什么情况下要加括号
else if(!Print_Expression(T->lchild)) return ERROR;
if(T->rchild->data是操作符&&T->rchild->data优先级低于T->data)
{
printf("(");
if(!Print_Expression(T->rchild)) return ERROR;
printf(")");
}
else if(!Print_Expression(T->rchild)) return ERROR;
}
else return ERROR; //非法字符
return OK;
}//Print_Expression
6.52
typedef struct{
BTNode node;
int layer;
} BTNRecord; //包含结点所在层次的记录类型
int FanMao(Bitree T)//求一棵二叉树的"繁茂度"
{
int countd; //count数组存放每一层的结点数
InitQueue(Q); //Q的元素为BTNRecord类型
EnQueue(Q,{T,0});
while(!QueueEmpty(Q))
{
DeQueue(Q,r);
count[r.layer]++;
if(r.node->lchild) EnQueue(Q,{r.node->lchild,r.layer+1});
if(r.node->rchild) EnQueue(Q,{r.node->rchild,r.layer+1});
} //利用层序遍历来统计各层的结点数
h=r.layer; //最后一个队列元素所在层就是树的高度
for(maxn=count[ 0 ],i=1;count[i];i++)
if(count[i]>maxn) maxn=count[i]; //求层最大结点数
return h*maxn;
}//FanMao
分析:如果不允许使用辅助数组,就必须在遍历的同时求出层最大结点数,形式上会复杂一些,你能写出来吗?
6.53
int maxh;
Status Printpath_MaxdepthS1(Bitree T)//求深度等于树高度减一的最靠左的结点
{
Bitree pathd;
maxh=Get_Depth(T); //Get_Depth函数见6.44
if(maxh<2) return ERROR; //无符合条件结点
Find_h(T,1);
return OK;
}//Printpath_MaxdepthS1
void Find_h(Bitree T,int h)//寻找深度为maxh-1的结点
{
path[h]=T;
if(h==maxh-1)
{
for(i=1;path[i];i++) printf("%c",path[i]->data);
exit; //打印输出路径
}
else
{
if(T->lchild) Find_h(T->lchild,h+1);
if(T->rchild) Find_h(T->rchild,h+1);
}
path[h]=NULL; //回溯
}//Find_h
6.54
Status CreateBitree_SqList(Bitree &T,SqList sa)//根据顺序存储结构建立二叉链表
{
Bitree ptr[sa.last+1]; //该数组储存与sa中各结点对应的树指针
if(!sa.last)
{
T=NULL; //空树
return;
}
ptr[ 1 ]=(BTNode*)malloc(sizeof(BTNode));
ptr[ 1 ]->data=sa.elem[ 1 ]; //建立树根
T=ptr[ 1 ];
for(i=2;i<=sa.last;i++)
{
if(!sa.elem[i]) return ERROR; //顺序错误
ptr[i]=(BTNode*)malloc(sizeof(BTNode));
ptr[i]->data=sa.elem[i];
j=i/2; //找到结点i的双亲j
if(i-j*2) ptr[j]->rchild=ptr[i]; //i是j的右孩子
else ptr[j]->lchild=ptr[i]; //i是j的左孩子
}
return OK;
}//CreateBitree_SqList
6.55
int DescNum(Bitree T)//求树结点T的子孙总数填入DescNum域中,并返回该数
{
if(!T) return -1;
else d=(DescNum(T->lchild)+DescNum(T->rchild)+2); //计算公式
T->DescNum=d;
return d;
}//DescNum
分析:该算法时间复杂度为O(n),n为树结点总数.注意:为了能用一个统一的公式计算子孙数目,所以当T为空指针时,要返回-1而不是0,
6.56
Bitree PreOrder_Next(Bitree p)//在先序后继线索二叉树中查找结点p的先序后继,并返回指针
{
if(p->lchild) return p->lchild;
else return p->rchild;
}//PreOrder_Next
分析:总觉得不会这么简单.是不是哪儿理解错了?
6.57
Bitree PostOrder_Next(Bitree p)//在后序后继线索二叉树中查找结点p的后序后继,并返回指针
{
if(p->rtag) return p->rchild; //p有后继线索
else if(!p->parent) return NULL; //p是根结点
else if(p==p->parent->rchild) return p->parent; //p是右孩子
else if(p==p->parent->lchild&&p->parent->tag)
return p->parent; //p是左孩子且双亲没有右孩子
else //p是左孩子且双亲有右孩子
{
q=p->parent->rchild;
while(!q->ltag||!q->rtag)
{
if(!q->ltag) q=q->lchild;
else q=q->rchild;
} //从p的双亲的右孩子向下走到底
return q;
}//else
}//PostOrder_Next
6.58
Status Insert_BiThrTree(BiThrTree &T,BiThrTree &p,BiThrTree &x)//在中序线索二叉树T的结点p下插入子树x
{
if(!p->ltag&&!p->rtag) return INFEASIBLE; //无法插入
if(p->ltag) //x作为p的左子树
{
s=p->lchild; //s为p的前驱
p->ltag=Link;
p->lchild=x;
q=x;
while(q->lchild) q=q->lchild;
q->lchild=s; //找到子树中的最左结点,并修改其前驱指向s
q=x;
while(q->rchild) q=q->rchild;
q->rchild=p; //找到子树中的最右结点,并修改其前驱指向p
}
else //x作为p的右子树
{
s=p->rchild; //s为p的后继
p->rtag=Link;
p->rchild=x;
q=x;
while(q->rchild) q=q->rchild;
q->rchild=s; //找到子树中的最右结点,并修改其前驱指向s
q=x;
while(q->lchild) q=q->lchild;
q->lchild=p; //找到子树中的最左结点,并修改其前驱指向p
}
return OK;
}//Insert_BiThrTree
6.59
void Print_CSTree(CSTree T)//输出孩子兄弟链表表示的树T的各边
{
for(child=T->firstchild;child;child=child->nextsib)
{
printf("(%c,%c),",T->data,child->data);
Print_CSTree(child);
}
}//Print_CSTree
6.60
int LeafCount_CSTree(CSTree T)//求孩子兄弟链表表示的树T的叶子数目
{
if(!T->firstchild) return 1; //叶子结点
else
{
count=0;
for(child=T->firstchild;child;child=child->nextsib)
count+=LeafCount_CSTree(child);
return count; //各子树的叶子数之和
}
}//LeafCount_CSTree
6.61
int GetDegree_CSTree(CSTree T)//求孩子兄弟链表表示的树T的度
{
if(!T->firstchild) return 0; //空树
else
{
degree=0;
for(p=T->firstchild;p;p=p->nextsib) degree++;//本结点的度
for(p=T->firstchild;p;p=p->nextsib)
{
d=GetDegree_CSTree(p);
if(d>degree) degree=d; //孩子结点的度的最大值
}
return degree;
}//else
}//GetDegree_CSTree
6.62
int GetDepth_CSTree(CSTree T)//求孩子兄弟链表表示的树T的深度
{
if(!T) return 0; //空树
else
{
for(maxd=0,p=T->firstchild;p;p=p->nextsib)
if((d=GetDepth_CSTree(p))>maxd) maxd=d; //子树的最大深度
return maxd+1;
}
}//GetDepth_CSTree
6.63
int GetDepth_CTree(CTree A)//求孩子链表表示的树A的深度
{
return SubDepth(A.r);
}//GetDepth_CTree
int SubDepth(int T)//求子树T的深度
{
if(!A.nodes[T].firstchild) return 1;
for(sd=1,p=A.nodes[T].firstchild;p;p=p->next)
if((d=SubDepth(p->child))>sd) sd=d;
return sd+1;
}//SubDepth
6.64
int GetDepth_PTree(PTree T)//求双亲表表示的树T的深度
{
maxdep=0;
for(i=0;i<T.n;i++)
{
dep=0;
for(j=i;j>=0;j=T.nodes[j].parent) dep++; //求每一个结点的深度
if(dep>maxdep) maxdep=dep;
}
return maxdep;
}//GetDepth_PTree
6.65
char Pred,Ind; //假设前序序列和中序序列已经分别储存在数组Pre和In中
Bitree Build_Sub(int Pre_Start,int Pre_End,int In_Start,int In_End)//由子树的前序和中序序列建立其二叉链表
{
sroot=(BTNode*)malloc(sizeof(BTNode)); //建根
sroot->data=Pre[Pre_Start];
for(i=In_Start;In[i]!=sroot->data;i++); //在中序序列中查找子树根
leftlen=i-In_Start;
rightlen=In_End-i; //计算左右子树的大小
if(leftlen)
{
lroot=Build_Sub(Pre_Start+1,Pre_Start+leftlen,In_Start,In_Start+leftlen-1);
sroot->lchild=lroot;
} //建左子树,注意参数表的计算
if(rightlen)
{
rroot=Build_Sub(Pre_End-rightlen+1,Pre_End,In_End-rightlen+1,In_End);
sroot->rchild=rroot;
} //建右子树,注意参数表的计算
return sroot; //返回子树根
}//Build_Sub
main()
{
...
Build_Sub(1,n,1,n); //初始调用参数,n为树结点总数
...
}
分析:本算法利用了这样一个性质,即一棵子树在前序和中序序列中所占的位置总是连续的.因此,就可以用起始下标和终止下标来确定一棵子树.Pre_Start,Pre_End,In_Start和In_End分别指示子树在前序子序列里的起始下标,终止下标,和在中序子序列里的起始和终止下标,
6.66
typedef struct{
CSNode *ptr;
CSNode *lastchild;
} NodeMsg; //结点的指针和其最后一个孩子的指针
Status Bulid_CSTree_PTree(PTree T)//由树T的双亲表构造其孩子兄弟链表
{
NodeMsg Treed;
for(i=0;i<T.n;i++)
{
Tree[i].ptr=(CSNode*)malloc(sizeof(CSNode));
Tree[i].ptr->data=T.node[i].data; //建结点
if(T.nodes[i].parent>=0) //不是树根
{
j=T.nodes[i].parent; //本算法要求双亲表必须是按层序存储
if(!(Tree[j].lastchild)) //双亲当前还没有孩子
Tree[j].ptr->firstchild=Tree[i].ptr; //成为双亲的第一个孩子
else //双亲已经有了孩子
Tree[j].lastchild->nextsib=Tree[i].ptr; //成为双亲最后一个孩子的下一个兄弟
Tree[j].lastchild=Tree[i].ptr; //成为双亲的最后一个孩子
}//if
}//for
}//Bulid_CSTree_PTree
6.67
typedef struct{
char data;
CSNode *ptr;
CSNode *lastchild;
} NodeInfo; //结点数据,结点指针和最后一个孩子的指针
Status CreateCSTree_Duplet(CSTree &T)//输入二元组建立树的孩子兄弟链表
{
NodeInfo Treed;
n=1;k=0;
if(getchar()!='^') return ERROR; //未按格式输入
if((c=getchar())=='^') T=NULL; //空树
Tree[ 0 ].ptr=(CSNode*)malloc(sizeof(CSNode));
Tree[ 0 ].data=c;
Tree[ 0 ].ptr->data=c;
while((p=getchar())!='^'&&(c=getchar())!='^')
{
Tree[n].ptr=(CSNode*)malloc(sizeof(CSNode));
Tree[n].data=c;
Tree[n].ptr->data=c;
for(k=0;Tree[k].data!=p;k++); //查找当前边的双亲结点
if(Tree[k].data!=p) return ERROR; //未找到:未按层序输入
r=Tree[k].ptr;
if(!r->firstchild)
r->firstchild=Tree[n].ptr;
else Tree[k].lastchild->nextsib=Tree[n].ptr;
Tree[k].lastchild=Tree[n].ptr; //这一段含义同上一题
n++;
}//while
return OK;
}//CreateCSTree_Duplet
6.68
Status CreateCSTree_Degree(char node[ ],int degree[ ])//由结点的层序序列和各结点的度构造树的孩子兄弟链表
{
CSNode * ptrd; //树结点指针的辅助存储
ptr[ 0 ]=(CSNode*)malloc(sizeof(CSNode));
i=0;k=1; //i为当前结点序号,k为当前孩子的序号
while(node[i])
{
ptr[i]->data=node[i];
d=degree[i];
if(d)
{
ptr[k++]=(CSNode*)malloc(sizeof(CSNode)); //k为当前孩子的序号
ptr[i]->firstchild=ptr[k]; //建立i与第一个孩子k之间的联系
for(j=2;j<=d;j++)
{
ptr[k++]=(CSNode*)malloc(sizeof(CSNode));
ptr[k-1]->nextsib=ptr[k]; //当结点的度大于1时,为其孩子建立兄弟链表
}//for
}//if
i++;
}//while
}//CreateCSTree_Degree
6.69
void Print_BiTree(BiTree T,int i)//按树状打印输出二叉树的元素,i表示结点所在层次,初次调用时i=0
{
if(T->rchild) Print_BiTree(T->rchild,i+1);
for(j=1;j<=i;j++) printf(" "); //打印i个空格以表示出层次
printf("%c\n",T->data); //打印T元素,换行
if(T->lchild) Print_BiTree(T->rchild,i+1);
}//Print_BiTree
分析:该递归算法实际上是带层次信息的中序遍历,只不过按照题目要求,顺序为先右后左,
6.70
Status CreateBiTree_GList(BiTree &T)//由广义表形式的输入建立二叉链表
{
c=getchar();
if(c=='#') T=NULL; //空子树
else
{
T=(CSNode*)malloc(sizeof(CSNode));
T->data=c;
if(getchar()!='(') return ERROR;
if(!CreateBiTree_GList(pl)) return ERROR;
T->lchild=pl;
if(getchar()!=',') return ERROR;
if(!CreateBiTree_GList(pr)) return ERROR;
T->rchild=pr;
if(getchar()!=')') return ERROR; //这些语句是为了保证输入符合A(B,C)的格式
}
return OK;
}//CreateBiTree_GList
6.71
void Print_CSTree(CSTree T,int i)//按凹入表形式打印输出树的元素,i表示结点所在层次,初次调用时i=0
{
for(j=1;j<=i;j++) printf(" "); //留出i个空格以表现出层次
printf("%c\n",T->data); //打印元素,换行
for(p=T->firstchild;p;p=p->nextsib)
Print_CSTree(p,i+1); //打印子树
}//Print_CSTree
6.72
void Print_CTree(int e,int i)//按凹入表形式打印输出树的元素,i表示结点所在层次
{
for(j=1;j<=i;j++) printf(" "); //留出i个空格以表现出层次
printf("%c\n",T.nodes[e].data); //打印元素,换行
for(p=T.nodes[e].firstchild;p;p=p->next)
Print_CSTree(p->child,i+1); //打印子树
}//Print_CSTree
main()
{
...
Print_CTree(T.r,0); //初次调用时i=0
...
}//main
6.73
char c; //全局变量,指示当前字符
Status CreateCSTree_GList(CSTree &T)//由广义表形式的输入建立孩子兄弟链表
{
c=getchar();
T=(CSNode*)malloc(sizeof(CSNode));
T->data=c;
if((c=getchar())=='(') //非叶结点
{
if(!CreateCSTree_GList(fc)) return ERROR; //建第一个孩子
T->firstchild=fc;
for(p=fc;c==',';p->nextsib=nc,p=nc) //建兄弟链
if(!CreateCSTree_GList(nc)) return ERROR;
p->nextsib=NULL;
if((c=getchar())!=')') return ERROR; //括号不配对
}
else T->firtchild=NULL; //叶子结点
return OK;
}//CreateBiTree_GList
分析:书后给出了两个间接递归的算法,事实上合成一个算法在形式上可能更好一些.本算法另一个改进之处在于加入了广义表格式是否合法的判断,
6.74
void PrintGlist_CSTree(CSTree T)//按广义表形式输出孩子兄弟链表表示的树
{
printf("%c",T->data);
if(T->firstchild) //非叶结点
{
printf("(");
for(p=T->firstchild;p;p=p->nextsib)
{
PrintGlist_CSTree(p);
if(p->nextsib) printf(","); //最后一个孩子后面不需要加逗号
}
printf(")");
}//if
}//PrintGlist_CSTree
6.75
char c;
int pos=0; //pos是全局变量,指示已经分配到了哪个结点
Status CreateCTree_GList(CTree &T,int &i)//由广义表形式的输入建立孩子链表
{
c=getchar();
T.nodes[pos].data=c;
i=pos++; //i是局部变量,指示当前正在处理的子树根
if((c=getchar())=='(') //非叶结点
{
CreateCTree_GList();
p=(CTBox*)malloc(sizeof(CTBox));
T.nodes[i].firstchild=p;
p->child=pos; //建立孩子链的头
for(;c==',';p=p->next) //建立孩子链
{
CreateCTree_GList(T,j); //用j返回分配得到的子树根位置
p->child=j;
p->next=(CTBox*)malloc(sizeof(CTBox));
}
p->next=NULL;
if((c=getchar())!=')') return ERROR; //括号不配对
}//if
else T.nodes[i].firtchild=NULL; //叶子结点
return OK;
}//CreateBiTree_GList
分析:该算法中,pos变量起着"分配"结点在表中的位置的作用,是按先序序列从上向下分配,因此树根T.r一定等于0,而最终的pos值就是结点数T.n,
6.76
void PrintGList_CTree(CTree T,int i)//按广义表形式输出孩子链表表示的树
{
printf("%c",T.nodes[i].data);
if(T.nodes[i].firstchild) //非叶结点
{
printf("(");
for(p=T->firstchild;p;p=p->nextsib)
{
PrintGlist_CSTree(T,p->child);
if(p->nextsib) printf(","); //最后一个孩子后面不需要加逗号
}
printf(")");
}//if
}//PrintGlist_CTree
《数据结构题集》P.190
4.25
typedef struct {
char? *ch;
int length;
}HString;
 
Status Replace(HString &s,HString t,HString v){
//用串v替换串s中所有的子串t
if (t.length==0) return ERROR;
i=0; j=0;
while (i<=s.length-1) {
while (i<=s.length-1 && j<=t.length-1) {
if (s.ch[i]==t.ch[j]){++i; ++j;}
else {i=i-j+2; j=0;}
}//while
if (j<=t.length-1) return OK;
pos=i-t.length;
if (!(s.ch=(char *)realloc(s.ch,(s.length-t.length+v.length)*sizeof(char))))
exit(OVERFLOW);
for (k=s.length-1; k>=i; --k)
s.ch[k-t.length+v.length]=s.ch[k];
s.ch[pos..pos+v.length-1]=v[0..v.length-1];
s.length=s.length-t.length+v.length;
i=pos+v.length; j=0;
}//while
return OK;
}//Replace
注意:在这里不能用串的其它基本操作来实现Replace
 
 
 
(以下内容来自http://bbs.kaoyan.com)
第四章 串
4.10
void String_Reverse(Stringtype s,Stringtype &r)//求s的逆串r
{
StrAssign(r,''); //初始化r为空串
for(i=Strlen(s);i;i--)
{
StrAssign(c,SubString(s,i,1));
StrAssign(r,Concat(r,c)); //把s的字符从后往前添加到r中
}
}//String_Reverse
4.11
void String_Subtract(Stringtype s,Stringtype t,Stringtype &r)//求所有包含在串s中而t中没有的字符构成的新串r
{
StrAssign(r,'');
for(i=1;i<=Strlen(s);i++)
{
StrAssign(c,SubString(s,i,1));
for(j=1;j<i&&StrCompare(c,SubString(s,j,1));j++); //判断s的当前字符c是否第一次出现
if(i==j)
{
for(k=1;k<=Strlen(t)&&StrCompare(c,SubString(t,k,1));k++); //判断当前字符是否包含在t中
if(k>Strlen(t)) StrAssign(r,Concat(r,c));
}
}//for
}//String_Subtract
4.12
int Replace(Stringtype &S,Stringtype T,Stringtype V);//将串S中所有子串T替换为V,并返回置换次数
{
for(n=0,i=1;i<=Strlen(S)-Strlen(T)+1;i++) //注意i的取值范围
if(!StrCompare(SubString(S,i,Strlen(T)),T)) //找到了与T匹配的子串
{ //分别把T的前面和后面部分保存为head和tail
StrAssign(head,SubString(S,1,i-1));
StrAssign(tail,SubString(S,i+Strlen(T),Strlen(S)-i-Strlen(T)+1));
StrAssign(S,Concat(head,V));
StrAssign(S,Concat(S,tail)); //把head,V,tail连接为新串
i+=Strlen(V); //当前指针跳到插入串以后
n++;
}//if
return n;
}//Replace
分析:i+=Strlen(V);这一句是必需的,也是容易忽略的.如省掉这一句,则在某些情况下,会引起不希望的后果,虽然在大多数情况下没有影响.请思考:设S='place',T='ace',V='face',则省掉i+=Strlen(V);运行时会出现什么结果?
4.13
int Delete_SubString(Stringtype &s,Stringtype t)//从串s中删除所有与t相同的子串,并返回删除次数
{
for(n=0,i=1;i<=Strlen(s)-Strlen(t)+1;i++)
if(!StrCompare(SubString(s,i,Strlen(t)),t))
{
StrAssign(head,SubString(S,1,i-1));
StrAssign(tail,SubString(S,i+Strlen(t),Strlen(s)-i-Strlen(t)+1));
StrAssign(S,Concat(head,tail)); //把head,tail连接为新串
n++;
}//if
return n,
}//Delete_SubString
4.14
Status NiBoLan_to_BoLan(Stringtype str,Stringtype &new)//把前缀表达式str转换为后缀式new
{
Initstack(s); //s的元素为Stringtype类型
for(i=1;i<=Strlen(str);i++)
{
r=SubString(str,i,1);
if(r为字母) push(s,r);
else
{
if(StackEmpty(s)) return ERROR;
pop(s,a);
if(StackEmpty(s)) return ERROR;
pop(s,b);
StrAssign(t,Concat(r,b));
StrAssign(c,Concat(t,a)); //把算符r,子前缀表达式a,b连接为新子前缀表达式c
push(s,c);
}
}//for
pop(s,new);
if(!StackEmpty(s)) return ERROR;
return OK;
}//NiBoLan_to_BoLan
分析:基本思想见书后注释3.23.请读者用此程序取代作者早些时候对3.23题给出的程序,
4.15
void StrAssign(Stringtype &T,char chars&#;)//用字符数组chars给串T赋值,Stringtype的定义见课本
{
for(i=0,T[ 0 ]=0;chars[i];T[ 0 ]++,i++) T[i+1]=chars[i];
}//StrAssign
4.16
char StrCompare(Stringtype s,Stringtype t)//串的比较,s>t时返回正数,s=t时返回0,s<t时返回负数
{
for(i=1;i<=s[ 0 ]&&i<=t[ 0 ]&&s[i]==t[i];i++);
if(i>s[ 0 ]&&i>t[ 0 ]) return 0;
else if(i>s[ 0 ]) return -t[i];
else if(i>t[ 0 ]) return s[i];
else return s[i]-t[i];
}//StrCompare
4.17
int String_Replace(Stringtype &S,Stringtype T,Stringtype V);//将串S中所有子串T替换为V,并返回置换次数
{
for(n=0,i=1;i<=S[ 0 ]-T[ 0 ]+1;i++)
{
for(j=i,k=1;T[k]&&S[j]==T[k];j++,k++);
if(k>T[ 0 ]) //找到了与T匹配的子串:分三种情况处理
{
if(T[ 0 ]==V[ 0 ])
for(l=1;l<=T[ 0 ];l++) //新子串长度与原子串相同时:直接替换
S[i+l-1]=V[l];
else if(T[ 0 ]<V[ 0 ]) //新子串长度大于原子串时:先将后部右移
{
for(l=S[ 0 ];l>=i+T[ 0 ];l--)
S[l+V[ 0 ]-T[ 0 ]]=S[l];
for(l=1;l<=V[ 0 ];l++)
S[i+l-1]=V[l];
}
else //新子串长度小于原子串时:先将后部左移
{
for(l=i+V[ 0 ];l<=S[ 0 ]+V[ 0 ]-T[ 0 ];l++)
S[l]=S[l-V[ 0 ]+T[ 0 ]];
for(l=1;l<=V[ 0 ];l++)
S[i+l-1]=V[l];
}
S[ 0 ]=S[ 0 ]-T[ 0 ]+V[ 0 ];
i+=V[ 0 ];n++;
}//if
}//for
return n;
}//String_Replace
4.18
typedef struct {
char ch;
int num;
} mytype;
void StrAnalyze(Stringtype S)//统计串S中字符的种类和个数
{
mytype T[MAXSIZE]; //用结构数组T存储统计结果
for(i=1;i<=S[ 0 ];i++)
{
c=S[i];j=0;
while(T[j].ch&&T[j].ch!=c) j++; //查找当前字符c是否已记录过
if(T[j].ch) T[j].num++;
else T[j]={c,1};
}//for
for(j=0;T[j].ch;j++)
printf("%c:%d\n",T[j].ch,T[j].num);
}//StrAnalyze
4.19
void Subtract_String(Stringtype s,Stringtype t,Stringtype &r)//求所有包含在串s中而t中没有的字符构成的新串r
{
r[ 0 ]=0;
for(i=1;i<=s[ 0 ];i++)
{
c=s[i];
for(j=1;j<i&&s[j]!=c;j++); //判断s的当前字符c是否第一次出现
if(i==j)
{
for(k=1;k<=t[ 0 ]&&t[k]!=c;k++); //判断当前字符是否包含在t中
if(k>t[ 0 ]) r[++r[ 0 ]]=c;
}
}//for
}//Subtract_String
4.20
int SubString_Delete(Stringtype &s,Stringtype t)//从串s中删除所有与t相同的子串,并返回删除次数
{
for(n=0,i=1;i<=s[ 0 ]-t[ 0 ]+1;i++)
{
for(j=1;j<=t[ 0 ]&&s[i+j-1]==t[i];j++);
if(j>m) //找到了与t匹配的子串
{
for(k=i;k<=s[ 0 ]-t[ 0 ];k++) s[k]=s[k+t[ 0 ]]; //左移删除
s[ 0 ]-=t[ 0 ];n++;
}
}//for
return n;
}//Delete_SubString
4.21
typedef struct{
char ch;
LStrNode *next;
} LStrNode,*LString; //链串结构
void StringAssign(LString &s,LString t)//把串t赋值给串s
{
s=malloc(sizeof(LStrNode));
for(q=s,p=t->next;p;p=p->next)
{
r=(LStrNode*)malloc(sizeof(LStrNode));
r->ch=p->ch;
q->next=r;q=r;
}
q->next=NULL;
}//StringAssign
void StringCopy(LString &s,LString t)//把串t复制为串s.与前一个程序的区别在于,串s业已存在.
{
for(p=s->next,q=t->next;p&&q;p=p->next,q=q->next)
{
p->ch=q->ch;pre=p;
}
while(q)
{
p=(LStrNode*)malloc(sizeof(LStrNode));
p->ch=q->ch;
pre->next=p;pre=p;
}
p->next=NULL;
}//StringCopy
char StringCompare(LString s,LString t)//串的比较,s>t时返回正数,s=t时返回0,s<t时返回负数
{
for(p=s->next,q=t->next;p&&q&&p->ch==q->ch;p=p->next,q=q->next);
if(!p&&!q) return 0;
else if(!p) return -(q->ch);
else if(!q) return p->ch;
else return p->ch-q->ch;
}//StringCompare
int StringLen(LString s)//求串s的长度(元素个数)
{
for(i=0,p=s->next;p;p=p->next,i++);
return i;
}//StringLen
LString * Concat(LString s,LString t)//连接串s和串t形成新串,并返回指针
{
p=malloc(sizeof(LStrNode));
for(q=p,r=s->next;r;r=r->next)
{
q->next=(LStrNode*)malloc(sizeof(LStrNode));
q=q->next;
q->ch=r->ch;
}//for //复制串s
for(r=t->next;r;r=r->next)
{
q->next=(LStrNode*)malloc(sizeof(LStrNode));
q=q->next;
q->ch=r->ch;
}//for //复制串t
q->next=NULL;
return p;
}//Concat
LString * Sub_String(LString s,int start,int len)//返回一个串,其值等于串s从start位置起长为len的子串
{
p=malloc(sizeof(LStrNode));q=p;
for(r=s;start;start--,r=r->next); //找到start所对应的结点指针r
for(i=1;i<=len;i++,r=r->next)
{
q->next=(LStrNode*)malloc(sizeof(LStrNode));
q=q->next;
q->ch=r->ch;
} //复制串t
q->next=NULL;
return p;
}//Sub_String
4.22
4.23
书后对这两题给出了一些提示.由于作者对这些提示的可行性感到困惑,或者尚没有完全理解其意图,致使无法给出参考算法.请读者认真思考后自行写出,欢迎发表你的解答,
4.24
void HString_Concat(HString s1,HString s2,HString &t)//将堆结构表示的串s1和s2连接为新串t
{
if(t.ch) free(t.ch);
t.ch=malloc((s1.length+s2.length)*sizeof(char));
for(i=1;i<=s1.length;i++) t.ch[i-1]=s1.ch[i-1];
for(j=1;j<=s2.length;j++,i++) t.ch[i-1]=s2.ch[j-1];
t.length=s1.length+s2.length;
}//HString_Concat
4.25
int HString_Replace(HString &S,HString T,HString V)//堆结构串上的置换操作,返回置换次数
{
for(n=0,i=0;i<=S.length-T.length;i++)
{
for(j=i,k=0;k<T.length&&S.ch[j]==T.ch[k];j++,k++);
if(k==T.length) //找到了与T匹配的子串:分三种情况处理
{
if(T.length==V.length)
for(l=1;l<=T.length;l++) //新子串长度与原子串相同时:直接替换
S.ch[i+l-1]=V.ch[l-1];
else if(T.length<V.length) //新子串长度大于原子串时:先将后部右移
{
for(l=S.length-1;l>=i+T.length;l--)
S.ch[l+V.length-T.length]=S.ch[l];
for(l=0;l<V.length;l++)
S[i+l]=V[l];
}
else //新子串长度小于原子串时:先将后部左移
{
for(l=i+V.length;l<S.length+V.length-T.length;l++)
S.ch[l]=S.ch[l-V.length+T.length];
for(l=0;l<V.length;l++)
S[i+l]=V[l];
}
S.length+=V.length-T.length;
i+=V.length;n++;
}//if
}//for
return n;
}//HString_Replace
4.26
Status HString_Insert(HString &S,int pos,HString T)//把T插入堆结构表示的串S的第pos个字符之前
{
if(pos<1) return ERROR;
if(pos>S.length) pos=S.length+1;//当插入位置大于串长时,看作添加在串尾
S.ch=realloc(S.ch,(S.length+T.length)*sizeof(char));
for(i=S.length-1;i>=pos-1;i--)
S.ch[i+T.length]=S.ch[i]; //后移为插入字符串让出位置
for(i=0;i<T.length;i++)
S.ch[pos+i-1]=T.ch[pos]; //插入串T
S.length+=T.length;
return OK;
}//HString_Insert
4.27
int Index_New(Stringtype s,Stringtype t)//改进的定位算法
{
i=1;j=1;
while(i<=s[ 0 ]&&j<=t[ 0 ])
{
if((j!=1&&s[i]==t[j])||(j==1&&s[i]==t[j]&&s[i+t[ 0 ]-1]==t[t[ 0 ]]))
{ //当j==1即匹配模式串的第一个字符时,需同时匹配其最后一个
i=i+j-2;
j=1;
}
else
{
i++;j++;
}
}//while
if(j>t[ 0 ]) return i-t[ 0 ];
}//Index_New
4.28
void Lget_next(LString &T)//链串上的get_next算法
{
p=T->succ;p->next=T;q=T;
while(p->succ)
{
if(q==T||p->data==q->data)
{
p=p->succ;q=q->succ;
p->next=q;
}
else q=q->next;
}//while
}//Lget_next
4.29
LStrNode * LIndex_KMP(LString S,LString T,LStrNode *pos)//链串上的KMP匹配算法,返回值为指针
{
p=pos;q=T->succ;
while(p&&q)
{
if(q==T||p->chdata==q->chdata)
{
p=p->succ;
q=q->succ;
}
else q=q->next;
}//while
if(!q)
{
for(i=1;i<=Strlen(T);i++)
p=p->next;
return p;
} //发现匹配后,要往回找子串的头
return NULL;
}//LIndex_KMP
4.30
void Get_LRepSub(Stringtype S)//求S的最长重复子串的位置和长度
{
for(maxlen=0,i=1;i<S[ 0 ];i++)//串S2向右移i格
{
for(k=0,j=1;j<=S[ 0 ]-i;j++)//j为串S2的当前指针,此时串S1的当前指针为i+j,两指针同步移动
{
if(S[j]==S[j+i]) k++; //用k记录连续相同的字符数
else k=0; //失配时k归零
if(k>maxlen) //发现了比以前发现的更长的重复子串
{
lrs1=j-k+1;lrs2=mrs1+i;maxlen=k; //作记录
}
}//for
}//for
if(maxlen)
{
printf("Longest Repeating Substring length:%d\n",maxlen);
printf("Position1:%dPosition 2:%d\n",lrs1,lrs2);
}
else printf("No Repeating Substring found!\n");
}//Get_LRepSub
分析:i代表"错位值".本算法的思想是,依次把串S的一个副本S2向右错位平移1格,2格,3格,...与自身S1相匹配,如果存在最长重复子串,则必然能在此过程中被发现.用变量lrs1,lrs2,maxlen来记录已发现的最长重复子串第一次出现位置,第二次出现位置和长度.题目中未说明"重复子串"是否允许有重叠部分,本算法假定允许.如不允许,只需在第二个for语句的循环条件中加上k<=i即可.本算法时间复杂度为O(Strlen(S)^2),
4.31
void Get_LPubSub(Stringtype S,Stringtype T)//求串S和串T的最长公共子串位置和长度
{
if(S[ 0 ]>=T[ 0 ])
{
StrAssign(A,S);StrAssign(B,T);
}
else
{
StrAssign(A,T);StrAssign(B,S);
} //为简化设计,令S和T中较长的那个为A,较短的那个为B
for(maxlen=0,i=1-B[ 0 ];i<A[ 0 ];i++)
{
if(i<0) //i为B相对于A的错位值,向左为负,左端对齐为0,向右为正
{
jmin=1;jmax=i+B[ 0 ];
}//B有一部分在A左端的左边
else if(i>A[ 0 ]-B[ 0 ])
{
jmin=i;jmax=A[ 0 ];
}//B有一部分在A右端的右边
else
{
jmin=i;jmax=i+B[ 0 ];
}//B在A左右两端之间.
//以上是根据A和B不同的相对位置确定A上需要匹配的区间(与B重合的区间)的端点:jmin,jmax.
for(k=0,j=jmin;j<=jmax;j++)
{
if(A[j]==B[j-i]) k++;
else k=0;
if(k>maxlen)
{
lps1=j-k+1;lps2=j-i-k+1;maxlen=k;
}
}//for
}//for
if(maxlen)
{
if(S[ 0 ]>=T[ 0 ])
{
lpsS=lps1;lpsT=lps2;
}
else
{
lpsS=lps2;lpsT=lps1;
} //将A,B上的位置映射回S,T上的位置
printf("Longest Public Substring length:%d\n",maxlen);
printf("Position in S:%dPosition in T:%d\n",lpsS,lpsT);
}//if
else printf("No Repeating Substring found!\n");
}//Get_LPubSub
分析:本题基本思路与上题同.唯一的区别是,由于A,B互不相同,因此B不仅要向右错位,而且还要向左错位,以保证不漏掉一些情况.当B相对于A的位置不同时,需要匹配的区间的计算公式也各不相同,请读者自己画图以帮助理解.本算法的时间复杂度是o(strlrn(s)*strlen(t))。
 

《数据结构题集》P.190
4.25
typedef struct {
char? *ch;
int length;
}HString;
 
Status Replace(HString &s,HString t,HString v){
//用串v替换串s中所有的子串t
if (t.length==0) return ERROR;
i=0; j=0;
while (i<=s.length-1) {
while (i<=s.length-1 && j<=t.length-1) {
if (s.ch[i]==t.ch[j]){++i; ++j;}
else {i=i-j+2; j=0;}
}//while
if (j<=t.length-1) return OK;
pos=i-t.length;
if (!(s.ch=(char *)realloc(s.ch,(s.length-t.length+v.length)*sizeof(char))))
exit(OVERFLOW);
for (k=s.length-1; k>=i; --k)
s.ch[k-t.length+v.length]=s.ch[k];
s.ch[pos..pos+v.length-1]=v[0..v.length-1];
s.length=s.length-t.length+v.length;
i=pos+v.length; j=0;
}//while
return OK;
}//Replace
注意:在这里不能用串的其它基本操作来实现Replace
 
 
 
(以下内容来自http://bbs.kaoyan.com)
第四章 串
4.10
void String_Reverse(Stringtype s,Stringtype &r)//求s的逆串r
{
StrAssign(r,''); //初始化r为空串
for(i=Strlen(s);i;i--)
{
StrAssign(c,SubString(s,i,1));
StrAssign(r,Concat(r,c)); //把s的字符从后往前添加到r中
}
}//String_Reverse
4.11
void String_Subtract(Stringtype s,Stringtype t,Stringtype &r)//求所有包含在串s中而t中没有的字符构成的新串r
{
StrAssign(r,'');
for(i=1;i<=Strlen(s);i++)
{
StrAssign(c,SubString(s,i,1));
for(j=1;j<i&&StrCompare(c,SubString(s,j,1));j++); //判断s的当前字符c是否第一次出现
if(i==j)
{
for(k=1;k<=Strlen(t)&&StrCompare(c,SubString(t,k,1));k++); //判断当前字符是否包含在t中
if(k>Strlen(t)) StrAssign(r,Concat(r,c));
}
}//for
}//String_Subtract
4.12
int Replace(Stringtype &S,Stringtype T,Stringtype V);//将串S中所有子串T替换为V,并返回置换次数
{
for(n=0,i=1;i<=Strlen(S)-Strlen(T)+1;i++) //注意i的取值范围
if(!StrCompare(SubString(S,i,Strlen(T)),T)) //找到了与T匹配的子串
{ //分别把T的前面和后面部分保存为head和tail
StrAssign(head,SubString(S,1,i-1));
StrAssign(tail,SubString(S,i+Strlen(T),Strlen(S)-i-Strlen(T)+1));
StrAssign(S,Concat(head,V));
StrAssign(S,Concat(S,tail)); //把head,V,tail连接为新串
i+=Strlen(V); //当前指针跳到插入串以后
n++;
}//if
return n;
}//Replace
分析:i+=Strlen(V);这一句是必需的,也是容易忽略的.如省掉这一句,则在某些情况下,会引起不希望的后果,虽然在大多数情况下没有影响.请思考:设S='place',T='ace',V='face',则省掉i+=Strlen(V);运行时会出现什么结果?
4.13
int Delete_SubString(Stringtype &s,Stringtype t)//从串s中删除所有与t相同的子串,并返回删除次数
{
for(n=0,i=1;i<=Strlen(s)-Strlen(t)+1;i++)
if(!StrCompare(SubString(s,i,Strlen(t)),t))
{
StrAssign(head,SubString(S,1,i-1));
StrAssign(tail,SubString(S,i+Strlen(t),Strlen(s)-i-Strlen(t)+1));
StrAssign(S,Concat(head,tail)); //把head,tail连接为新串
n++;
}//if
return n,
}//Delete_SubString
4.14
Status NiBoLan_to_BoLan(Stringtype str,Stringtype &new)//把前缀表达式str转换为后缀式new
{
Initstack(s); //s的元素为Stringtype类型
for(i=1;i<=Strlen(str);i++)
{
r=SubString(str,i,1);
if(r为字母) push(s,r);
else
{
if(StackEmpty(s)) return ERROR;
pop(s,a);
if(StackEmpty(s)) return ERROR;
pop(s,b);
StrAssign(t,Concat(r,b));
StrAssign(c,Concat(t,a)); //把算符r,子前缀表达式a,b连接为新子前缀表达式c
push(s,c);
}
}//for
pop(s,new);
if(!StackEmpty(s)) return ERROR;
return OK;
}//NiBoLan_to_BoLan
分析:基本思想见书后注释3.23.请读者用此程序取代作者早些时候对3.23题给出的程序,
4.15
void StrAssign(Stringtype &T,char chars&#;)//用字符数组chars给串T赋值,Stringtype的定义见课本
{
for(i=0,T[ 0 ]=0;chars[i];T[ 0 ]++,i++) T[i+1]=chars[i];
}//StrAssign
4.16
char StrCompare(Stringtype s,Stringtype t)//串的比较,s>t时返回正数,s=t时返回0,s<t时返回负数
{
for(i=1;i<=s[ 0 ]&&i<=t[ 0 ]&&s[i]==t[i];i++);
if(i>s[ 0 ]&&i>t[ 0 ]) return 0;
else if(i>s[ 0 ]) return -t[i];
else if(i>t[ 0 ]) return s[i];
else return s[i]-t[i];
}//StrCompare
4.17
int String_Replace(Stringtype &S,Stringtype T,Stringtype V);//将串S中所有子串T替换为V,并返回置换次数
{
for(n=0,i=1;i<=S[ 0 ]-T[ 0 ]+1;i++)
{
for(j=i,k=1;T[k]&&S[j]==T[k];j++,k++);
if(k>T[ 0 ]) //找到了与T匹配的子串:分三种情况处理
{
if(T[ 0 ]==V[ 0 ])
for(l=1;l<=T[ 0 ];l++) //新子串长度与原子串相同时:直接替换
S[i+l-1]=V[l];
else if(T[ 0 ]<V[ 0 ]) //新子串长度大于原子串时:先将后部右移
{
for(l=S[ 0 ];l>=i+T[ 0 ];l--)
S[l+V[ 0 ]-T[ 0 ]]=S[l];
for(l=1;l<=V[ 0 ];l++)
S[i+l-1]=V[l];
}
else //新子串长度小于原子串时:先将后部左移
{
for(l=i+V[ 0 ];l<=S[ 0 ]+V[ 0 ]-T[ 0 ];l++)
S[l]=S[l-V[ 0 ]+T[ 0 ]];
for(l=1;l<=V[ 0 ];l++)
S[i+l-1]=V[l];
}
S[ 0 ]=S[ 0 ]-T[ 0 ]+V[ 0 ];
i+=V[ 0 ];n++;
}//if
}//for
return n;
}//String_Replace
4.18
typedef struct {
char ch;
int num;
} mytype;
void StrAnalyze(Stringtype S)//统计串S中字符的种类和个数
{
mytype T[MAXSIZE]; //用结构数组T存储统计结果
for(i=1;i<=S[ 0 ];i++)
{
c=S[i];j=0;
while(T[j].ch&&T[j].ch!=c) j++; //查找当前字符c是否已记录过
if(T[j].ch) T[j].num++;
else T[j]={c,1};
}//for
for(j=0;T[j].ch;j++)
printf("%c:%d\n",T[j].ch,T[j].num);
}//StrAnalyze
4.19
void Subtract_String(Stringtype s,Stringtype t,Stringtype &r)//求所有包含在串s中而t中没有的字符构成的新串r
{
r[ 0 ]=0;
for(i=1;i<=s[ 0 ];i++)
{
c=s[i];
for(j=1;j<i&&s[j]!=c;j++); //判断s的当前字符c是否第一次出现
if(i==j)
{
for(k=1;k<=t[ 0 ]&&t[k]!=c;k++); //判断当前字符是否包含在t中
if(k>t[ 0 ]) r[++r[ 0 ]]=c;
}
}//for
}//Subtract_String
4.20
int SubString_Delete(Stringtype &s,Stringtype t)//从串s中删除所有与t相同的子串,并返回删除次数
{
for(n=0,i=1;i<=s[ 0 ]-t[ 0 ]+1;i++)
{
for(j=1;j<=t[ 0 ]&&s[i+j-1]==t[i];j++);
if(j>m) //找到了与t匹配的子串
{
for(k=i;k<=s[ 0 ]-t[ 0 ];k++) s[k]=s[k+t[ 0 ]]; //左移删除
s[ 0 ]-=t[ 0 ];n++;
}
}//for
return n;
}//Delete_SubString
4.21
typedef struct{
char ch;
LStrNode *next;
} LStrNode,*LString; //链串结构
void StringAssign(LString &s,LString t)//把串t赋值给串s
{
s=malloc(sizeof(LStrNode));
for(q=s,p=t->next;p;p=p->next)
{
r=(LStrNode*)malloc(sizeof(LStrNode));
r->ch=p->ch;
q->next=r;q=r;
}
q->next=NULL;
}//StringAssign
void StringCopy(LString &s,LString t)//把串t复制为串s.与前一个程序的区别在于,串s业已存在.
{
for(p=s->next,q=t->next;p&&q;p=p->next,q=q->next)
{
p->ch=q->ch;pre=p;
}
while(q)
{
p=(LStrNode*)malloc(sizeof(LStrNode));
p->ch=q->ch;
pre->next=p;pre=p;
}
p->next=NULL;
}//StringCopy
char StringCompare(LString s,LString t)//串的比较,s>t时返回正数,s=t时返回0,s<t时返回负数
{
for(p=s->next,q=t->next;p&&q&&p->ch==q->ch;p=p->next,q=q->next);
if(!p&&!q) return 0;
else if(!p) return -(q->ch);
else if(!q) return p->ch;
else return p->ch-q->ch;
}//StringCompare
int StringLen(LString s)//求串s的长度(元素个数)
{
for(i=0,p=s->next;p;p=p->next,i++);
return i;
}//StringLen
LString * Concat(LString s,LString t)//连接串s和串t形成新串,并返回指针
{
p=malloc(sizeof(LStrNode));
for(q=p,r=s->next;r;r=r->next)
{
q->next=(LStrNode*)malloc(sizeof(LStrNode));
q=q->next;
q->ch=r->ch;
}//for //复制串s
for(r=t->next;r;r=r->next)
{
q->next=(LStrNode*)malloc(sizeof(LStrNode));
q=q->next;
q->ch=r->ch;
}//for //复制串t
q->next=NULL;
return p;
}//Concat
LString * Sub_String(LString s,int start,int len)//返回一个串,其值等于串s从start位置起长为len的子串
{
p=malloc(sizeof(LStrNode));q=p;
for(r=s;start;start--,r=r->next); //找到start所对应的结点指针r
for(i=1;i<=len;i++,r=r->next)
{
q->next=(LStrNode*)malloc(sizeof(LStrNode));
q=q->next;
q->ch=r->ch;
} //复制串t
q->next=NULL;
return p;
}//Sub_String
4.22
4.23
书后对这两题给出了一些提示.由于作者对这些提示的可行性感到困惑,或者尚没有完全理解其意图,致使无法给出参考算法.请读者认真思考后自行写出,欢迎发表你的解答,
4.24
void HString_Concat(HString s1,HString s2,HString &t)//将堆结构表示的串s1和s2连接为新串t
{
if(t.ch) free(t.ch);
t.ch=malloc((s1.length+s2.length)*sizeof(char));
for(i=1;i<=s1.length;i++) t.ch[i-1]=s1.ch[i-1];
for(j=1;j<=s2.length;j++,i++) t.ch[i-1]=s2.ch[j-1];
t.length=s1.length+s2.length;
}//HString_Concat
4.25
int HString_Replace(HString &S,HString T,HString V)//堆结构串上的置换操作,返回置换次数
{
for(n=0,i=0;i<=S.length-T.length;i++)
{
for(j=i,k=0;k<T.length&&S.ch[j]==T.ch[k];j++,k++);
if(k==T.length) //找到了与T匹配的子串:分三种情况处理
{
if(T.length==V.length)
for(l=1;l<=T.length;l++) //新子串长度与原子串相同时:直接替换
S.ch[i+l-1]=V.ch[l-1];
else if(T.length<V.length) //新子串长度大于原子串时:先将后部右移
{
for(l=S.length-1;l>=i+T.length;l--)
S.ch[l+V.length-T.length]=S.ch[l];
for(l=0;l<V.length;l++)
S[i+l]=V[l];
}
else //新子串长度小于原子串时:先将后部左移
{
for(l=i+V.length;l<S.length+V.length-T.length;l++)
S.ch[l]=S.ch[l-V.length+T.length];
for(l=0;l<V.length;l++)
S[i+l]=V[l];
}
S.length+=V.length-T.length;
i+=V.length;n++;
}//if
}//for
return n;
}//HString_Replace
4.26
Status HString_Insert(HString &S,int pos,HString T)//把T插入堆结构表示的串S的第pos个字符之前
{
if(pos<1) return ERROR;
if(pos>S.length) pos=S.length+1;//当插入位置大于串长时,看作添加在串尾
S.ch=realloc(S.ch,(S.length+T.length)*sizeof(char));
for(i=S.length-1;i>=pos-1;i--)
S.ch[i+T.length]=S.ch[i]; //后移为插入字符串让出位置
for(i=0;i<T.length;i++)
S.ch[pos+i-1]=T.ch[pos]; //插入串T
S.length+=T.length;
return OK;
}//HString_Insert
4.27
int Index_New(Stringtype s,Stringtype t)//改进的定位算法
{
i=1;j=1;
while(i<=s[ 0 ]&&j<=t[ 0 ])
{
if((j!=1&&s[i]==t[j])||(j==1&&s[i]==t[j]&&s[i+t[ 0 ]-1]==t[t[ 0 ]]))
{ //当j==1即匹配模式串的第一个字符时,需同时匹配其最后一个
i=i+j-2;
j=1;
}
else
{
i++;j++;
}
}//while
if(j>t[ 0 ]) return i-t[ 0 ];
}//Index_New
4.28
void Lget_next(LString &T)//链串上的get_next算法
{
p=T->succ;p->next=T;q=T;
while(p->succ)
{
if(q==T||p->data==q->data)
{
p=p->succ;q=q->succ;
p->next=q;
}
else q=q->next;
}//while
}//Lget_next
4.29
LStrNode * LIndex_KMP(LString S,LString T,LStrNode *pos)//链串上的KMP匹配算法,返回值为指针
{
p=pos;q=T->succ;
while(p&&q)
{
if(q==T||p->chdata==q->chdata)
{
p=p->succ;
q=q->succ;
}
else q=q->next;
}//while
if(!q)
{
for(i=1;i<=Strlen(T);i++)
p=p->next;
return p;
} //发现匹配后,要往回找子串的头
return NULL;
}//LIndex_KMP
4.30
void Get_LRepSub(Stringtype S)//求S的最长重复子串的位置和长度
{
for(maxlen=0,i=1;i<S[ 0 ];i++)//串S2向右移i格
{
for(k=0,j=1;j<=S[ 0 ]-i;j++)//j为串S2的当前指针,此时串S1的当前指针为i+j,两指针同步移动
{
if(S[j]==S[j+i]) k++; //用k记录连续相同的字符数
else k=0; //失配时k归零
if(k>maxlen) //发现了比以前发现的更长的重复子串
{
lrs1=j-k+1;lrs2=mrs1+i;maxlen=k; //作记录
}
}//for
}//for
if(maxlen)
{
printf("Longest Repeating Substring length:%d\n",maxlen);
printf("Position1:%dPosition 2:%d\n",lrs1,lrs2);
}
else printf("No Repeating Substring found!\n");
}//Get_LRepSub
分析:i代表"错位值".本算法的思想是,依次把串S的一个副本S2向右错位平移1格,2格,3格,...与自身S1相匹配,如果存在最长重复子串,则必然能在此过程中被发现.用变量lrs1,lrs2,maxlen来记录已发现的最长重复子串第一次出现位置,第二次出现位置和长度.题目中未说明"重复子串"是否允许有重叠部分,本算法假定允许.如不允许,只需在第二个for语句的循环条件中加上k<=i即可.本算法时间复杂度为O(Strlen(S)^2),
4.31
void Get_LPubSub(Stringtype S,Stringtype T)//求串S和串T的最长公共子串位置和长度
{
if(S[ 0 ]>=T[ 0 ])
{
StrAssign(A,S);StrAssign(B,T);
}
else
{
StrAssign(A,T);StrAssign(B,S);
} //为简化设计,令S和T中较长的那个为A,较短的那个为B
for(maxlen=0,i=1-B[ 0 ];i<A[ 0 ];i++)
{
if(i<0) //i为B相对于A的错位值,向左为负,左端对齐为0,向右为正
{
jmin=1;jmax=i+B[ 0 ];
}//B有一部分在A左端的左边
else if(i>A[ 0 ]-B[ 0 ])
{
jmin=i;jmax=A[ 0 ];
}//B有一部分在A右端的右边
else
{
jmin=i;jmax=i+B[ 0 ];
}//B在A左右两端之间.
//以上是根据A和B不同的相对位置确定A上需要匹配的区间(与B重合的区间)的端点:jmin,jmax.
for(k=0,j=jmin;j<=jmax;j++)
{
if(A[j]==B[j-i]) k++;
else k=0;
if(k>maxlen)
{
lps1=j-k+1;lps2=j-i-k+1;maxlen=k;
}
}//for
}//for
if(maxlen)
{
if(S[ 0 ]>=T[ 0 ])
{
lpsS=lps1;lpsT=lps2;
}
else
{
lpsS=lps2;lpsT=lps1;
} //将A,B上的位置映射回S,T上的位置
printf("Longest Public Substring length:%d\n",maxlen);
printf("Position in S:%dPosition in T:%d\n",lpsS,lpsT);
}//if
else printf("No Repeating Substring found!\n");
}//Get_LPubSub
分析:本题基本思路与上题同.唯一的区别是,由于A,B互不相同,因此B不仅要向右错位,而且还要向左错位,以保证不漏掉一些情况.当B相对于A的位置不同时,需要匹配的区间的计算公式也各不相同,请读者自己画图以帮助理解.本算法的时间复杂度是o(strlrn(s)*strlen(t))。