个人学习数据结构记录,如有错误或者可以优化的地方,烦请评论指出,谢谢。😃
目录
- 【线性表】
- 【栈与队列】
- 【串】
- (定长顺序存储)-实现字符串比较StrCompare(S,T)
- (定长顺序存储)-实现字符串替换Replace(&S,T,V)
- (定长顺序存储)-实现串匹配算法和KMP算法
- *(定长顺序存储)-求取next数组及nextval
- *(堆分配存储)-字符串分配StrAssign(&T,chars)
- *(堆分配存储)-字符串求子串SubString(&Sub,S,pos,len)
- *(堆分配存储)-字符串插入StrInsert(&S,pos,T)
- *(堆分配存储)-字符串联接Concat(&T,S1,S2)
- (堆分配存储)-字符串替换Replace(&S,T,V)
- (块链存储)-判断串是否具有对称性,时间复杂度要求O(StrLength(S))
- 【数组和广义表】
- 【树与二叉树】
- (顺序存储)-完全二叉树先序遍历
- (二叉链表)-递归计算叶子结点数量
- (二叉链表)-递归计算所有结点数量
- (二叉链表)-递归计算单分支结点数量
- (二叉链表)-递归交换所有结点左右子树
- (二叉链表)-递归计算以结点值为x的结点为根的子树深度
- *(二叉链表)-递归计算二叉树中结点值为x的结点数量
- *(二叉链表)-递归删除元素值为x的结点及其子树,并释放内存;
- *(二叉链表)-非递归计算二叉树高度
- *(二叉链表)-判断给定二叉树是否为完全二叉树
- *(二叉链表)-根据顺序存储结构建立二叉树
- *(二叉链表)-递归复制二叉树
- *(二叉链表)-根据先序序列建立二叉树
- (二叉链表)-输入二叉树的广义表形式,建立二叉树并进行层次遍历
- (二叉链表)-建立二叉树,其先序与中序序列存放于一维数组
- (二叉链表)-先序/中序非递归遍历二叉树
- *(二叉链表)-后序非递归遍历二叉树
- *(二叉链表)-层次遍历二叉树
- (二叉链表)-输出二叉树表示的算术表达式,包括括号
- (孩子兄弟)-计算树的叶子结点数量
- 【图】
- 【查找】
- 【排序】
【线性表】
*(线性表)-求并集A=A∪B
//A=A∪B
void Union(List &La,List Lb){
LaLen=ListLength(La);
LbLen=ListLength(Lb);
for(i=1;i<=LbLen;i++){
GetElem(Lb,i,e);
if(!LocateElem(La,e,equal))//若A中不存在e则插入到A中末尾位置;
ListInsert(La,++LaLen,e);
}
}
*(顺序表)-求交集C=A∩B
假设以两个元素依值递增有序排列的线性表A和B分别表示两个集合(即同一表中的元素值各不相同),现要求另辟空间构成一个线性表C,其元素为A和B中元素的交集,且表C中的元素也依值递增有序排列。试对顺序表编写求C的算法。
//C=A∩B
void Intersection(SqList A,SqList B,SqList &C){
C.elem=(Elemtype*)malloc((A.length+B.length)*sizeof(Elemtype));
if(!C.elem) //初始化线性表C
exit(OVERFLOW);
C.length=0;
C.listsize=A.length+B.length;
k=i=j=0;
while(i<A.length&&j<B.length){
if(A.elem[i]<B.elem[j])
i++;
else if(A.elem[i]>B.elem[j])
j++;
else{
C.elem[k++]=A.elem[i];
i++;j++;
}
}//while
C.length=k;
}//Intersection
(顺序表)-合并两个非递减有序顺序表
void Merge(SqList A,SqList B,SqList &C){
i=j=k=0;
while(i<A.length&&j<B.length){
if(A.elem[i]<=B.elem[j])
C.elem[k++]=A.elem[i++];
else
C.elem[k++]=B.elem[j++];
}
while(i<A.length)
C.elem[k++]=A.elem[i++];
while(j<B.length)
C.elem[k++]=B.elem[j++];
C.length=k;
}
(单链表)-合并两个递增有序链表为递减有序
Status ListMerge(LinkList &La,LinkList &Lb,LinkList &Lc){
pa=La->next;
pb=Lb->next;
Lc=La;
Lc->next=NULL;
free(Lb);
while(pa&&pb){
if(pa->data<=pb->data){
r=pa->next;
pa->next=Lc->next;
Lc->next=pa;
pa=r;
}
else{
r=pb->next;
pb->next=Lc->next;
Lc->next=pb;
pb=r;
}
}//while
if(pa) pb=pa;
while(pb){
r=pb->next;
pb->next=Lc->next;
Lc->next=pb;
pb=r;
}
return OK;
}
(顺序表)-删除第i元素起的k个元素
//i为位序,i+k-1为删除的最后一个元素的位序,那总不能大于长度吧;
//i+k-1是删除的最后一个元素的后一位下标;
//从0开始存放数据元素;
Status DeleteK(SqList &L,int i,int k){
if(i<1||k<0||i+k-1>L.length) //i为位序,i+k-1为删除的最后一个元素的位序,那总不能大于长度吧;
return INFEASIBLE;
for(int j=i+k-1;j<=L.length-1;j++)//i+k-1是删除的最后一个元素的后一位下标;
L.elem[j-k]=L.elem[j];
L.length-=k;
return OK;
}
(线性表)-删除非递减有序的线性表中值相同元素
//从0开始存放数据元素,j是位序;
Status DeleteSame(SqList &L){
int i = 1;
while(i<=ListLength(L)-1){//注意i最高为length-1,因为后面GetElem中有i+1;
GetElem(L,i,e1);
GetElem(L,i+1,e2);
if(e1!=e2)
i++;
else
ListDelete(L,i+1,e);
}
return OK;
}
(单链表)-删除关键值在某区间内的结点
//适用于递增有序链表;
Status DeleteCon(LinkList &L,int mink,int maxk){
if(!L||!L->next)
return ERROR;
if(mink>=maxk)
return ERROR;
p=L;
while(p->next&&p->next->data<=mink){//p指向最后一个不大于mink的结点;
p=p->next;
}
if(p->next){
q=p->next;
while(q&&q->data<maxk){
p->next=q->next;
free(q);
q=p->next;
}//while
return OK;
}//if
}//DeleteCon
//有序或无序链表都适用;
Status DeleteCon(LinkList &L,int Min,int Max){
if(Min>=Max||!L||!L->next) //合法性检验
return ERROR;
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{
pre=p;
p=p->next;
}
}//while
}//DeleteCon
(顺序表)-插入元素到递增有序表中并保持有序性
//从0开始存;
Status InsertSort(SqList &L,Elemtype e){
if(L.length>=L.listsize){//空间已满,重新分配增加内存:
newbase = (Elemtype*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(Elemtype));
if(!newbase)
exit(OVERFLOW);
L.elem=newbase;
L.listsize+=LISTINCREMENT;
}
for(j=L.length-1;j>=0&&L.elem[j]>e;--j)
L.elem[j+1]=L.elem[j];
L.elem[j+1]=e;
++L.length;
return OK;
}
//在顺序表第i个位置之前插入新的元素e;
//注意realloc()的返回值需要赋值给线性表的元素指针;
Status InsertSq(SqList &L,int i,ElemType e){
if(i<1||i>L.length+1)
return ERROR;
if(L.length>=L.listsize){//空间已满,重新增加分配内存;
newbase=(ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase)
exit(OVERFLOW);
L.elem=newbase;
L.listsize+=LISTINCREMENT;
}
for(j=L.length-1;j>=i;--j)//元素后移
L.elem[j+1]=L.elem[j];
L.elem[j+1]=e;
++L.length;
return OK;
}
(顺序表)-比较两个顺序表大小
int Compare(SqList A,SqList B){
i=0;
while(i<A.length&&i<B.length){
if(A.elem[i]>B.elem[i])
return 1;
else if(A.elem[i]<B.elem[i])
return -1;
else
i++;
}
if(i<A.length)
return 1;
else if(i<B.length)
return -1;
else return 0;
}
*(顺序表)-就地逆置
逆置顺序表第start到第end个元素
Status Reverse(int A[],int start,int end){
if(start<1||end>A[0]||start>end)
return ERROR;
for(i=0;i<(end-start+1)/2;++i){
temp=A[end-i-1];
A[end-i-1]=A[start+i-1];
A[start+i-1]=temp;
}
}
(单链表)-就地逆置
Status ListInverse(LinkList &L){//头插法
p = L->next;
L->next = NULL;
while(p){ //头插法
r=p->next;
p->next=L->next;
L->next=p;
p=r;
}
return OK;
}
(链式存储)-多项式拆分
算法一:从原表中摘下奇次项组成新表;
void Split(LinkedPoly &La,LinedPoly &Lb){
Lb=(LinkedPoly)malloc(sizeof(PolyNode));
Lb->next=Lb;
r=Lb; //r指向奇次链表Lb尾结点
pre=La;
p=La->next;
while(p!=La){
if(p->data.exp%2==0){ //若为偶次则前进
pre=p;
p=p->next;
}
else{ //若为奇次则摘下放入Lb表中
pre->next=p->next; //从La中删去p
p->next=r->next; //将p插入Lb表尾
r->next=p;
p=pre->next; //更新p指针
r=r->next; //更新Lb链表尾指针
}//else
}//while
}//Split
算法二:将原表拆为两个新表,分别含奇次项和偶次项;
Status DividePoly(LinkedPoly &L,LinkedPoly &La,LinkedPoly &Lb){
La=L;
Lb=(LinkedPoly)malloc(sizeof(PolyNode));
p=L->next;
pa=La;
pb=Lb;
while(p!=L){
if(p->data.exp%2){ //奇次项
pa->next=p;
pa=p;
}
else{
pb->next=p;
pb=p;
}
p=p->next;
}//while
pa->next=La; //恢复成循环链表
pb->next=Lb;
return OK;
}
*(顺序存储)-多项式求值
注意:pow函数的返回值要强转为(int)
//多项式的顺序存储结构
typedef struct{
float coef;//系数
int exp;//指数
}PolyTerm;
typedef struct{
PolyTerm *data;
int last; //多项式项数
}SqPoly;
int CalcPoly(SqPoly P,int x){
for(i=0,sum=0;i<P.last;++i)
sum+=P.data[i].coef*(int)((pow(x,P.data[i].exp)));
return sum;
}
*(顺序存储)-多项式减法
思路:初始化多项式C,遍历AB,比较指数,若指数不同,将较小指数者先插入C中,
若指数相同,则求系数和,若系数和非零则插入C中,否则只改变AB指针,C指针不变;
最后判断是否有剩余,插入C中;
注意:
#若A指数>B指数,则B的系数要变号,因为是减法;
#C的多项式参数C.last最后记得更新;
Status Sub(SqPoly Pa,SqPoly Pb,SqPoly &Pc){
Pc->data=(PolyTerm*)malloc((Pa.last+Pb.last)*sizeof(PolyTerm));
if(!Pc->data) exit(OVERFLOW);
i=j=k=0;
while(i<Pa.last&&j<Pb.last){
if(Pa.data[i].exp<Pb.data[j].exp){//Pa指数<Pb指数
Pc.data[k]=Pa.data[i];
k++;i++;
}
else if(Pa.data[i].exp>Pb.data[j].exp){
Pc.data[k]=Pb.data[j];
k++;j++;
}
else{//Pa,Pb指数相同
sum=Pa.data[i].coef+Pb.data[j].coef;
if(sum){
Pc.data[k].coef=sum;
Pc.data[k].exp=Pa.data[i].exp;
k++;
}
i++;
j++;
}
}//while
while(i<Pa.last){
Pc.data[k]=Pa.data[i];
k++;i++;
}
while(j<Pb.last){//注意系数变号
Pc.data[k].coef=-Pb.data[j].coef;
Pc.data[k].exp=Pb.data[j].exp;
k++;
j++;
}
Pc->last=k;
return OK;
}//Sub
*(链式存储)-多项式求导
//多项式的链式存储
typedef struct{
float coef;//系数
int exp;//指数
}PolyTerm;
typedef struct PolyNode{
PolyTerm data;
struct PolyNode *next;
}PolyNode, *PolyLink;
typedef PolyLink LinkedPoly;
试以循环链表作稀疏多项式的存储结构,编写求其导函数的算法,要求利用原多项式中的结点空间存放其导函数(多项式),同时释放所有无用(被删)结点。
思路:设立pre,p分别指向头结点,表头结点;当p!=L时循环遍历链表,若p的指数不为0,则对p求导,并将pre否则删除指数为0的常数项p;
Status DerivedFun(LinkedPoly &L){
if(!L) return ERROR;
pre=L;
p=L->next;
while(p!=L){
if(p->data.exp){//指数不为0,则求导
p->data.coef*=p->data.exp;
p->data.exp--;
pre=p; //pre指向已导完的尾结点;
}
else{//指数为0,删除p
pre->next=p->next;
free(p);
}
p=pre->next;//更新下个求导结点;
}
}
*(链式存储)-多项式加法
保留Pa,销毁Pb
//此算法中抽象函数都自带相关处理,如DelFirst(hb,qa),删除hb链表中的qa,自动会链接到正确位置,保证不断链;
typedef LinkedPoly Polynomial
void AddPoly(Polynomial &Pa,Polynomial &Pb){
ha=GetHead(Pa); //ha,hb分别指向当前结点前驱
hb=GetHead(Pb);
qa=NextPos(ha);//qa,qb分别指向Pa,Pb当前结点
qb=NextPos(hb);
while(qa&&qb){
a=GetCurElem(qa); //ab为当前比较项
b=GetCurElem(qb);
switch(CmpExp(a,b)){ //cmp(a,b)比较ab指数大小
case -1: //qa结点指数更低,ha,qa后移
ha=qa;
qa=NextPos(ha);
break;
case 0: //qaqb结点指数相同
sum=a.coef+b.coef;
if(sum!=0.0){//qaqb系数和非0
qa->data.coef=sum;
ha=qa; //更新Pa的头结点ha,因为当前结点已处理
}
else{//系数和为0则删除qa
DelFirst(ha,qa);
FreeNode(qa);
}
DelFirst(hb,qb);//删除Pb中走过结点
FreeNode(qb);
qa=NextPos(ha);//qa,qb后移
qb=NextPos(hb);
break;
case 1: //qb结点指数更低,"摘下"qb后插入到ha后
DelFirst(hb,qb);
InsFirst(ha,qb);
qb=NextPos(hb);//qb及ha后移
ha=NextPos(ha);
break;
}//switch
}//while
if(!ListEmpty(Pb)) //若Pb还有剩余,则链接到Pa后
Append(Pa,qb);
FreeNode(hb);
}
*(链式存储)-多项式减法
保留Pa,销毁Pb
减法与加法区别:qa指数更小时系数变号;循环结束Pb非空时所有剩余结点变号后链接到Pa;
void SubPoly(Polynomial &Pa,Polynomial &Pb){
ha=GetHead(Pa); //ha,hb分别指向当前结点前驱
hb=GetHead(Pb);
qa=NextPos(ha);//qa,qb分别指向Pa,Pb当前结点
qb=NextPos(hb);
while(qa&&qb){
a=GetCurElem(qa);
b=GetCurElem(qb);
switch(CmpExp(a,b)){//比较ab指数大小
case -1://qa结点指数较低
ha=qa;
qa=NextPos(ha);
break;
case 0://qa,qb指数相同
sum=a.coef-b.coef;
if(sum!=0.0){
qa->data.coef=sum;
ha=qa;//更新Pa的头结点ha,因为当前结点已处理
}
else{
DelFirst(ha,qa);
FreeNode(qa);
}
DelFirst(hb,qb);
FreeNode(qb);
qb=NextPos(hb);
qb=NextPos(ha);
break;
case 1: //qb结点指数较低
DelFirst(hb,qb);
qb->data.coef*=-1;//变号
InsFirst(ha,qb);
qb=NextPos(hb);
ha=NextPos(ha);
break;
}//switch
}//while
if(!ListEmpty(Pb))
r=qb;
whlie(r){//变号
r->data.coef*=-1;
r=r->next;
}
Append(Pa,qb);//链接到Pa后面,注意是Pa最后面;
FreeNode(hb);
}//SubPoly
*(链式存储)-多项式乘法
保留Pa,销毁Pb
思路:建立ptemp记录每个Pa结点与Pb链表的乘积,再利用Pc结点记录每个Ptemp的和,结束时Pa链表为空,将Pc加到Pa中;
void Multiply(SqPoly &Pa,SqPoly Pb){
InitList(Pc);//Pc记录每个Ptemp之和
la=PolynLength(Pa);
lb=PolynLength(Pb);
for(i=1;i<=la;++i){
InitList(Ptemp);//ptemp记录每个qa与Pb链表的乘积;
DelFirst(Pa,qa);//删除Pa中第一个数据结点用qa返回;
qb=Pb->head->next;//qb指向Pb中第一个数据结点;
for(j=1;j<=lb;++j){
s=(LinkedPoly)malloc(sizeof(PolyNode));
if(!s) exit(OVERFLOW);
s->next=NULL;
s->data.coef=pa->data.coef*qb->data.coef;//系数相乘;
s->data.exp=pa->data.exp+qb->data.exp;//指数相加:
Append(Ptemp,s);//结果链接到Ptemp中
qb=qb->next;//更新qb
}//for
AddPoly(Pc,Ptemp);//结果相加到Pc中;
}//for
AddPoly(Pa,Pc);//结束时,Pa只剩下头结点,将结果加到Pa中;
}
*约瑟夫环问题
设有n个人围坐在圆桌周围,现从某个位置m(1≤m≤n)上的人开始报数,报数到k的
人就站出来。下一个人,即原来的第k+1位置上的人又从1开始报数,再报数到k的人站出来。依此重复下去,直到全部的人都站出来为止。试设计一个程序求出出列序列。
本算法即采用一个不带头结点的循环链表来处理约瑟夫环问题,其中的n个人用n个结点来表示
//循环链表定义
typedef struct CNode{
Elemtype data;
struct CNode *next;
}CNode;
Status Joseph(CNode &C,int m,int n,int k){
if(m>n||!C) return ERROR;
p=C;
for(i=1;i<m;i++)//p指向m位置
p=p->next;
while(p){
for(i=1;i<k-1;i++)//p指向第k-1个结点
p=p->next;
q=p->next;//q指向当前“该死”结点
printf("%d",q->data);
if(p->next=p) //若p是最后一个结点则删除p
p=NULL;
else{ //若p不是最后一个结点则删除q
p->next=q->next;
p=p->next;
free(q);
}
}//while
return OK;
}//Joseph
int f(int n, int k){//从1开始报数,返回最后存活的人的编号;
if(n == 1) return n;
return (f(n - 1, k) + k - 1) % n + 1;
}
【栈与队列】
(顺序表)-表达式括号匹配
利用栈S判定,将左括号类’(‘、’[‘、’{‘入栈,当遇到右括号类’)‘、’]‘、’}'时,检查栈是否空,若栈空则表示不配对>>FALSE,否则,则判断是否为对应的括号,若对应则退栈,否则表示不配对>>FALSE;当整个表达式检查完时,栈空表示括号正确配对,否则不配对;
Status Bracket(SqList L){
i=0;
InitStack(S);
while(i<L.length){
switch(L.elem[i]){
case '(':
case '[':
case '{':
Push(S,L.elem[i]);
break;
case ')':
case ']':
case '}':
if(StackEmpty(S))
return FALSE;
Pop(S,e);
if(!Match(e,L.elem[i]))
return FALSE;
break;
}//switch
i++;
}//while
if(StackEmpty(S)) //判断是否有剩余括号未匹配
return OK;
else
return ERROR;
}//Bracket
Status Match(char a,char b){
switch(a){
case '(':
if(b!=')')
return ERROR;
break;
case '[':
if(b!=']')
return ERROR;
break;
case '{':
if(b!='}')
return ERROR;
break;
default: //传入非左括号字符
return ERROR;
}//switch
return OK;
}//Matching
表达式转换为逆波兰表达式及求值
//OPTR为存放运算符的栈;
//OP为运算符集合;
//In(char,OP)判断字符char是否在运算符集合中;
//Precede(a,b)比较运算符a,b优先级;
Mind:设转换前表达式格式为#xxxxx#存在A中,转换后表达式格式为xxxxx#存入B中;
设立栈OPTR存放暂未确定运算顺序的操作符,栈底为#,如A为#(1+5)-6/2+(8-4)3#,则转换后B为15+62/-84-3+;
【当栈顶与当前元素不同时为#循环】,若非操作符则直接输入到B中,否则与栈顶操作符比较优先级
栈顶<当前操作符,则当前操作符入栈;
栈顶=当前操作符,则栈顶退栈,此时应该是括号;
栈顶>当前操作符,则栈顶退栈后输入到ExpB中;
Status InversePolandExp(SqList A,SqList &B){
ListInit(B);
InitStack(OPTR);
Push(OPTR,A.elem[0]); //'#'入栈
i=1;j=0;
GetTop(OPTR,e);
while(A.elem[i]!='#'||e!='#'){//OPTR栈顶和顺序表A当前元素都为'#'时代表都空了,即转换完毕;
if(!In(A.elem[i],OP)){ //非运算符直接输入到B中;
B.elem[j++]=A.elem[i++];
B.length++;
}
else{
GetTop(OPTR,e);
switch(Precede(e,A.elem[i])){
case'<': Push(OPTR,A.elem[i]); //OPTR栈顶元素优先级低,将运算符入栈OPTR;
i++;
break;
case'=': Pop(OPTR,x); //只有(括号)和表达式转换完毕的#号才出现相等;
i++; //脱括号,即左括号出栈;
break;
case'>': Pop(OPTR,e); //OPTR栈顶元素优先级高,出栈输入到B中;
B.elem[j++]=e;
B.length++;
break;
}//switch
}//else
}//while
DestoryStack(OPTR);
}//InversePolandExp
//若利用转换后的B进行求值,如下:
Mind:设立栈OPND存放暂未确定运算顺序的数
遍历表达式B,若当前元素不是操作符,则入栈OPND;
否则出栈两个数,利用当前操作符进行运算后入栈;
最后栈OPND中剩余最后一个元素即为运算结果;
OperandType CalVal(SqList B){
InitStack(OPND);
j=0;
while(j<B.length){
if(!In(B.elem[j],OP))
Push(OPND,B.elem[j]);
else{
Pop(OPND,b);
Pop(OPND,a);
theta=B.elem[j];
Push(OPND,Operate(a,theta,b));
}
j++;
}//while
Pop(OPND,e);
return e;
}//CalVal
转换与求值同时进行:
OperandType Evaluate(){
InitStack(OPTR);
INitStack(OPND);
Push(OPTR,'#');
c=getchar();
GetTop(OPTR,e);
while(c!='#'||e!='#'){
if(!In(c,OP)){
Push(OPND,c);
c=getchar();
}
else{
GetTop(OPTR,e);
switch(Precede(e,c)){
case '<':
Push(OPTR,c);
c=getchar();
break;
case '=':
Pop(OPTR,x);
c=getchar();
break;
case '>':
Pop(OPND,b);
Pop(OPND,a);
Pop(OPTR,theta);
Push(OPND,Operate(a,theta,b));
break;
}//switch
}//else
}//while
GetTop(OPND,e);
return e;
}//Evaluate
(循环链表)-实现循环队列初始化、入队出队
//链队列结构定义
typedef struct QNode{
ElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
//循环链队列初始化
Status InitCQueue(LinkQueue &Q){
Q.rear=(QueuePtr)malloc(sizeof(QNode));
if(!Q.rear) exit(OVERFLOW);
Q.rear->next=Q.rear; //构造循环队列
return OK;
}
//循环链队列入队
Status EnCQueue(LinkQueue &Q,ElemType e){
p=(QueuePtr)malloc(sizeof(QNode));
if(!p) exit(OVERFLOW);
p->data=e;
p->next=Q.rear->next;//结点插入队尾
Q.rear->next=p;
Q.rear=p; //队尾指针指向新插入结点;
return OK;
}
//循环链队列出队
status DeCQueue(LinkQueue &Q,ElemType &e){
if(Q.rear->next=Q.rear) //判断队列非空
return INFEASIBLE;
p=Q.rear->next->next;
e=p->data;
Q.rear->next->next=p->next;//Q.rear->next为队头结点,所以出队的是Q.rear->next->next这个结点
if(Q.rear==p) //判断p是否为最后一个元素,需要特殊处理;
Q.rear=p->next; //即队列尾指针会丢失,需要将其指向头结点;
free(p);
return OK;
}
*(顺序队列)-实现循环队列初始化、入队、出队
如果希望循环队列中的元素都能得到利用,则需设置一个标志域tag,并以tag的值为0和1来区分,尾指针和头指针值相同时的队列状态是“空”还是“满”。试编写与此结构相应的入队列和出队列的算法,并从时间和空间角度讨论设标志和不设标志这两种方法的使用范围(如当循环队列容量较小而队列中每个元素占的空间较多时,哪一种方法较好)。
由于增设了tag标记区分队空队满,故可不用再牺牲一个单元来区分队空队满,即队头指针可直接指向队头元素位置,队尾指针直接指向队尾元素位置;
若没有tag标记的原始方法,则队头指针->队头元素,队尾指针->队尾元素的后一位置;要注意区分!!!
typedef int QElemType;
typedef struct{
QElemType *base;
int front;
int rear;
int tag;//0表示空、1表示满
}SqQueue;
Status InitQueue(SqQueue &Q){
Q->base=(QElemType*)malloc(MAXSIZE*sizeof(QElemType));
if(!Q->base)
exit(OVERFLOW);
Q->front=Q->rear=0;
Q->tag=0;
}
Status EnQueue(SqQueue &Q,QElemType e){
if(Q->rear==Q->front&&Q->tag==1)//队列满
return ERROR;
Q->base[Q->rear]=e;
Q->rear=(Q->rear+1)%MAXSIZE;
if(Q->rear==Q->front)//入队后队满
Q->tag = 1;
return OK;
}
Status DeQueue(SqQueue &Q,QElemType &e){
if(Q->rear==Q->front&&Q->tag==1)//队列空
return ERROR;
e=Q->base[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
if(Q->rear==Q->front)//出队后队空
Q->tag==0;
return OK;
}
判断回文1.0
算法一:利用一个栈和一个队列
Status IsHW( ){
InitStack(S);
InitQueue(Q);
while(c=getchar()!='@'){
Push(S,c);
EnQueue(Q,c);
}//while
while(!StackEmpty(S)){
Pop(S,a);
DeQueue(Q,b);
if(a!=b)
return FALSE;
}
return OK;
}
算法二:利用一个栈,前半部分进栈,跳过中位元素,后半部分出栈比较
int IsPalindrome(char C[]){
InitStack(S);
len=C[0]; //记录字符串长度
for(i=1;i<=len/2;i++)
Push(S,C[i]);
if(len%2!=0)
i++;
while(!StackEmpty(S)){
Pop(S,e);
if(e!=C[i])
return FALSE;
else
i++;
}
return OK;
}
判断回文2.0
试写一个算法,识别一次读入的一个以@为结束符的字符序列是否为形如‘序列1&序列2’模式的字符序列。其中序列1和序列2中都不含字符‘&’,且序列2是序列1的逆序列。例如,‘a+b&b+a’是属该模式的字符序列,而‘1+3&3-1’则不是。
>由于是依次读入,未知长度,故只能以@判断结束
设立栈S,依次读入字符,在遇到&之前字符入栈S,&之后字符出栈与当前读入元素对应比较,
若不同则返回FALSE,同则继续,在遇到@结束读取;
Status Demo(){
InitStack(S);
flag=0; //标记是否遇到&
while((c=getchar())!='@'){
if(!flag){//前半部分
if(c!='&')
Push(S,c);
else
flag=1;
}
else{//后半部分
if(StackEmpty(S))//栈空说明输入格式有问题&后部更长
return FALSE;
Pop(S,e);
if(e!=c)
return FALSE;
}//else
}//while
if(!StackEmpty(S))//栈非空说明输入格式有问题&前部更长
return FALSE;
else
return OK;
}//###
递归-求杨辉三角某项值
int CalcYH(int row,int col){
if(col==1||row==col)
return 1;
else
return (CalcYH(row-1,col-1)+CalcYH(row-1,col));
}
应用-背包问题
#define x 20//可选的物品数量
int w[x]; //物品重量;
int v[x]; //物品价值;
int q[x];//q[i]为1代表选取了这件物品(第i+1件);
//q[x]初始化全为0;
int bag(int n,int c){
if(n==0||c==0) //当物品数量为0或背包容量为0时
return 0;
else{ //第n件重量小于背包容量&&选了第n件物品后的总价值大于没选时
if((w[n]<=c)&&((bag(n-1,c-w[n])+v[n])>bag(n-1,c))){
q[n]=1;
return v[n]+bag(n-1,c-w[n]);
}else{
q[n]=0;
return bag(n-1,c);
}
}//else
}//bag
*应用-Hanoi塔问题
//move(A,n,C)为把n从塔A移动到塔C(一个);
//hanoi(n,A,B,C)为把n个数从塔A移动到塔C(多个);
void hanoi(int n,char A,char B,char C){
if(n==1)//将1从A移动到C;
move(A,1,C);
else{
hanoi(n-1,A,C,B);//将A塔上n-1个数移到B;
move(A,n,C);//将n从A移到C;
hanoi(n-1,B,A,C);//将B塔上n-1个数移动C;
}
}
*应用-进制转换
将十进制数i转换为八进制;
void conversion(int i){
InitStack(S);
whlie(i){ //由低位到高位进栈
Push(S,i%8);
i=i/8;
}
wlhie(!StackEmpty(S)){//由高位到低位出栈
Pop(S,e);
printf("%d",e);
}
}
*应用-迷宫问题
伪代码:
设置当前位置为入口位置;
do{
if(当前位置可通){
当前位置入栈;
if(当前位置是出口位置) 则结束;
else 设置当前位置的东邻块为新的当前位置;
}
else{//当前位置不可通,则找下一个新的当前位置
if(栈非空){
栈顶出栈
while(栈顶块四周均不可通&&栈非空){
删除栈顶块//即标记为不可通
出栈直到寻找一个可通的块
}
if(栈顶块存在未探索方向)
设置栈顶块的顺时针方向的下一块为当前位置
}
}
}while(栈非空)
typedef struct{
int ord; //通道块在路径上的"序号";
PosType seat;//通道块在迷宫中的"坐标"
int di; //走向下一个通道块的方向;1234东南西北
}SElemTYpe; //栈的元素类型
Status MazePath(MazeType maze,PosType start,PosType end){
InitStack(S);
curpos=start;
curstep=1;
do{
if(Pass(curpos)){
FootPrint(curpos);//留下足迹
e=(curstep,curpos,1);
Push(S,e);
if(curpos==end) return OK;
curpos=NextPos(curpos,1);//设置东临块为新的当前位置
curstep++;
}//if
else{
if(!StackEmpty(S)){
Pop(S,e);
wlhie(e.di==4&&!StackEmpty(S)){//e.di==4意味着4个方向都走过了
MarkPrint(e.seat);//留下不能通过的足迹
Pop(S,e);
}
if(e.di<4){
e.di++;
Push(S,e);
curpos=NextPos(e.seat,e.di);//设置栈顶块的顺时针方向的下一块为当前位置;
}
}//if栈非空
}//else
}while(!StackEmpty(S))
}
*应用-斐波那契数列
int Fib(int n){//返回斐波那契数列第n个数,n从0开始;
if(n==0||n==1)
return n;
else
return Fib(n-1) + Fib(n-2);
}
*应用-阶乘
int Factorial(int n){
if (n == 0)
return 1;
else
return n* Factorial(n-1);
}
*应用-函数F(n)的递归算法,并消除递归
//递归:
int Fn(int n ){
if(n<0)
exit(ERROR);
if(n==0)
return n+1;
else
return n*Fn(n/2);
}
//迭代消除递归,即存储了上一代的结果
int Fn(int n ){
if(n<0)
exit(ERROR);
for(i=1,a[0]=1;i<=n;i++)
a[i]=i*a[i/2];
return a[n];
}
*递归-Ackerman函数
递归:
int Akm(int m,int n){
if(m<0||n<0)
exit(ERROR);
if(m==0)
return n+1;
else if(n==0)
return Akm(m-1,1);
else
return Akm(m-1,Akm(m,n-1));
}
【串】
(定长顺序存储)-实现字符串比较StrCompare(S,T)
S>T时,返回正数;S==T时,返回0,S<T时,返回负数;
//串的定长顺序存储结构
#define MAXSIZE 255
typedef unsigned char SString[MAXSIZE+1]; //0存放串长度;
int StrCompare(SString S,SString T){
i=1;
while(i<=S[0]&&i<=T[0]){
if(S[i]==T[i])
i++;
else //检查到不同元素,返回元素差值
return S[i]-T[i];
}//比较直到某个字符串挺不住了,或者两个字符串都到底了
return S[0]-T[0];//则返回长度差;
}
(定长顺序存储)-实现字符串替换Replace(&S,T,V)
将S串中所有子串T替换为V,并返回置换次数;
范围移动问题明确两点即可,1、两个起点;2、一个范围
做这道题的几道坑:
1、明确i是指已经找到了匹配的子串时候,起点在主串中的下标。
2、每轮替换结束后,i+vlen确实是新的匹配起点,但是for循环中会自动i++,所以我们要在末尾设为i=i+vlen-1,抵消影响;
3、腾位置与填空缺时,尽量以端点坐标为条件,而不是以移动数量为条件,这样可以避免数量计算错误;
4、如果想使用变量来标记某个算法参数,如用slen记录S[0]即S串长度,则要注意实现更新问题,即在串长度改变后,slen是否也同步变化了,否则会出现“意想不到”的惊喜,所以推荐尽量还是用S[0]来表示,在堆分配存储的串则用S.length表示,这样在串长发生变化时只要更改一个长度变量即可;
5、注意到一个规律,即不管V比T长还是V比T短,S中元素的变化量(每个元素的移动幅度)都是V[0]-T[0],当其大于0时为往右移动,当其小于0时为往左移动;
此外,不管是腾位置还是填空缺,需要移动的元素端点下标都是i+T[0]与S[0],不过在腾位置时,以S[0]起点,i+T[0]为终点;在填空缺时,则反之;(注意此处说的起点终点不是指某个元素从哪移动到哪,而是指需要移动的元素跨度)
//串的定长顺序存储结构
#define MAXSIZE 255
typedef unsigned char SString[MAXSIZE+1];
//0存放串长度,串从1开始存;
//i为每轮替换的起点在主串中的下标!!!
Status Replace(SString &S,SString T,SString V){
for(count=0,i=1;i<=S[0]-T[0]+1;i++){ //S[0]-T[0]+1是最后一个可能出现子串起点的下标;
for(j=i,k=1;k<=T[0]&&S[j]==T[k];j++,k++) //j指示主串S位置,k指示T串位置
if(k>T[0]){ //当k超出T串长度,说明匹配成功,要分三种情况进行替换;
if(T[0]==V[0]) //1、T串V串长度相同,直接放入,i为起点,后同;
for(m=0;m<V[0];++m)
S[i+m]=V[m+1];
else if(T[0]<V[0]){ //2、新串V比旧串T长,腾位置;
for(m=S[0];m>=i+T[0];--m)
S[m+V[0]-T[0]]=S[m];
for(m=0;m<V[0];m++)
S[i+m]=V[m+1];
}
else{ //3、新串V比旧串T短,填空缺;
for(m=i+T[0];m<=S[0];++m)
S[m-(T[0]-V[0])]=S[m];
for(m=0;m<V[0];m++)
S[i+m]=V[m+1];
}
S[0]+=V[0]-T[0]; //更新主串长度;
i+=V[0]-1; //更新下轮匹配主串起点,-1是为了抵消for循环开始i++的影响;
count++; //匹配成功次数+1;
}//if
}//外层for
return count;
}
(定长顺序存储)-实现串匹配算法和KMP算法
假设模式串T在主串S从第pos位置开始有匹配的子串,返回其起始位置,否则返回0;
int SString_Index(SString S,SStirng T,int pos){
i=pos;
j=1
while(i<=S[0]&&j<=T[0]){
if(S[i]==T[j])
{i++;j++;}
else{
i=i-j+2; //i指示主串位置,回溯到i-j+2;
j=1; //i-j+1为当前检查子串首地址,再+1即为下轮检查子串首地址;
}
}//while
if(j>T[0])
return (i-T[0]);
else
return 0;
}
int SString_KMP_Index(SString S,SString T,int pos){
i=pos;
j=1;
while(i<=S[0]&&j<=T[0]){
if(j==0||S[i]==T[j])
{i++;j++;}
else
j=next[j]; //发生不匹配时,i不回溯,模式串指针j回溯到next[j]位置;
}//while
if(j>T[0])
return (i-T[0]);
else
return 0;
}
*(定长顺序存储)-求取next数组及nextval
void getNext(SString T,int next[]){
int i=1,j=0;
next[1]=0;
whlie(i<T[0]){
if(j==0||T[i]==T[j]){
i++;j++;//j表示了公共前后缀的长度
next[i]=j;
}
else
j=next[j];
}//while
}
void getNextval(SString T,int nextval[]){
int i = 1,j = 0;
nextval[1]=0;
whlie(i<T[0]){
if(j==0||T[i]==T[j]){
i++;j++;
if(T[i]!=T[j]) //different->j;
nextval[i]=j;
else //same->nextval[j];
nextval[i]=nextval[j];
}
else
j=nextval[j];
}//while
}
*(堆分配存储)-字符串分配StrAssign(&T,chars)
typedef struct{
char *ch; //从0开始存
int length;
}HString; //Heap即堆的意思;
Status StrAssign(HString &T,char *chars){
if(T.ch) free(T.ch);//释放原有空间
for(len=0,ch=chars;*ch;++len,++ch);//求chars串长
if(!len){
T.ch=NULL;
T.length=0;
}
else{
if(!(T.ch=(char*)malloc(len*sizeof(char))))
exit(OVERFLOW);
for(i=0;i<len;++i) //分配
T.ch[i]=chars[i];
T.length=len;
}//else
return OK;
}
*(堆分配存储)-字符串求子串SubString(&Sub,S,pos,len)
//用Sub返回串S的第pos个字符起长度为len的子串;
Status SubString(HString &Sub,HString S,int pos,int len){
if(pos<1||pos>S.length)
return ERROR;
if(len<0||len>S.length-pos+1)//pos+len最大为S.length+1
if(Sub.ch) free(Sub.ch);
if(!len){
Sub.ch=NULL;
SUb.length=0;
}
else{
if(!(Sub.ch=(char*)malloc(len*sizeof(char))))
exit(OVERFLOW);
for(i=0;i<=len-1;i++)//分配
Sub.ch[i]=S.ch[pos-1+i];
}//else
return OK;
}
*(堆分配存储)-字符串插入StrInsert(&S,pos,T)
//在串S的第pos个字符之前插入串T
Status StrInsert(HString &S,int pos,HString T){
if(pos<1||pos>S.length+1)
return ERROR;
if(T.length){ //T串非空则S串重新分配空间
if(!(S.ch=(char*)realloc(S.ch,(S.length+T.length)*sizeof(char))))
exit(OVERFLOW);
for(i=S.length-1;i>=pos-1;--i)//腾出空间
S.ch[i+T.length]=S.ch[i]
for(i=0;i<=T.length-1;i++)//pos-1为第一个插入位置下标
S.ch[pos-1+i]=T.ch[i];
S.length+=T.length;
}//if
return OK;
}
*(堆分配存储)-字符串联接Concat(&T,S1,S2)
//用T返回S1与S2联接后的新串;
Status Concat(HString &T,HString S1,HStirng S2){
if(T.ch) free(T.ch); //释放原有空间;
if(!(T.ch=(char*)malloc((S1.length+S2.length)*sizeof(char))));
exit(OVERFLOW);
for(i=0;i<=S1.length-1;++i)
T.ch[i]=S1.ch[i];
for(i=0;i<=S2.length-1;++i)
T.ch[S1.length+i]=S2.ch[i];
T.length+=S1.length+S2.length;
return OK;
}
(堆分配存储)-字符串替换Replace(&S,T,V)
//串从0开始存,i为每轮替换的起点在主串中的下标!!!
//注意对比:定长顺序存储的字符串是从1开始存储的!!!
//而堆分配的串是从0开始存储,所以在函数中有些许参数差异,但整体思路是一致的
int Replace_HS(HString &S,HString T,HString V){
for(count=0,i=0;i<=S.length-T.length;++i){ //S.length-T.length是最后一个可能出现匹配串起点的下标
for(j=i,k=0;k<T.length&&S.ch[j]==T.ch[k];++j,++k)
if(k==T.length){ //当k等于T.length说明找到了与模式串匹配的子串,以i为起点下标;
if(T.length==V.length) //1-新旧串长度相同
for(m=0;m<V.length;++m)
S.ch[i+m]=V.ch[m]; //注意此处V串下标与顺序存储的不同
else if(T.length<V.length){ //2-新串V比旧串T长,腾位置
if(!(S.ch=(char*)realloc(S.ch,(S.length+V.length-T.length)*sizeof(char)))
exit(OVERFLOW);
for(m=S.length-1;m>=i+T.length;--m)
S[m+V.length-T.length]=S[m]; //注意此处S串下标与顺序存储的不同
for(m=0;m<V.length;++m)
S[i+m]=V[m];
}
else{ //3-新串V比旧串T短,填空缺
for(m=i+T.length;m<=S.length-1;++m)
S[m-(T.length-V.length)]=S[m]; //注意此处S串下标与顺序存储相同,想想为什么;
for(m=0;m<V.length;m++) //因为是以i为相对位置,而i在顺序或者堆的算法中都对应了起点下标
S[i+m]=V[m];
}
S.length+=V.length-T.length;
i+=V.length-1; //更新下轮匹配主串起点,-1是为了抵消for循环开始i++的影响;
count++;
}//if
}//for
return count;
}//Replace_HS
(块链存储)-判断串是否具有对称性,时间复杂度要求O(StrLength(S))
//串的块链存储结构定义
#define CHUNKSIZE 80
typedef struct Chunk{ //块结点
char ch[CHUNKSIZE];
struct Chunk *next;
}Chunk;
typedef struct { //块链串
Chunk *head,*tail; //串的头指针和尾指针;
int curlen; //串的当前长度;
}LString;
注意p为指针,访问块内元素应使用p->ch[j];
Status IsSymmetry(LString L){
InitStack(S);
p=L.head;
if(!p||!(L.curlen))
return ERROR;
i=1; //i指示当前元素位序
j=0; //j指示当前块内元素
whlie(i<=L.curlen){
if(i<=(L.curlen/2))//前半部分入栈
Push(S,p->ch[j]);
else{ //后半部分出栈比较
if(!StackEmpty(S))
Pop(S,e);
if(e!==p->ch[j])
return FALSE;
}
++i; //更新位序和块内下标
if((j=(j+1)%MAXCHUNKSIZE)==0)//j走到块内尽头则p指向下一块
p=p->next;
if((L.curlen%2)&&i==(curlen/2)+1){//若字符串长度为奇数,且当前元素为中位数,则"跳过";
++i;
if((j=(j+1)%MAXCHUNKSIZE)==0)
p=p->next;
}//if
}//whlie
if(StackEmpty(S)&&i>S.curlen)
return OK;
else
return FALSE;
}//IsSymmetry
【数组和广义表】
(一维数组)-循环右移数组元素,且移动/交换次数为O(n)
解法一:循环右移
void CMove(int A[n],int k){
for(i=1;i<=k;i++) //求n和k最大公约数
if(n%i==0&&k%i==0)
p=i;
for(i=0;i<p;i++){ //i为每次循环链起点;
j=i; //j为循环点,m为下一次循环点;
m=(j+k)%n;
temp=A[i]; //temp中保存的是上一次循环所换下来的值;
while(m!=i){ //循环右移,注意此处不是swap交换;
A[j]=temp;
temp=A[m];
A[m]=A[j];
j=m;
m=(j+k)%n;
}//while
A[i]=temp;
}//for
}
解法二:利用逆置;注意从0开始存!!!
//逆置函数,逆置数组A第start到end的元素,数组从0开始存;
Status Reverse(int A[],int n,int start,int end){
if(start<1||end>n||start>end)
return FALSE;
for(i=0;i<=(end-start)/2;i++){
swap(A[start-1+i],A[end-1-i]); //start-1与end-1分别是需交换的首元和尾元;
}
}
void CMove(int A[],int n,int k){
p = k%n; //p为实际需要循环右移的步长,因为当p=n时相当于没移;
if(p){ //p为0时无需逆置;
Reverse(A,n,1,n); //全组逆置;
Reverse(A,n,1,p); //前p位逆置为顺序;
Reverse(A,n,p+1,n); //后n-p位逆置为顺序;
}
}
(二维数组)-寻找马鞍点
解法一:寻找每行最小元素所在列minj,再到对应列判断是否为该列最大元素,是则输出;
void FindSaddle1(int A[m][n]){
int minj,flag; //变量声明
for(i=0;i<m;i++){ //寻找每一行的马鞍点
for(minj=0,j=1;j<n;j++){ //寻找i行最小元素所在列minj
if(A[i][j]<A[i][minj])
minj=j;
for(flag=1,k=0;k<m;k++) //判断是否为minj列最大元素;
if(A[k][minj]>A[i][minj])
flag=0; //flag标记A[i][minj]是否为minj列最大元素
if(flag)
printf("%d,",A[i][minj]);
}
}//外层for
}//FindSaddle1
解法二:求出每行最小元素存入min[m]中,求出每列最大元素存入max[n]中,若元素即在min[m],又在max[n]中则是马鞍点;
void FindSaddle2(int A[m][n]){
flag = 0;
for(i=0;i<m;i++)//寻i行最小元放入min[i1]
for(min[i]=A[i][0],j=1;j<n;j++)
if(A[i][j]<min[i])
min[i]=A[i][j]
for(j=0;j<n;j++)//寻j列最大元素放入max[j]
for(max[j]=A[0][j],i=1;i<m;i++)
if(A[i][j]>max[j])
max[j]=A[i][j];
for(i=0;i<m;i++){
for(j=0;j<n;j++)
if(min[i]==max[j]){
print("%d,",A[i][j]);
flag=1;
}
}
if(!flag)
printf("没有马鞍点!");
}//FindSaddle2
(三元组顺序表)-实现两个矩阵相加
//三元组结构定义
#define MAXSIZE 100
typedef struct{
int i,j;
ElemType e;
}Triple;
typedef struct{
Triple data[MAXSIZE+1];//从1开始存;
int mu,nu,tu; //mu为行数,nu为列数,
//tu为非零元素数量;
}TSMatrix;
Status ADDTSMatrix(TSMatrix A,TSMatrix B,TSMatrix &C){
if(A.mu!=B.mu||A.nu!=B.nu)//判断是否同型矩阵;
return ERROR;
C.mu=A.mu;
C.nu=A.nu;
C.tu=0;
pa=pb=pc=1;
while(pa<=A.tu&&pb<=B.tu){ //遍历AB三元组
if(A.data[pa].i<B.data[pb].i){ //若A行<B行
C.data[pc]=A.data[pa];//分别将A.data[pa].i、j、e赋值给C对应元素,下同;
pa++;
}
else if(A.data[pa].i>B.data[pb].i){//若A行>B行
C.data[pc]=B.data[pb];
pb++;
}
else{ //AB行相同,下面比较列
if(A.data[pa].j<B.data[pb].j){ //A列<B列
C.data[pc]=A.data[pa];
pa++;
}
else if(A.data[pa].j>B.data[pb].j){//A列>B列
C.data[pc]=B.data[pb];
pb++;
}
else{ //AB行列都相同,则求和
sum=A.data[pa].e+B.data[pb].e;
if(sum){ //和不为0
C.data[pc].i=A.data[pa].i;
C.data[pc].j=A.data[pa].j;
C.data[pc].e=sum;
pa++;
pb++;
}
else{ //和为0则跳过
pa++;
pb++;
continue;//跳过后续语句,否则C指针pc与元素数量计算会出错;
}
}//else 同行列
}//else 同行
pc++; //更新三元组C的指针和元素数量;
C.tu++;
}//while
while(pa<=A.tu){ //处理A中剩余元素;
C.data[pc]=A.data[pa];
pa++;
pc++;
C.tu++;
}
while(pb<=B.tu){ //处理B中剩余元素;
C.data[pc]=B.data[pb];
pb++;
pc++;
C.tu++;
}
return OK;
}//ADDTSMatrix
*(三元组顺序表)-实现矩阵转置和快速转置
Status Transpose(TSMatrix A,TSMatrix &B){
B.mu=A.mu;
B.nu=A.nu;
B.tu=A.tu;
if(B.tu){ //判断非0矩阵,则进行转置
q=1;
for(col=1;col<=A.nu;++col)//遍历列
for(p=1;p<=A.tu;++p)//遍历A三元组
if(A.data[p].j==col){//找到A中列为col的非零元素
B.data[q].i=A.data[p].j;//以转置方式放入B中;
B.data[q].j=A.data[p].i;
B.data[q].e=A.data[p].e;
++q;
}//if
return OK;
}
}//Transpose
//num记录转置前矩阵A的每列元素数量;
//cpot记录转置前矩阵A的每列第一个非0元素在转置后矩阵B.data中的位置;
//显然cpot[1]=1
Status QuickTrans(TSMatrix A,TSMatrix &B){
B.mu=A.mu;
B.nu=A.nu;
B.tu=A.tu;
if(B.tu){ //判断非0矩阵,则进行转置
for(col=1;col<=A.nu;++col)//num数组初始化
num[i]=0;
for(p=1;p<=A.tu;++p) //计算num数组值
++num[A.data[p].j];
cpot[1]=1; //cpot初始化
for(col=2;col<=A.nu;++col)//计算cpot值
cpot[col]=cpot[col-1]+num[col-1];
for(p=1;p<=A.tu;++p){//遍历三元组A放入B
col=A.data[p].j;
q=cpot[col];
B.data[q].i=A.data[p].j;//以转置方式放入B中;
B.data[q].j=A.data[p].i;
B.data[q].e=A.data[p].e;
++cpot[col];//既然col列的第一个非0元素位置为cpot[col],那后一个不就是cpot[col]+1吗?
}//for
return OK;
}//if
}//QuickTrans
【树与二叉树】
(顺序存储)-完全二叉树先序遍历
void PreOrder_Sq(SqBiTree BT){
InitStack(S);
p=1;
Push(S,p);
while(!IsEmpty(S)){
Pop(S,p);
Visit(BT[p]); //Vist()为访问函数;
if(p*2+1<=n){ //右子树根结点入栈;
Push(S,p*2);
}
if(p*2<=n){ //左子树根结点入栈
Push(S,p*2+1);
}
}//while
}
//也可以写成同二叉链表存储的非递归遍历同样形式:
void PreOrder_Sq(SqBiTree BT){
InitStack(S);
p=1;
while((p<=n)||(!StackEmpty(S))){
while(p<=n){ //循环访问到最左结点;
Visit(BT[p]);
Push(S,p);
p=p*2;
} //最左访问结束
if(!StackEmpty(S)){
Pop(S,p); //出栈一个结点
p=p*2+1; //往右拐个弯,诶,继续!
}
}//while
}
(二叉链表)-递归计算叶子结点数量
int Calc_Leaves(BiTree BT){
if(!BT)
return 0;
else if(BT->lchild==NULL&&BT->rchild==NULL)
return 1;
else
return Calc_Leaves(BT->lchild)+Calc_Leaves(BT->rchild);
}
(二叉链表)-递归计算所有结点数量
int Calc_Nodes(BiTree BT){
if(!BT)
return 0;
else
return 1+Calc_Nodes(BT->lchild)+Calc_Nodes(BT->rchild);
}
(二叉链表)-递归计算单分支结点数量
int Calc_Single(BiTree BT){
if(!BT)
return 0;
else if((!BT->lchild)&&BT->rchild)||(BT->lchlid&&!BT->rchild)) //BT为单分支结点
return 1+Calc_Single(BT->lchild)+Calc_Single(BT->rchild);
else
return Calc_Single(BT->lchild)+Calc_Single(BT->rchild);
}
(二叉链表)-递归交换所有结点左右子树
void Exchange_BiTree(BiTree BT){
if(BT){
Swap(BT->lchild,BT->rchlid);
Exchange_BiTree(BT->lchild);
Exchange_BiTree(BT->rchild);
}
}
(二叉链表)-递归计算以结点值为x的结点为根的子树深度
利用两个函数,一个求某结点深度,一个寻找值为x的结点;
int GetDepth(BiTree T){ //求取T结点的深度;
if(!T)
return 0;
else{
m=GetDepth(T->lchild);
n=GetDepth(T->rchild);
return (m>n?m:n)+1;
}
}
void GetX(BiTree T,int x){ //求取值为x的结点的深度;
if(T){
if(T->data==x){//若有多个x则都打印
printf("%d,", GetDepth(T));
}
else{
if(T->lchild)
GetX(T->lchild,x);
if(T->rchild)
GetX(T->rchlid,x);
}//else
}//if
}
*(二叉链表)-递归计算二叉树中结点值为x的结点数量
int xNum(BiTree T,int x){
if(!T)
return 0;
else if(T->data==x)
return 1+xNum(T->lchild)+xNum(T->rchild);
else
return xNum(T->lchild)+xNum(T->rchild);
}
*(二叉链表)-递归删除元素值为x的结点及其子树,并释放内存;
//递归寻找结点值为x的二叉树
void FindX(BiTree T,int x){
if(T){
if(T->data==x)
ClearBiTree(T);
else{
FindX(T->lchild);
FindX(T->rchild);
}
}
}
//递归删除二叉树
void ClearBiTree(BiTree &T){
if(T){
if(T->lchild)
ClearBiTree(T->lchild);
if(T->rchild)
ClearBiTree(T->rchild);
free(T);//释放根结点
T=NULL//置空树指针
}
}
*(二叉链表)-非递归计算二叉树高度
//非递归计算二叉树高度
/*思路:层次遍历二叉树,设立二叉树指针数组Q模拟队列,利用last标记每层最右结点序号,
当出队元素为该层最右结点序号时,层数level加1,并更新last
(当每层最右结点出队时,说明上一个入队的rear应该是下层的最右结点序号)
*/
int CalcLevel(BiTree T){
if(!T) return ERROR;
BiTree Q[MaxSize];
int front=-1,rear=-1;
int last=0,level=0;
Q[++rear]=T;
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;
}
*(二叉链表)-判断给定二叉树是否为完全二叉树
//进行层次遍历,将二叉树的顺序存入数组Order中,如根结点为1,则其左子树为2*1,右子树为2*1+1;
//依照完全二叉树定义,最后Order中的次序应该是连续递增的(1,2,3,4,5....),若出现无序,则说明不是完全二叉树;
//p存放结点,order存放它的位序, p和order都是按照层序一个一个结点存入的,若当某个结点的位序大于层次遍历序列中的前驱时,说明不是完全二叉树。
Status IsCBT(BiTree T){
i=j=0;//i指示根结点,j指示当前结点
if(T){
P[j]=T;//根结点入"队列"
Order[j]=1;//Order记录层次访问的顺序
j++;
while(i<j){
if(i>0&&Order[i]>Order[i-1]+1)//若序号不连续
return ERROR;
if(P[i]->lchild){
P[j]=P[i]->lchild;//
Order[j]=2*Order[i];
j++;
}
if(P[i]->rchild){
P[j]=P[i]->rchild;
Order[j]=2*Order[i]+1;
j++;
}
i++;//更新根结点指针
}//while
}//if
}//IsCBT
*(二叉链表)-根据顺序存储结构建立二叉树
假设以顺序表sa表示一棵完全二叉树,sa.elem[1…sa.last]中存放树中各结点的数据元素。试编写算法由此顺序存储结构建立该二叉树的二叉链表。
思路: 设立辅助数组node记录二叉树结点,每次创建结点时从node中寻找父结点指针,将新建结点插入对应父结点的左右孩子中
Status CreateBiTree(BiTree &T,TElemType sa[]){
for(i=1,f=1;i<=sa.last;++i){
if(sa[i]=='#') //#表示空结点值
p=NULL;
else{//建立结点
p=(BiTree)malloc(sizeof(BiTNode));
p->lchild=p->rchild=NULL;
p->data=sa[i];
}
if(i==1)
T=p;
else{
while(f!=i/2)//寻找i的父结点f
f++;
if(i==2*f) //根据i的值将其父结点指向p
node[f]->lchild=p;
if(i==2*f+1)
node[f]->rchild=p;
}
node[i]=p; //node为辅助数组,暂存各结点的指针,为了与sa同步,0单元弃用
}//for
}//CreateBiTree
/*按照完全二叉树定义,层次遍历时,遇到的第一个空结点的该层之后应该都为空,
所以以层次遍历,当遇到空结点时,将剩余结点依次出队,若有非空结点则不是完全二叉树;
*/
Status IsComplete(BiTree T){
if(!T) return ERROR;
InitQueue(Q);
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 ERROR;
}
}
}
*(二叉链表)-递归复制二叉树
void CopyTree(BiTree T,BiTree &S){
if(T==NULL){
S=NULL;
return;
}
else{
S=(BiTree)malloc(sizeof(BiTNode));
S->data=T->data;
CopyTree(T->lchild,S->lchild);
CopyTree(T->rchild,S->rchild);
}
}
*(二叉链表)-根据先序序列建立二叉树
按照先序遍历顺序依次输入二叉树结点值,建立二叉树;
如按AB##C##顺序输入,建立一个根结点为A,左右子树分别为B和C的二叉树;
void CreateBiTree(BiTree &T){
scanf("%c",&ch);
if(ch=='#')
T=NULL;
else{
if(!T=(BiTree)malloc(sizeof(BiTNode)))//生成根结点
exit(OVERFLOW);
T->data=ch;//按照先序建立二叉树
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}//else
}//CreateBiTree
(二叉链表)-输入二叉树的广义表形式,建立二叉树并进行层次遍历
//根据输入的广义表形式建立二叉树;
void Create_BiTree(BiTree &BT){
InitStack(S);
*BT = NULL; //根结点初始化
while(!InputKey(ch)){//InputKey为已定义的输入函数
switch(ch){
case '(': //遇到左括号,说明下个字符为左孩子,将双亲p入栈先
Push(S,p); //栈顶始终保存当前字符的双亲结点;
flag=0; //flag标记下个字符是左孩子0还是右孩子1;
break;
case ',': //遇到逗号,说明下个字符为右孩子;
flag=1;
break;
case ')': //遇到右括号,说明当前层读取完毕,要返回上一层,则出栈一个双亲结点;
Pop(S,e); //上层双亲结点成为新的栈顶元素;
break;
default: //遇到字母,则创建新结点p;
p=(BiTree)malloc(sizeof(BiNode));
p->lchild=p->rchild=NULL;
p->data=ch;
if(!(*BT)) //若根结点BT为空,说明此p要成为根结点;
*BT=p;
else{ //若根结点BT非空;
GetTop(S,r); //则根据flag确定其此p成为其双亲结点的左孩子还是右孩子;
if(flag)
r->rchild=p;
else
r->lchild=p;
}//else
}//switch
}//while
}//Create_BiTree
(二叉链表)-建立二叉树,其先序与中序序列存放于一维数组
一颗子树在先序和中序序列中的所占位置总是连续的,即可用起始下标和终止下标来确定一颗子树
BiTree Build_Sub(int pre_start,int pre_end,int in_start,int in_end){
root = (BTNode*)malloc(sizeof(BTNode));
root->data=A[pre_start];
for(i=in_start;B[i]!=root->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);
root->lchild=lroot;
}
if(rightlen){
rroot=Build_Sub(pre_end-rightlen+1,pre_end,in_end-rightlen+1,in_end);
root->rchild=rroot;
}
return root;
}
(二叉链表)-先序/中序非递归遍历二叉树
void PreBiTree(Bitree BT){
IninStack(S);
if(!BT) return;
p=BT;
while(p||!StackEmpty(S)){
while(p){ //循环访问到最左结点;
Visit(p->data);
Push(S,p);
p=p->lchild;
}
if(!StackEmpty(S)){ //最左访问结束
Pop(S,p); //出栈一个结点
//若将Visit放在Pop后面则变成中序遍历;
p=p->rchild; //往右拐个弯,诶,继续!
}
}
}
*(二叉链表)-后序非递归遍历二叉树
void PostOrder(BiTree T){//左右根
InitStack(S);
p=T;
r=NULL;
while(p||!StackEmpty(S)){
while(p){ //走到最左下
Push(S,p);
p=p->lchild;
}
if(!StackEmpty(S)){
GetTop(S,p);
if(p->rchlid&&p->rchlid!=r)//右子树存在且未被访问
p=p->rchild;
else{
Pop(S,p);
Visit(p);
r=p; //记录上一个访问结点
p=NULL; //重置p指针;
}
}
}//while
}
//双栈写法
void PostOrder(BiTree T){
if(!T) return;
InitStack(S1);
InitStack(S2);
Push(S1,T);
whlie(!StackEmpty(S1)){
Pop(S1,p); //S1一出栈就入栈S2
Push(S2,p);
if(p->lchild)//注意左子树先入栈1;
Push(S1,p);
if(p->rchild)
Push(S1,p);
}//while
while(!StackEmpty(S2)){
Pop(S2,p);
Visit(p);
}
}
*(二叉链表)-层次遍历二叉树
//层次遍历
void LevelTraverse(BiTree T){
if(!T) exit(0);
InitQueue(Q);
EnQueue(Q,T);
while(!QueueEmpty(Q)){
DeQueue(Q,p);
Visit(p->data);
if(p->lchild)
EnQueue(Q,p->lchild);
if(p->rchild)
EnQueue(Q,p->rchild);
}
}//LevelTraverse
(二叉链表)-输出二叉树表示的算术表达式,包括括号
//可用以下伪函数来表示相关操作;
Status IsOperator(char c); //判断字符c是否为操作符;
Status LowPriority(char a,char b); //判断操作符a优先级是否低于b;
```c
Status IsOperator(char c); //判断字符c是否为操作符;
Status LowPriority(char a,char b); //判断操作符a优先级是否低于b;
Status Print_Exp(BiTree T){
if(T){
if(T->lchild){//有左右子树的T一定是操作符
if(T的左孩子是操作符&&它的优先级低于T){
printf("(");
if(!Print_Exp(T->lchild))
return ERROR;
printf(")");
}
else if(!Print_Exp(T->lchild))
return ERROR;
}//if(T->lchild)
printf("%c",T->data);
if(T->rchild){
if(T的右孩子是操作符&&它的优先级低于){
printf("(");
if(!Print_Exp(T->rchild))
return ERROR;
printf(")");
}
else if(!Print_Exp(T->rchlid))
return ERROR;
}//if(T->rchild)
}//if(T)
}
Status IsOperator(char c){
if(c=='+'||c=='-'||c=='*'||c=='/')
return TRUE;
else
return ERROR;
}
Status LowPriority(char a,char b){
if((a=='+'||a=='-')&&(b=='*'||c=='/'))
return TRUE;
else
return ERROR;
}
(孩子兄弟)-计算树的叶子结点数量
//孩子兄弟链表
typedef struct CSNode{
ElemType data;
struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;
//递归算法
int Calc(CSTree T){
if(T){
if(!T->firstchild)
return 1+Calc(T->nextsibling);
else
return Calc(T->firstchild)+Calc(T->nextsibling);
}
else
return 0;
}
【图】
//邻接矩阵结构定义
typedef struct ArcCell{
VRType adj; //顶点关系类型,无权图为0/1,带权图为权值类型;
InfoType *info; //该弧相关信息的指针;
}ArcCell,AdjMatrix[MaxVertexNum][MaxVertexNum];
typedef struct{
VertexType vexs[MaxVertexNum]; //顶点向量;
AdjMatrix arcs; //邻接矩阵;
int vexnum,arcnum; //图的当前顶点数和边数;
GraphKind kind; //图的种类标记
}MGraph;
//邻接表结构定义
typedef struct ArcNode{
int adjvex;
struct ArcNode *nextarc;
}ArcNode;
typedef struct VNode{
VertexType data;
ArcNode *firstarc;
}VNode,AdjList[MaxVertexNum];
typedef struct {
AdjList vertices;
int vexnum,arcnum; //当前顶点数量和边数量;
}ALGraph;
(邻接矩阵)-实现插入边操作Insert_Arc(G,v,w)
注意v和w表示顶点值,如A和B,而不是顶点表下标;
Status Insert_Arc(MGraph &G,char v,char w){
if((i=LocateVex(G,v))<0||(j=LocateVex(G,w)<0) //顶点v或w不存在
return ERROR;
if(i==j) //顶点v和w相同
return ERROR;
if(!G.arcs[i][j].adj){ //判断弧<v,w>是否已存在
G.arcs[i][j].adj=1;
G.arcnum++;
return OK;
}
else //若弧<v,w>已存在;
return ERROR;
}
(邻接表)-实现插入边操作Insert_Arc(G,v,w)
Status Insert_Arc(ALGraph &G,char v,char w){
if((i=LocateVex(G,v)<0)||(j=LocateVex(G,w))<0)
return ERROR;
if(i==j)
return ERROR;
s=(ArcNode*)malloc(sizeof(ArcNode)); //创建新边表结点
s->nextarc==NULL;
s->adjvex=j;
p=G.vertices[i].firstarc;//p指向顶点v的第一条出边
if(!p) //若顶点v没有出边则新边插入作为第一条出边
G.vertices[i].firstarc=s;
else{ //若顶点v有出边则遍历判断是否弧<v,w>已存在;
pre=p;
while(p){//p遍历所有边,pre指向前驱
if(p->adjvex==j)
return ERROR;
pre=p;
p=p->nextarc;
}//结束时pre指向最后一条边
pre->nextarc=s;
}
G.arcnum++;
return OK;
}
*递归广度优先遍历图
因为广度优先遍历需要用到队列,所以不能递归,一般与栈相关算法才递归。
*非递归广度优先遍历图,图为强连通图
注意在非强连通图中实现广度或者深度遍历的非递归算法时,都要把代码(不包括visited和队列或栈初始化部分)放在一个for循环中,即
for(i=0;i<G.vexnum;i++) if(!visited[i]){ //放入DFS或BFS代码;}
才能实现对所有结点的访问
void BFSTraverse(Graph G){
for(i=0;i<G.vexnum;i++)
visited[i]=FALSE;
InitQueue(Q);
for(i=0;i<G.vexnum;i++) //图可能非连通图,要从多个起点出发才能遍历;
if(!visited[i]){
Visit(i);
visited[i]=TRUE:
EnQueue(Q,i); //第一个顶点访问后入队;
while(!QueueEmpty(Q)){
DeQueue(Q,v); //顶点出队,并将其所有未访问的邻接点访问后入队;
for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))
if(!visited[w]){
Visit(w);
visited[w]=TRUE;
EnQueue(Q,w);
}
}//while
}//if
}//BFSTraverse
*递归深度优先遍历图
void DFSTraverse(Graph G){
for(i=0;i<G.vexnum;i++)
visited[i]=FALSE;
for(i=0;i<G.vexnum;++i)
if(!visited[i])
DFS(G,i);
}
//从顶点v出发深度优先遍历图G
void DFS(Graph G,int v){
Visit(v);
visited[v]=TRUE: //遍历顶点v所有邻接顶点,没访问过就给他塞进DFS里面去;
for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))
if(!visited[w])
DFS(G,w);
}
非递归深度优先遍历图,图为强连通图
因为是强连通图,则从任意顶点出发都可访问所有结点;
算法中不规定具体的存储结构,而将图Graph看成是一种抽象的数据类型。
非递归深度优先遍历存在两种写法,区别在于在遇到某个顶点的多个未访问的邻接点时,是按从右往左(高下标往低下标)顺序,还是从左往右(低下标往高下标);
第一种,反映在邻接表中就是从最右边的邻接点往左边顺序,反映在邻接矩阵存储中就是从高下标往低下标顺序;
//假设图为连通图,从顶点v开始访问
void DFSTraverse1(Graph G,int v){
for(i=0;i<G.vexnum;i++) //标记访问数组,初始值全为FALSE;
visited[i]=FALSE;
InitStack(S);
visited[v]=TRUE; //仅仅将第一个顶点标记为已访问,而非真的访问;
Push(S,v); //第一个顶点入栈;
while(!StackEmpty(S)){
Pop(S,k); //出栈后访问该顶点,此处为真正访问操场;
Visit(k); //遍历顶点k的邻接点,将未访问过的顶点全部入栈;
for(w=FirstAdjVex(G,k);w>=0;w=NextAdjVex(G,k,w))
if(!visited[w]){
visited[w]=TRUE; //每个顶点标记为已访问,而非真的访问;
Push(S,w);
}//if
}//while
}//DFSTraverse1
void DFSTraverse2(Graph G){
for(i=0;i<G.vexnum;i++)
visited[v]=FALSE;
InitStack(S);
Visit(0); //访问第一个顶点并标记已访问;
visited[0]=TRUE;
Push(S,0); //第一个顶点入栈
while(!StackEmpty(S)){
GetTop(S,k); //取栈顶元素,但不出栈,
for(w=FirstAdjVex(G,k);w>=0;w=NextAdjVex(G,k,w)){
if(!visited[w]){ //遍历栈顶元素k的邻接点
Visit(w); //在遇到第一个未访问的顶点时,访问并入栈,同时跳出循环;
visited[w]=TRUE;
Push(S,w);
break;
}
}//for
if(w<0){ //当栈顶元素k没有未访问的邻接点时将其出栈
Pop(S,e);
}
}//while
}//DFSTraverse2
若以邻接矩阵存储为例,实现第二种访问顺序,也就是正常手工求解深度优先遍历生成树的顺序;
void DFSTraverse(MGraph G){
for(v=0;v<G.vexnum;v++) //标记访问数组,初始值全为FALSE;
visited[v]=FALSE;
InitStack(S);
Visit(v);
visited[v]=TRUE;
Push(S,v);
while(!StackEmpty(S)){
GetTop(S,k);
for(i=0;i<G.vexnum;i++){ //遍历顶点k的邻接点,将第一个未访问过的进行访问后入栈;
if(G.arcs[k][i].adj&&!visited[i]){//存在未访问的边
Visit(i);
visited[i]=TRUE;
Push(S,i);
break; //成功找到一个未访问的入栈后就跳出for循环;
}
}//for
if(i>=G.vexnum) //若越界,说明顶点k没有未访问的邻接点,将其出栈;
Pop(S,e);
}//while
}
(邻接表)-输出由有向无环图表示的算术表达式的逆波兰表达式
有向无环图,即DAG图,联想到求拓扑排序和关键路径;以DAG图表示表达式则只有一个入度为0的结点,此外,非原子顶点即运算符顶点都只有两个出边,且第一条边邻接的顶点为其左操作数(左表达式),第二条边邻接的顶点为其右操作数(表达式);
算法需要首先寻找到入度为0的结点,作为起始结点,然后利用递归函数输出逆波兰表达式;
//输出以顶点r为根的表达式的逆波兰式
void NBL(ALGraph G,int r){
p=G.vertices[r].firstarc;
c=G.vertices[r].data;
if(!p) //若顶点i无出边,则为原子顶点,输出
printf("%c",c);
else{ //否则顶点i为运算符,则先递归输出左表达式和右表达式
NBL(G,p->adjvex);//输出左孩子表达式;
NBL(G,p->nextarc->adjvex);//输出右孩子表达式;
printf("%c",c); //最后输出操作符,即后缀;
}//else
}//PrintNBL
//查找入度为0的根顶点r;
void FindZero(ALGraph G){
FindIndegree(G,indegree);//FindIndegree为已定义函数计算每个顶点入度,存入indegree数组中;
for(i=0;i<G.vexnum;i++)
if(!indegree[i])
r=i;
NBL(G,r);
}
【查找】
折半查找的递归与非递归算法
从1开始存放元素,0存放顺序表长度;
//折半查找
int BinSearch(int A[],int low,int high,int x){
if(low>high) return -1;
mid=(low+high)/2;
if(A[mid]==x)
return mid;
else if(A[mid]>x)
return BinSearch(A,low,mid-1,x);
else
return BinSearch(A,mid+1,high,x)
}
int BinSearch(int A[],int x){
low=1;
high=A[0]; //A[0]保存了顺序表当前长度;
while(low<=high){
mid=(low+high)/2;
if(A[mid]==x)
return mid;
else if(A[mid]>x)
high=mid-1;
else
low=mid+1;
}
return -1;
}
(二叉链表)-判别给定二叉树是否为二叉排序树,结点值都>0且不同
利用中序遍历二叉树,判断是否有序,若存在逆序则说明不是二叉排序树;
//利用全局变量记录上一次值;
int last=0;
int flag=1; //全局变量标记
Status IsBinSort(BiTree T){
if(T->lchild&&flag)
IsBinSort(T->lchild);
if(T->data<last) //当遇到小于前驱结点值的结点,flag置0;
flag=0;
last=T->data;
if(T->rchild&&flag)
IsBinSort(T->rchlid);
return flag;
}
//利用参数记录上一次值;
void IsBinSort(BiTree T,int *last,int *flag){
if(*flag==TRUE){
if(T->lchild)
IsBinSort(T,last,flag);
if(T->data>*last)
*last = T->data;
else
*flag = FALSE;
if(T->rchild)
IsBinSort(T,last,flag);
}
}
(二叉链表)-递归从大到小输出二叉排序树中所有结点值不小于x的结点
void OutX(BiTree T,int x){
if(T){
OutX(T->rchild,x);
if(T->data>=x)
printf("%d",T->data);
else
return;
OutX(T->lchild,x);
}
}
*(二叉链表)-计算排序二叉树中指定结点所在层次
非递归
int FindNode(BiTree T,TElemType x){
if(!T) return 0;
int k=1;
p=T;
while(p){
if(p->data==x)
return k;
else if(p->data>x)
p=p->lchild;//在左子树中继续查找;
else
p=p->rchild;//在右子树中继续查找;
++k; //层数+1
}//while
return 0; //遍历结束仍未找到返回0
}
由输入的一组关键字构造哈希表,表长为m,哈希函数为H(x),链地址法解决冲突
//邻接表结点
typedef struct KNode{
int key;
struct KNode *next;
}KNode;
//表头结点
typedef struct{
KNdoe *first;
int num; //哈希表当前元素数量,可写可不写;
}HNode,HashTable[MAXSIZE];
void Create_Hash(HashTable H){//HashTable为数组首地址,不要加地址引用符号;
for(i=0;i<MAXSIZE;++i) //哈希表表头结点初始化
H[i].first=NULL;
while((k=InputKey())!=NULL){ //读取关键字
s=(KNode*)malloc(sizeof(KNode)); //建立结点
s->key=k;
s=>next=NULL;
p=H(k);
//以下对邻接表进行排序插入
if(H[p].first==NULL)
H[p].first=s;
else{
if(H[p].first->key>k){
s->next=H[p].first;
H[p].first=s;
}else{
r=H[p].first;
while(r->next&&r->next->key<k)
r=r->next;
s->next=r->next;
r->next=s;
}//else
}//else
H.num++;
}//whlie
}//Create_Hash
*(二叉链表)-二叉排序树的构造、插入、删除、查找
构造
二叉排序树的构造本质上就是一个一个结点的插入过程
Status CreateBST(BSTree &T,SqList L){
T=NULL;
if(L.length){
for(i=0;i<L.length;i++)
InsertBST(T,L.elem[i]);
return OK;
}
else
return ERROR;
}
插入
当二叉排序树中不存在关键字等于 key 的数据元素时,插入 key 并返回TRUE
Status InsertBST(BSTree T,int key){
if(!SearchBST(T,key,NULL,p)){//若没找到key,则插入
s=(BSTree)malloc(sizeof(BSTNode));
s->data=key;
s->lchild=s->rchlid=NULL;
if(!p)//若连它爸都不存在,则它自己变成根结点
T=s;
else if(p->data>key)
p->lchild=s;
else
p->rchild=s;
return OK;
}
else
return FALSE;
}
查找 ,两种形式;
第一种
若查找成功返回Key的指针,
若查找不成功则返回NULL
BSTree SearchBST1(BSTree T,int key){
if(!T||EQ(key,T->data))
return T;
else if(key<T->data)//LT:less than
return SearchBST1(T->lchild,key);
else
return SearchBST1(T->rchild,key);
}
第二种查找,为了能在查找不成功时返回插入位置 (利用p)
若查找成功,则指针 p 指向该数据元素结点,并返回TRUE
若查找不成功, 则指针 p 指向查找路径上访问的最后一个结点(即插入位置的父结点),并返回FALSE
指针 F 指向 T 的 双亲,其初始调用值为NULL
Status SearchBST(BSTree T,int key,BSTree F,BSTree p){
if(!T){
p=F;//查找失败,把它爸喊来
return FALSE;
}
else if(key==T->data){
p=T;
return TRUE;
}
else if(key<T->data)
return SearchBST(T->lchild,key,T,p);
else
return SearchBST(T->rchild,key,T,p);
}
删除
Status DeleteBST(BSTree T,int key){
if(!T)//不存在key这个结点
return FALSE;
else{
if(key==T->data)
return Delete(T);
else if(key<T->data)
return DeleteBST(T->lchild,key);
else
return DeleteBST(T->rchild,key);
}
}
Status Delete(BSTree &p){
if(!p->rchild){//右子树为空,重接左子树
q=p;
p=p->lchild;
free(q);
}
else if(!p->lchild){//左子树为空,重接右子树
q=p;
p=p->rchild;
free(q);
}
else{//左右子树均非空,则寻找其前驱;
q=p;
s=p->lchild;//转左
while(s->rchild){//进入最右尽头(即中序前驱)
q=s;
s=s->rchild;
}//循环结束时,s指向被删结点的中序前驱,q指向前驱的父结点
p->data=s->data;//用p的前驱取代p
if(q!=p) //q!=p说明前驱在左右右右,则把前驱的左子树接到q的右边
q->rchild=s->lchild;
else //q==p说明前驱就是左孩子,则把前驱的左子树接到q的左边,也就是p的左边
q->lchild=s->lchild;
free(s);
}
return OK;
}
【排序】
实现直接插入排序
要求:以L.r[k+1]为监视哨,1到k为待排记录,且k<MAXSIZE;
void InsertSort(SqList &L){
k=L.length;
while(i=k-1;i>0;--i){//从后往前遍历
if(L.elem[i]>L.elem[i+1]){//发现逆序
L.elem[k+1]=L.elem[i]; //设为哨岗
while(j=i+1;L.elem[j]<L.elem[k+1];++j) //将后续逆序数前移
L.elem[j-1]=L.elem[j];
L.elem[j-1]=L.elem[k+1]; //放入正确位置
}//if
}//while
}//InsertSort
实现2路插入排序
2路插入排序是在折半插入排序基础上进行改进,目的是减少排序过程中记录的移动次数,但需要额外n个记录大小的辅助空间,移动记录的次数约为n²/8,并且当第一个元素d[0]为L中最大或最小元素时,2路插入排序就失去了优越性;
具体逻辑:将L中第一个元素存入d[0]中,设置first、final分别指向已排序列中首元素和尾元素,初始都为指向d[0],然后依次将L中后续元素插入到已排序序列的前部序列或后部序列,,并动态更新first和final的指向。
注意往前循环移动时,有可能出现负数所以要+MAXSIZE修正,即first=(first-1+MAXSIZE)%MAXSIZE;而往后移动时,则不需要,即final=(final+1)%MAXSIZE;
void TwoWayInsertSort(SqList &L){
first=0; //标记已排序序列中首元素位置
final=0; //标记已排序序列中尾元素位置
ElemType d[MAXSIZE]; //辅助数组从0单元开始存储;
if(L.length>0) //将第一个元素存入辅助数组中
d[0]=L.elem[1];
for(i=2;i<=L.length;++i){ //处理剩余元素
if(L.elem[i]<d[first]){ //【当前元素<最小元素】
first=(first-1+MAXSIZE)%MAXSIZE;//往前循环移动时,指针可能为负,故+MAXSIZE修正;
d[first]=L.elem[i];
}else if(L.elem[i]<d[final]){//【最小元素<当前元素<最大元素】
for(j=final;d[j]>L.elem[i];j=(j-1+MAXSIZE)%MAXSIZE)
d[(j+1)%MAXSIZE]=d[j];//以在final序列之前进行直接插入排序;
d[(j+1)%MAXSIZE]=L.elem[i];
final=(final+1)%MAXSIZE;
}else{ //【最大元素<当前元素】
final=(final+1)%MAXSIZE;
d[final]=L.elem[i];
}
}//while //最后将辅助数组d元素全放回表中;
for(i=1,j=first;i<=L.length;++i,j=(j+1)%MAXSIZE)
L.elem[i]=d[j];
}
实现表插入排序
//静态链表结构定义
#define MAXSIZE 100
typedef struct{
RcdType key; //Rcd=Record即记录
int next;
}SlNode;
typedef struct{
SLNode r[MAXSIZE]; //0号单元为表头结点
int length;
}SLinkListType;
void TBSort(SLinkListType &L){
InitTB(L); //将数据存入表中;
L.r[0].key=MAXINT; //MAXINT为已定义最大整数常量
L.r[0].next=1; //构造初始循环链表,表头的next指向当前链表最小结点
L.r[1].next=0;
for(i=2;i<=L.length;i++){//从第二个结点开始逐个插入,
p=0;
q=L.r[p].next;
x=L.r[i].key;
while(q&&L.r[q].key<x){//寻找当前有序链表中,不小于x的第一个结点
p=q; //循环结束时,p指向最后一个小于x的结点,p指向抵押给不小于x的结点
q=L.r[q].next;
}
L.r[p].next=i;//将i插入到pq之间
L.r[i].next=q;
}//for
}
//记录重排
void Arrange(SLinkListType &L){
p=L.r[0].next; //p指示最小结点;
for(i=1;i<L.length;++i){ //1到i-1已经有序
while(p<i) //第i个记录在L中位置不小于i
p=L.r[p].next; //使p指向顺序上第i个记录;
q=L.r[p].next; //q指向p的下一个记录,即"应当的"第i+1记录;
if(p!=i){ //若p!=i意味着p不在正确位置上,进行调整;
Swap(L.r[p],L.r[i]); //将p与i对调
L.r[i].next=p; //同时将i的后继设为p
}//if
p=q; //更新当前最小元素;
}//for
}//Arrange
(顺序表)-使奇数排在偶数前,至多使用一个记录的辅助空间,时间复杂度O(n)
void Sort(int A[]){
left = 1;
right = A[0];
while(left<right){
while(left<right&&A[left]%2==1)
left++
while(left<right&&A[right]%2==0)
right--;
if(left<right){
Swap(A[left],A[right]);
left++;
right--;
}
}//while
}
(顺序表)-使负数排在非负数前,至多使用一个记录的辅助空间,时间复杂度O(n)
void Sort(int A[]){
left = 1;
right = A[0];
while(left<right){
while(left<right&&A[left]<0)
left++
while(left<right&&A[right]>=0)
right--;
if(left<right){
Swap(A[left],A[right]);
left++;
right--;
}
}//while
}
(单链表)-实现简单选择排序
void Select_Link(LinkedList &L){
q=L; //q指向已排序的尾结点
while(q->next){ //仍有未排序结点
pre=q;
minpre=pre;
while(pre->next){ //寻找待排序列中最小结点的前驱;
if(pre->next->data<minpre->next->data)
minpre=pre;
pre=pre->next;
}//while
if(minpre!=q->next){ //最小结点不是待排序列首结点
minp=minpre->next; //则把最小结点插入到q之后;
minpre->next=minp->next;
minp->next=q->next;
q->next=minp;
}
q=q->next;
}//while
}
荷兰国旗问题
typedef enum {RED,WHITE,BLUE} color;
void RWBSort(color [] C,int n){
low=0; //low以前全为红
high=n-1; //high以后全为蓝
k=0; //k为活动指针
while(k<=high){
switch(C[k]){
case RED: //k红,则把low交换过来,low一定是白,或者lowk同步;
Swap(C[low],C[k]);
low++;
k++;
break;
case WHITE: //k白,则k指针前进;
k++;
break;
case BLUE: //k蓝,则把high交换过来,high可能是白也可能是红,所以k不动;
Swap(C[k],C[high]);
high--;
}
}
}
砾石排序问题
可使用荷兰国旗的解法;或以下算法:
由算法逻辑知,red指向红色后一位置,white指向白色后一位置;
当red<white时,情况如下:red指向元素必为白色;
当red>white时,左侧全是红色,此时white还处于开始位置
当red==white时,red和white应当都处于初始位置;
最后一粒砾石是白色的情况:
最后一粒砾石是红色的情况:
算法最后结束时,红色red-1个,白色white-red个,蓝色n-white+1个;
//1红2白3蓝
void QKSort(int []C){
red=1; //指向最后红色后一位置
white=1; //指向最后白色后一位置
k=n; //指向当前处理元素
while(k!=white){
while(C[k]==3) //当前元素是蓝色,指针左移
k--;
if(C[k]==1){ //当前元素是红色
if(red>=white){ //red左侧全是红色,则把k所指红色交换到red;
Swap(C[k],C[red]);
red++;
}else{ //左侧有红有白,则先把red所指白色交换到white位置,
Swap(C[red],C[white]);
white++;
Swap(C[k],C[red]);//再把k所指红色交换到red位置;
red++;
}
}//red红色
if(C[k]==2){ //当前元素是白色
if(red<=white){ //左侧有白色,则把k所指白色交换到white位置;
Swap(C[k],C[white]);
white++;
}else{ //左侧全是红色,则把k所指白色交换到red位置;
Swap(C[k],C[red]);
white=red+1; //red指向最后红色后一位置,white指向最后白色后一位置;
}
}//if白色
}//while
if(C[k]==2) //处理最后一粒砾石
white++;
else if(C[k]==1){
Swap(C[red],C[k]);
red++;
white++;
} //最后红色red-1个,白色white-red个,蓝色n-white+1个;
}//QKSort