【数据结构】-常用算法

个人学习数据结构记录,如有错误或者可以优化的地方,烦请评论指出,谢谢。😃

目录

【线性表】

*(线性表)-求并集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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值