王道课后代码题

第二章-线性表

2-1-搜索整个顺序表,查找最小值元素并记住其位置,搜索结束后用最后一个元素填补空出的原最小值元素的位置。
bool Del_Min(Sqlist &L, int &value){
	if(L.length == 0) return;
	value = L.data[0];
	int pos = 0;
	for(i=1; i<L.length; i++){
		if(L.data[i] < value){
			value = L.data[i];
			pos = i;
		}
	}
	L.data[pos] = L.data[L.length-1];
	L.length--;
	return true;
}
2-2-顺序表L所有元素逆置,空间复杂度为O(1)
void reverse(SqList &L){
	int temp;
	for(int i=0; i<(L.length)/2; i++){
		temp = L.data[i];
		L.data[i] = L.data[L.length-i-1];
		L.data[L.length-i-1] = temp;
	}
}
2-3-顺序表删除所有值为x的数据元素,时间复杂度O(n),空间复杂度O(1)
void del_all_x(SqList &L, int x){
	int k=0;
	for(int i=0; i<L.length; i++){
		if(L.data[i] != x){
			L.data[k++] = L.data[i];
		}
	}
	L.length = k;
}
2-4-有序顺序表中删除给定值 s 与 t 之间的所有元素
bool del_s_t(SeqList &L, int s, int t){
	if(L.length == 0 || s>=t){
		return false;
	}
	for(int i=0; i<L.length&&L.data<s; i++);
	if(i>=L.length) return false;
	for(j=i; j<L.length&&L.data[j]<=t; j++);
	for(; j<L.length; j++){
		L.data[i++] = L.data[j];
	}
	L.length = i;
	return true;
}
2-5-从顺序表中删除给定值s与t之间的所有元素。
bool del(SqList &L, int s, int t){
	if(L.length==0 || s>t) return false;
	for(int i=0; i<L.length; i++){
		if(L.data[i]>=s && L.data[i]<=t){
			k++;
		}
		else{
			L.data[i-k] = L.data[i];
		}
	}
	L.length -=k;
	return true;
}
2-6-有序顺序表中删除所有值重复的元素,使所有元素的值均不同
bool del_same(SeqList &L){
	int i, j;
	for(i=0,j=1; j<L.length; j++){
		if(L.data[i] != L.data[j]){
			L.data[++i] = L.data[j];
		}
	}
	L.length = i+1;
	return true;
}
2-7-两个有序顺序表合并为一个新的有序顺序表,由函数返回结果顺序表
bool merge(SeqList A, SeqList B, SeqList &C ){
	if(A.length + B.length > C.length) retrun false;
	int i=0,j=0,k=0;
	while(i<A.length && j<B.length){
		if(A.data[i] < B.data[j]){
			C.data[k++]=A.data[i++];
		}
		else
			C.data[k++]=B.data[j++];
	}
	while(i<A.length){
		C.data[k++] = A.data[i++];
	}
	while(j<B.length){
		C.data[k++] = B.data[j++];
	}
	C.length = k;
	return true;
}
2-8-一维数组 A[m+n] 中将两个顺序表m和顺序表n 的位置互换
void reverse(int A[], int left, int right, int arraysize){
	int temp;
	if(left >= right || right>=arraysize) return;
	int mid = (left + right) / 2;
	for(int i=0; i<mid-left; i++){
		temp = A[left + i];
		A[left + i] = A[right - i];
		A[right - i] = temp;
	}
}

void exchange(int A[], int left, int right, int arraysize){
	reverse(A, 0, m+n-1, arraysize);
	reverse(A, 0, n-1, arraysize);
	reverse(A, n, m+n-1, arraysize)
}
2-9-递增有序顺序表,查找数值为 x 的元素。若找到则与其后继元素交换位置,找不到则将其插入表中。
void serach(int A[], int x){
	int low=0, high=n-1, temp;
	while(low < high){
		int mid = (low+high)/2;
		if(A[mid] = x)
			break;
		else if(A[mid] > x)
			high = mid - 1;
		else
			low = mid + 1;
	}
	if(A[mid] == x && mid != n-1){
		temp = A[mid];
		A[mid] = A[mid + 1];
		A[mid + 1] = temp;
	}
	if(low > high){
		for(i=n-1; i>high; i--)
			A[i+1] = A[i];
		A[i+1] = x;
	}
}
2-10一维数组R的序列循环左移p个位置,空间和时间上都尽量高效。
void reverse(int R[], int left, int right){
	int temp;
	int mid = (left + right) /2;
	for(int i=0; i<mid-left; i++){
		temp = R[left + i];
		R[left + i] = R[right - i];
		R[right - i] = temp;
	}

void exchange(int R[], int left, int right){
		reverse(R, 0, p-1);
		reverse(R, p, n-1);
		reverse(R, 0, n-1);
	}
}
2-11-找出两个有序序列的中位数并返回
int search_middle(int A[], int B[]){
	int c[length(A)+length(B)]={0};
	int i,j,k;
	while(i<A.length&&j<B.length){
		if(A[i]<B[j])
			C[k++] = A[i++];
		else
			C[k++] = B[j++];
	}
	while(i<A.length){
		C[k++] = A[i++];
	}
	while(j<B.length){
		C[k++] = B[j++];
	}
	return C[C.length/2];
}
2-12-设计一种尽可能高效的算法找出A的主元素
int majority(int A, int n){
	int c,count=0;
	c=A[0];
	for(int i=0; i<n; i++){
		if(A[i] == c){
			count++;
		}
		else
			if(count > 0)
				count--;
			else{
				c=A[i];
				count=1;
			}
	}
	if(count>0){
		for(int i=count=0; i<n; i++){
			if(A[i] == c)
				count++;
		}
	}
	if(count > n/2)
		return c;
	else
		return -1;
}
2-13-找出数组中未出现的最小正整数,时间上尽可能高效。
int find_Min(int A[], int n){
	B = (int*) malloc(sizeof(n));
	for(int i=0; i<n; i++){
		B[i]=0;
	}
	for(int i=0; i<n; i++){
		if(A[i]>=1 && A[i]<=n){
			B[A[i]-1] = 1;
		}
	}
	for(int i=0; i<n; i++){
		if(B[i] == 0){
			break;
		}
	}
	return i+1;
}
2-14 设计一个递归算法,不带头结点的单链表L删除所有值为x的结点
void del_x(LinkList &L, int x){
	LNode *p;
	if(L==null)
		return;
	if(L->data == x){
		p = L;
		L = L->next;
		free(p);
		del_x(L,x);
	}
	else{
		del_x(L->next,x);
	}	
}
2-15-带头结点的单链表L删除所有值为 x 的结点,释放其空间,假设值 x 的结点不唯一
void del_x(LinkList &L, int x){
	LNode *pre=L, *q;
	LNode *p=L->next;
	while(p!=null){
		if(p->data == x){
			q=p;
			p=p->next;
			pre->next=p;
			free(q);
		}
		else{
			pre=p;
			p=p->next;
		}
	}
}
2-16-带头结点的单链表L逆向输出每个结点的值
void reverse(LinkList &L){
	Stack s;
	InitStack(s);
	LNode *p=L->next;
	while(p!=null){
		push(S, p->data);
		p=p->next;
		while(p->next=null&&!StackEmpty(s)){
			pop(s, p);
			printf("%d",p->data);
		}
	}
}
2-17-带头结点的单链表L中删除最小值结点(假设最小值结点唯一),时间高效
LinkList del_min(LinkList &L){
	LNode *pre=L, *p=pre->next;
	LNode *minpre=pre, *minp=p;
	while(p!=null){
		if(p->data < minp->data){
			minp = p;
			minpre = pre;
		}
		pre=p;
		p=p->next;
	}
	minpre->next = minp->next;
	free(minp);
	return L;
}
2-18- 带头结点的单链表就地逆置,即辅助空间复杂度为O(1)。
LinkList reverse(LinkList &L){
	LNode *p=L-next, *r;
	L->next=null;
	while(p != null){
		r=p->next;
		p-next = L->next;
		L->next = p;
		p=r;
	}
	return L;
}
2-19- 带头结点的单链表L使其元素递增有序
void sort(LinkList &L){
	LNode *p=L->next, *pre;
	r=p->next;
	p->next=null;
	p=r;
	while(p!=null){
		r=p->next;
		pre=L;
		while(pre->next!=null && pre->next->data<p->data){
			pre=pre->next;
		}
		p->next=pre->next;
		pre->next=p;
		p=r;
	}
}
2-20- 带头结点的单链表L无序,删除表中所有介于给定的两个值之间的元素(若存在)
LinkList del(LinkList &L, int m, int n){
	LNode *p=L->next, *pre=L;
	while(p != null){
		if(p->data >m && p-data<n){
			pre->next = p->next;
			free(p);
			p=pre->next;
		}
		pre=p;
		p=p->next;
	}
	return L;
}
2-21-找出两个链表的公共结点
LinkList search(LinkList L1, LinkList L2){
	LNode *longList, *shortList;
	int len1=length(L1);
	int len2=length(L2);
	int dist;
	if(len1 > len2){
		longList=L1->next;
		shortList=L2->next;
		dist=len1-len2;	
	}
	else{
		longList=L2->next;
		shortList=L1->next;
		dist=len2-len1;
	}
	while(dist--){
		longList=longList->next;
	}
	while(longList){
		if(longList=shortList)
			return longList;
		else{
			longList=longList->next;
			shortList=shortList->next;
		}
	}
	return null;
}
2-22-带头结点的单链表,head为头指针,递增输出数据元素,并释放结点存储空间。(不允许使用数组作为辅助空间)
void increasing_print(LinkList &head){
	while(head->next !=null){
		LNode *pre=L, *p=pre->next, *q;
		while(p->next!=null){
			if(p-next->data<pre->next->data)
				pre=p;
			p=p->next;
			}
			printf("%d",pre->next->data);
			q=pre->next;
			pre->next=q->next;
			free(q);
	}
	free(head);
}
2-23-带头结点的单链表A分解为使A表中含有原表中序号为奇数的元素,B表中含有原表中序号为偶数的元素,相对顺序保持不变。
LinkList dis_chart(LinkList &A){
	LinkList B=(LNode*)malloc(sizeof(LNode));
	B->next=null;
	LNode *ra=A, *rb=B, *p=A->next;
	A->next=null;
	int i=0;
	while(p!=null){
		i++;
		if(i%2==0){
			rb->next=p;
			rb=p;
		}
		else{
			ra-next=p;
			ra=p;
		}
		p=p->next;
	}
	ra-next=null;
	rb->next=null;
	return B;
}
2-24-带头结点的单链表A={a1,b1,a2,b2······am,bn}拆解为两个线性表,使得A={a1,a2···an},B = {bm,····b2,b1}
LinkList dis_chart(LinkList &A){	
	LinkList B=(LNode*)malloc(sizeof(LNode));
	B->next=null;
	LNode *ra=A, *p=A->next, *q;
	A->next=null;
	while(p!=null){
		ra->next=p;
		ra=p;
		p=p->next;
		if(p!=null){
			q->p->next;
			p->next=B->next;
			B->next=p;
			p=q;
		}
	}
	ra->next=null;
	return B;
}
2-25-递增的单链表,去掉表中重复的元素,只保留一个数值
LinkList del_same(LinkList &L){
	LNode *p=L->next, *q;
	while(p!=null){
		q=p->next;
		if(p->data=q->data){
			p->next=q->next;
			free(q);
		}
		else{
			p=p->next;
		}	
	}
	return L;
}
2-26-两个递增的单链表归并为一个递减的单链表,并要求利用原来两个单链表的结点存放归并后的单链表
void MergeList(LinkList &La, LinkList &Lb){
	LNode *pa=La->next, *pb=Lb->next, *r;
	La->next=null;
	while(pa&&pb){
		if(pa->data < pb->data){
			r=pa->next;
			pa->next=La->next;
			La->next=pa;
			pa=r;
		}
		else{
			r=pb->next;
			pb->next=La->next;
			La->next=pb;
			pb=r;
		}
	}
	if(pa){
		pb=pa;
	}
	while(pb){
		r=pb->next;
		pb->next=La->next;
		La->next=pb;
		pb=r;
	}
	free(Lb);
}
2-27-两个带头结点并且递增的单链表A和B中产生单链表C,C中包含它们的公共元素,要求不破坏A、B的结点
LinkList search_com(LinkList A, LinkList B){
	LNode *pa=A->next, *pb=B->next, *r, *s;
	LinkList C=(LNode*)malloc(size(LNode));
	C->next=null;
	r=C;
	while(pa&&pb){
		if(pa->data<pb->data){
			pa=pa->next;
		}
		else if(pb->data<pa->data){
			pb=pb->next;
		}
		else{
			s=(LNode*)malloc(sizeof(LNode));
			s->data=pa->data;
			s->next=r->next;
			r->next=s;
			r=s;
			pa=pa->next;
			pb=pb->next;
		}
	}
	r->next=null
	return C;
}
2-28两个递增的单链表A和B,将A和B的公共元素存放于A链表中
LinkList Union(LinkList &A, LinkList &B){
	LNode *pa=A->next, *pb=B->next, *r, *q;
	A->next=null;
	r=A;
	while(pa&&pb){
		if(pa->data==pb->data){
			pa->next=r->next;
			r->next=pa;
			r=pa;
			pa=pa->next;
			q=pb;
			pb=pb->next;
			free(pb);
		}
		else if(pa->data<pb->data){
			q=pa;
			pa=pa->next;
			free(q);
		}
		else{
			q=pb;
			pb=pb->next;
			free(q);
		}
	}
	while(pa){
		q=pa;
		pa=pa->next;
		free(q);
	}
	while(pb){
		q=pb;
		pb=pb->next;
		free(q);
	}
	r->next=null;
	return A;
}
2-29-判断单链表B中的序列是否是单链表A中序列的连续子序列
int Pattern(LinkList A, LinkList B){
	LNode *pa=A, *pb=B, *pre=pa;
	while(pa&&pb){
		if(pa->data=pb->data){
			pa=pa->next;
			pb=pb->next;
		}
		else{
			pre=pre->next;
			p=pre;
			pb=B;
		}
	}
	if(pb==null) return 1;
	else return 0;
}
2-30-判断带头结点的循环双链表是否对称
int Symmetry(DLinkList L){
	DNode *p=L->next, *q=L->prior;
	while(p!=q && q->next=p){
		if(p->data == q->data){
			p=p->next;
			q=q->prior;
		}
		else
			return 0
	}
	return 1;
}
2-31-将循环单链表h2链接到h1之后,要求链接后的链表仍保持循环链表形式
LinkList link(LinkList &h1, LinkList &h2){
	LNode *p=h1, *q=h2;
	while(p->next != h1)
		p=p->next;
	while(q->next != h2)
		q=q->next;
	p->next=h2;
	q->next=h1;
	return h1;
}
2-32- 带头结点的循环单链表,结点值均为正整数,反复找出结点值最小的结点输出并删除,直到单链表为空,再删除表头结点。
void del_all(LinkList &L){
	LNode *p, *pre, *minpre, *minp;
	while(L->next != L){
		p=L->next;
		pre=p;
		minpre=pre;
		minp=p;
		while(p!=L){
			if(p->data<minp->data){
				minp=p;
				minpre=pre;
			}
			pre=p;
			p=p->next;
		}
		printf("%d",p->data);
		minpre->next=minp->next;
		free(minp)
	}
	free(L);
}
2-33-查找链表倒数第 k 个位置上的结点 (k为正整数)。若查找成功,输出该结点的data域的值,并返回1;否则只返回0。(已给出头指针list)
int search_k(LinkList L, int k){
	LNode *list=L->next, *q=list;
	int count=0;
	while(p!=null){
		if(count<k){
			count++;
		}
		else{
			q=q->next;
		}
		p=p->next;
	}
	if(count<k) return 0;
	else printf("%d",q->data);
}
2-34-带头结点的单链表保存保存单词,当两个单词有相同的后缀时,可共享相同的后缀存储空间,找出由 str1 和 str2所指向两个链表共同后缀的起始位置。
int len(LNode *head){
	int len=0;
	while(head->next){
		len++;
		head=head->next;
	}
	return len;
}
LNode *find_addr(LNode *str1, LNode *str2){
	LNode *p=str1, *q=str2;
	m=len(str1);
	n=len(str2);
	while(m<n){
		p=p->next;
	}
	while(n<m){
		q=q->next;
	}
	while(p->next!=null && p->next!=q->next){
		p=p->next;
		q=q->next;
	}
	return p->next;
}
2-35-带头结点的单链表保存 m 个整数,且 |data|<=n,(n为正整数), 对于链表中绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点,时间上尽可能高效。
void func(LinkList &L){
	LNode *p=L, *q, *r;
	q=(LNode*)malloc(sizeof(LNode));
	for(int i=0; i<m; i++){
		*(q+i)=0;
	}
	int n;
	while(p->next != null){
		if(p->next->data>0)
			n=p->next->data;
		else
			n=-p->next->data;
		if(*(q+n)==0)
			*(q+n)=1;
		else{
			r=p->next;
			p->next=r->next;
			free(r);
		}
	}
	free(q);
}
2-36-判断一个链表是否有环,如果有则返回环的入口地址
LNode *find_loop(LinkList head){
	LNode *fast=head, *slow=head;
	while(slow && fast->next){
		slow=slow->next;
		fast=fast->next->next;
		if(fast == slow)
			break;
	}
	if(slow==null || fast->next==null)
		return null;
	LNode *p=head, *q=slow;
	while(p!=q){
		p=p->next;
		q=q->next;
	}
	return p->next;
}
2-38-设线性表L(a1,a2,a3,…,an-2,an-1,an),采用带有头结点的链表保存,设计尽可能高效的算法重新排列L为L’(a1,an,a2,an-1,…)
LinkList relist(LinkList &head){
	LNode *p=head, *q=head, *r, *s;
	while(q->next!=null){
		p=p->next;
		q=q->next;
		if(q->next != null)
			q=q->next;
	}
	q=p->next;
	p->next=null;
	while(q->next!=null){
		r=q->next;
		q->next=p->next;
		p->next=q;
		q=r;
	}
	s=head->next;
	q=p->next;
	p->next=null;
	while(q!=null){
		r=q->next;
		q->next=s->next;
		s->next=q;
		q=r;
	}
}

第三章-栈、队列

3-1-进栈表示I,出栈表示O,判断一组有IO组成的序列是否合法
int judge(char A[]){
	int i=0,j,k;
	while(A[i] != '\0'){
		switch(A[i]){
			case 'I': j++;break;
			case'O':k++;
			if(k>j)
				return 0;
		}
		i++;
	}
	if(k!=j)
		return 0;
	else
		return 1;
}
3-2-单链表头指针为L,data域为字符型。判断链表的全部 n 个字符是否中心对称。例如 xyx 是中心对称
int symmetry(LinkList L, int n){
	LNode *p=L->next;
	int i=0;
	char[n/2];
	for(i=0; i<n/2; i++){
		s[i]=p->data;
		p=p->next;
	}
	i--;
	if(n%2!=0)
		p=p->next;
	while(p&&s[i]=p->data){
		i--;
		p=p->next;
	}
	if(i=-1)
		return 1;
	else
		return 0;
}
3-3-编写共享栈的入栈和出栈
#define maxsize 100
typedef struct{
	int stack[maxsize];
	int top[2];
}stk;
stk s;
//入栈
int push(int i, int key){
	if(i<1 || i>maxsize)
		return 0;
	if(s.top[1]-s.top[0] == 1)
		return 0;
	switch(i){
		case 0: s.stack[++s.top[0]]=x;return 1;break;
		case 1: s.stack[--s.top[1]]=x;return 1;
	}
}
//出栈
int pop(int i){
	if(i<1 || i>maxsize)
		return 0;
	switch(i){
		case 0:
			if(s.top[0]==-1) return 0;
			else return s.stack[s.top[0]--];
			break;
		case 1:
			if(s.top[1]==maxsize) return 0;	
			else return s.stack[s.top[1]++];		
	}
}
3-4-栈的括号匹配
bool match(char str[]){
	InitStack(S);
	int i=0;
	while(str[i] != '\0'){
		switch(str[i]){
			case '(': push(S, '(');break;
			case '[': push(S, '[');break;
			case '{': push(S, '{');break;
			case ')': pop(S, e); if(e != '(') return false; break;
			case ']': pop(S, e); if(e != '[') return false; break;
			case '}': pop(S, e); if(e != '}') return false; break;
		}
		i++;
	}
	if(!StackEmpty(S)) return false;
	else return true;
}
3-5-铁道火车调度问题,将所有的软座调度到硬座之前
void train_arrange(char *train){
	Stack s;
	InitStack(S);
	char *p=train, *q=train, c;
	while(*p){
		if(*p == 'H')
			push(s, *p);
		else
			*(q++)=*p;
		p++;
	}
	while(!StackEmpty(S)){
		pop(s, c);
		*(q++)=c;
	}
}
3-6-利用递归函数实现非递归计算
double p_solution(int n, int x){
	struct stack{
		int no;    //保存n
		double val;//保存Pn(x)值
	}st[maxsize];
	int top=-1, i;
	double fv1=1, fv2=2*x;
	for(i=n; i>=2; i--){
		top++;
		st[top].no=i;
	}
	while(top>=0){
		st[top].val=2*x*fv2-2*(st[top.no-1])*fv1;
		fv1=fv2;
		fv2=st[top].val;
		top--;
	}
	if(n==0) return fv1;
	return fv2;
}
3-7-客货车载渡问题
Queue q //载渡队列
Queue q1 //客车队列
Queue q2 //货车队列
void manager(){
	int i, j; //j总车辆
	while(j<10){
		if(!QueueEmpty(q1) && i<4){
			Dequeue(q1,x);
			Enqueue(q,x);
			i++;
			j++;
		}
		else if(i==4 && !QueueEmpty(q2)){
			Dequeue(q2,x);
			Enqueue(q,x);
			j++;
		}
		else{
			while(j<10&&i<4&&!QueueEmpty(q2)){
				Dequeue(q2,x);
				Enqueue(q,x);
				i++;
				j++;
			}
			i=0;
		}
		if(QueueEmpty(q1)&&QueueEmpty(q2)) return;
	}
}

第五章-树

5-1-一颗采取顺序存储的二叉树,求两个节点的最近公共祖先
int com_ancestor(Sqtree T[], int i, int j){
	if(T[i]!='#' && T[j]!='#'){
		while(i!=j){
			if(i>j)
				i=i/2;
			else
				j=j/2;
		}
		return T[i];
	}
}
5-2-编写后序遍历二叉树的非递归算法
void post_order(BiTree T){
	Stack s;
	BiNode *p=T, *r=null;
	InitStack(s);
	while(p || !StackEmpty(s)){
		if(p){
			push(s,p);
			p=p->lchild;
		}
		else{
			getTop(s,p);
			if(p->rchild && p!=r){
				p=p->rchild;
			}
			else{
				pop(s,p);
				visit(p->data);
				r=p;
				p=null;
			}
		}
	}
	
	
}
5-3- 编写二叉树的自下而上、自右到左的层次遍历算法
void level(BiTree T){
	BiNode *p=T;
	Stack S; Queue(Q);
	InitQueue(Q);
	InitStack(S);
	Enqueue(Q,p);
	while(!QueueEmpty(Q)){
		Dequeue(Q,p);
		push(S,p);
		if(p->lchild)
			Enqueue(Q,p->lchild);
		if(p->rchild)
			Enqueue(Q,p->rchild);
	}
	while(!StackEmpty(S)){
		pop(S,p);
		visit(p->data);
	}
}
5-4-非递归算法求二叉树的高度
int tree_depth(BiTree T){
	BiTree q[maxsize];
	int rear=-1, front=-1;
	int level=0, last=0;
	if(!T)
		return 0;
	q[++rear]=T;
	BiTree p;
	while(front < rear){
		p=q[++front];
		if(p->lchild)
			q[++rear]=p->lchild;
		if(p->rchild)
			q[++rear]=p->rchild;
		if(front == last){
			level++;
			last=rear;
		}
	}
	return level;
}

/*
求某层的结点个数、每层的结点个数、树的最大宽度等,都采用与此题类似的思想。当然,此题可编写递归算法,其实现如下
*/
int Btdepth2(BiTree T)
{
    if(T==NULL)
        return 0;  //空树,高度为0
    ldep = Btdepth(T->lchild); //左子树高度
    rdep = Btdepth(T->rchild); //右子树高度
    if(ldep > rdep)
        return ldep+1; //树的高度为子树最大高度加根节点
    else
        return rdep+1;
}
5-5-二叉树各结点的值互不相同,其先序遍历和中序遍历序列分别存于两个一维数组A[1···n]和B[1···n]中,试编写算法建立该二叉树的二叉链表。
/*
由先序序列和中序序列可以唯一确定一棵二叉树。算法的实现步骤如下:
1) 根据先序序列确定树的根结点。
2) 根据根结点在中序序列中划分出二叉树的左、右子树包含哪些结点,然后根据左、右子树结点在先序序列中的次序确定子树的根结点,即回到步骤1)。
3) 如此重复上述步骤,直到每棵子树仅有一个结点(该子树的根结点)为止。
*/
BiTree PreInCreat(int A[],int B[],int l1,int h1,int l2,int h2)
{
    //11,h1为先序的第一和最后一个结点下标,12,h2为中序的第一和最后一个结点下标
    //初始调用时,l1=l2=1, h1=h2=n
	root = (BiTNode*)malloc(sizeof(BiTNode));
    root->data = A[l1];
    for(i = l2;B[i]!=root->data;i++);
    llen = i - l2;
    rlen = h2 - i;
    if(llen)
        root->lchild = PreInCreat(A,B,l1+1,l1+llen,l2,l2+llen-1);
    else
        root->lchild = NULL;
    return root;
}
5-6-二叉树按二叉链表形式存储,写一个判别给定二叉树是否是完全二叉树的算法。
/*
根据完全二叉树的定义,具有n个结点的完全二叉树与满二又树中编号从1~n的结点一一对应。
算法思想:采用层次遍历算法,将所有结点加入队列(包括空结点)。遇到空结点时,查看其后是否有非空结点。若有,则二又树不是完全了叉树。
*/
bool IsComplete(BiTree T)
{
	Queue Q;
    InitQueue(Q);
    if(!T)
        return 1; //空树为满二叉树
    EnQueue(Q,T);
    while(!IsEmpty(Q))
    {
        DeQueue(Q,p);
        if(p)  //结点非空,将其左、右子树入队列(包括空节点)
        {
            EnQueue(Q,p->lchild); 
            EnQueue(Q,p->rchild);
        }
        else   //结点为空,检查其后是否有非空结点
            while(!IsEmpty(Q)){
                DeQueue(Q,p);
                if(p)    //结点非空,则二叉树为非完全二叉树
                    return 0;
            }
    }
    return 1;
}
5-7-二叉树按二叉链表形式存储,计算一棵给定二叉树的所有双分支结点个数
int d_node_count(BiTree T){
	int cnt=0;
	if(!T)
		return 0;
	if(T->lchild && T->rchild)
		cnt++;
	cnt += d_node_count(T->lchild);
	cnt += d_node_count(T->rchild);
} 
5-8-二叉树B按二叉链表形式存储,编写一个树B中所有结点的左、右子树进行交换的函数。
void swap(BiTree &T){
	int temp;
	if(T != NULL){
		swap(T->lchild);
		swap(T->rchild);
		temp=T->lchild;
		T->lchild=T->rchild;
		T->rchild=temp;
	}	
}
5-9-二叉树按二叉链表形式存储,求先序遍历序列中第 k (1<=k<=二叉树中结点个数) 个结点的值。
/*
设置一个全局变量i记录已访问过的结点的序号,其初值是根结点在先序序列中的序号,即1.当二叉树b为空时返回特殊字符,,当i==k时,表示找到了满足条件的结点,返回b->data;当ik时,递归地在左子树中查找,若找到则返回该值,否则继续递归地在右子树中查找,并返回其结果。
本题实质上就是一个遍历算法的实现,只不过用一个全局变量来记录访问的序号,求其他遍历序列的第k个结点也采用相似的方法。二叉树的遍历算法可以引申出大量的算法题,因此考生务必要熟练掌握二又树的遍历算法。
*/
int i=1;  //遍历序号的全局变量
int PreNode(BiTree b,int k)
{
    if(b==NULL) //空结点,则返回特殊字符
        return '#'; //相等,则当前结点即为第k个结点
    if(i==k)
        return b->data;
    i++; //下一个结点
    ch = PreNode(b->lchild,k); //左子树中递归寻找
    if(ch !='#') //在左子树中,则返回该值
        return ch;
    ch = PreNode(b->rchild,k); //在右子树中递归寻找
    	return ch;
}

5-10-二叉树按二叉链表形式存储,对于树中每个元素值为 x 的结点,删去以它为根的子树,并释放相应的空间。
/*
删除以元素值x为根的子树,只要能删除其左、右子树,就可以释放值为x的根结点,因此宜采用后序遍历。
算法思想:删除值为x的结点,意味着应将其父结点的左(右)子女指针置空,用层次遍历易于找到某结点的父结点。
本题要求删除树中每个元素值为x的结点的子树,因此要遍历完整棵二叉树。
*/
 void DeleteXTree(BiTree bt)   //删除以bt为根的子树
 {
     if(bt){
         DeleteTree(bt->lchild);
         DeleteTree(bt->rchild); //删除bt的左子树、右子树
         free(bt);  //释放被删结点所占的存储空间
     }
 }

//在二叉树上查找所有以x为元素值的结点,并删除以其为根的子树
void Search(BiTree bt,int x)
{
    BiTree Q[maxsize]; //Q是存放二叉树结点指针的队列,容量足够大
    if(bt){
        if(bt->data == x){  //若根结点值为x,则删除整棵树
            DeleteXTree(p->lchild);
        	exit(0);    
        }
        Init Queue(Q);
        EnQueue(Q,bt);
        while(!IsEmpty(Q)){
            DeQueue(Q,p);
            if(p->lchild) //若左子女非空
                if(p->lchild->data == x){ //左子树符合则删除左子树
                    DeleteXTree(p->lchild);
                    p->lchild = NULL;
                }  //父结点的左子女置空
                else
            		EnQueue(Q,p->lchild); //左子树入队列
            if(p->rchild) //若右子女非空
                if(p->rchild->data ==x){ //右子女符合则删除右子树
                    DeleteXTree(p->rchild);
                    p->rchild = NULL; //父结点的右子女置空
                }
            	else
                    EnQueue(Q,p->rchild); //右子女入队列
        }
    }
}
5-11-二叉树中查找值为 x 的结点,打印值为 x 的结点的所有祖先,假设值为x的结点不多于一个。
/*
算法思想:采用非递归后序遍历,最后访问根结点,访问到值为x的结点时,栈中所有元素均为该结点的祖先,依次出栈打印即可。因为查找的过程就是后序遍历的过程,因此使用的栈的深度不超过树的深度。
*/
typedef struct{
    BiTree t;
    int tag; //tag=0表示左子女已被访问,tag=1表示右子女已被访问
}stack;
void Search(BiTree bt,int x)
{
    stack s[]; //栈容量足够大
    top = 0;
    while(bt!=NULL || top>0) 
    {
        while(bt!=NULL && bt->data !=x) //结点入栈
        {
            s[++top].t = bt;
            s[top].tag = 0;
            bt = bt->lchild; //沿左分支向下
        }
        if(bt->data ==x)
        {
        	printf("所查结点的所有祖先结点的值为:\n"); //找到x
            for(i=1;i<=top;i++)
                printf("%d",s[i].t->data); //输出祖先值后结束
            exit(1);
        }
        while(top!=0 && s[top].tag==1)
            top--;  //退栈(空遍历)
        if(top!=0)
        {
            s[top].tag =1;
            bt = s[top].t->rchild;  //沿右分支向下遍历
        }
    }//while
}
5-12-二叉树按二叉链表形式存储,试求非空二叉树b的宽度 (即具有结点数最多的那一层的结点个数)。
/*
采用层次遍历的方法求出所有结点的层次,并将所有结点和对应的层次放在一个队列中。然后通过扫描队列求出各层的结点总数,最大的层结点总数即为二又树的宽度。
注意:本题队列中的结点,在出队后仍需要保留在队列中,以便求二又树的宽度,所以设置的队列采用非环形队列,否则在出队后可能被其他结点覆盖,无法再求二又树的宽度。
*/
typedef struct(
    BiTree data[MaxSize];	//i保存队列中的结点指针
    int level[MaxSize];		//保存data中相同下标结点的层次
    int front,rear;
}Qu;

int BTWidth(BiTree b){
	BiTree p;
	int k,max,i,n;
	Qu.front=Qu.rear= -1;		//队列为空
	Qu.rear++;
	Qu.data[Qu.rear]=b;			//根结点指针入队
	Qu.level[Qu.rear]=1; 		//根结点层次为1
	while(Qu.front<Qu.rear){
		Qu.front++;				//出队
		p=Qu.data[Qu.front];	//出队结点
		k=Qu.level[Qu.front];	//出队结点的层次
	if(p->lchild!=NULL){		//左孩子进队列
		Qu.rear++;
		Qu.data[Qu.rear]=p->lchild;
		Qu.level[Qu.rear]=k+1;
    }
        
	if(p->rchild!=NULL){		//右孩子进队列
		Qu.rear++;
		Qu.data [Qu.rear]=p->rchild;
		Qu.level[Qu.rear]=k+1;
	}
}//while
    max=0;i=0;						//max保存同一层最多的结点个数
    k = 1; 							//k表示从第一层开始查找
    while(i<=Qu.rear){			    //i扫描队中所有元素
        n=0;						//n统计第k层的结点个数
        while(i<=Qu.rear&6Qu.level[i]==k){
            n++;
            i++;
        }
        k=Qu.level[i];
        if(n>max) max=n;			//保存最大的n
    }
	return max;
}
5-13-设有一棵满二叉树(所有结点值均不同),已知其先序序列为pre,设计一个算法求其后序序列post.
/*
对一般二叉树,仅根据先序或后序序列,不能确定另一个遍历序列。但对满二叉树,任意一个结点的左、右子树均含有相等的结点数,同时,先序序列的第一个结点作为后序序列的最后一个结点,由此得到将先序序列pre[11..h1]转换为后序序列post[12..h2]的递归模型如下:
f(pre,l1,h1,post,l2,h2) =不做任何事情           h1<l1时
f(pre,l1,h1,post,l2,h2) = post[h2] = pre[11]  其他情况
	取中间位置half=(h1-12)/2:
	将pre[l1+1,l1+ha1f]左子树转换为post[l2,l2+half-1],
	即f(pre,l1+1,l1+half,post,12,l2+half-1);
	将pre[11+half+1,h1]右子树转换为post[12+half,h2-1],
	即f(pre,l1+half+1,h1,post,l2+half,h2-1)。
其中,post[h2]=pre[l1]表示后序序列的最后一个结点(根结点)等于先序序列的第一个结点(根结点)。
*/

void PreToPost(int pre[],int l1,int h1,int post[]int l2,int h2){
	int half;
	if(h1>=l1){
		post[h2]=pre[l1];
		half = (h1-l1)/2;
		PreroPost(pre,l1+1,l1+half,post,l2,l2+half-1);//转换左子树
		PreroPost(pre,l1+half+1,h1,post,l2+half,h2-1;//转换右子树
     }
}
5-14-二叉树按二叉链表形式存储,设计算法将二叉树的叶结点按从左到右的顺序连成一个单链表,表头指针为head,链接时用叶结点的右指针域来存放单链表指针。
/*
算法思想:设置前驱结点指针pre,初始为空。第一个叶结点由指针head指向,遍历到叶结点时,就将它前驱的rchi1d指针指向它,最后一个叶结点的rchi1d为空。算法的时间复杂度为O(n),辅助变量使用head和pre,栈空间复杂度为O(n)。
*/
LinkedList head,pre=NULL;				//全局变量
LinkedList InOrder(BiTree bt){
    if(bt){
        Inorder(bt->lchild);			//中序遍历左子树
        if(bt->lchild==NULL && bt->rchild==NULL)//叶结点
            if(pre==NULL){
                head=bt;
                pre=bt;
            } 							//处理第一个叶结点
            else{
                pre->rchild=bt;
                pre=bt;
            }							//将叶结点链入链表
    Inorder(bt->rchild);				//中序遍历右子树
    pre->rchi1d=NULL;					//设置链表尾
	return head;
}

5-15-判断两棵二叉树是否相似的算法,所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;或T1的左子树和T2的左子树是相似的,且T1的右子树和T2的右子树是相似的。
/*
本题采用递归的思想求解,若T1和T2都是空树,则相似;若有一个为空另一个不空则必然不相似;否则递归地比较它们的左、右子树是否相似。递归函数的定义如下:
1) 若T1=T2==NULL,则f(T1,T2) =1;
2) 若T1和T2之一为NULL,另一个不为NULL,则f(T1,T2) =0;。
3) 若T1和T2均不为NULL,则f(T1,T2) = f(T1->1child,T2->1child) && f(T1->rchild,T2->rchild);
*/
int similar(BiTree T1,BiTree T2){
	//采用递归的算法判断两个二叉树是否相似
	int leftS,rightS;
	if(T1==NULL && T2==NULL)//两树皆空
		return 1;
	else if(T1==NULL||T2==NULL//只有一树为空
		return 0;
	else{   						//递归判断
		leftS = similar(T1->lchild,T2->lchild);
		rightS = similar(T1->rchild,T2->rchild);
		return leftS && rightS;
    }
}
5-16-写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法
/*
算法思想:在后序序列中,若结点p有右子女,则右子女是其前驱,若无右子女而有左子女,则左子女是其前驱。若结点p左、右子女均无,设其中序左线索指向某祖先结点f(p是f右子树中按中序遍历的第一个结点),若f有左子女,则其左子女是结点p在后序下的前驱;若f无左子女,则顺其前驱找双亲的双亲,一直找到双亲有左子女(这时左子女是p的前驱)。还有一种情况,若p是中序遍历的第一个结点,则结点p在中序和后序下均无前驱。
*/
BiThrTree InPostPre(BiThrTree t,BiThrTree p){
    BiThrTree q;
    if(p->rtag == 0) //若p有右子女,则右子女是其后序前驱
        q = p->rchild;
    else if(p->ltag ==0) //若p只有左子女,左子女是其后序前驱
        q = p->lchild;
    else if(p->lchild ==NULL)
        q = NULL;  //p是中序序列第一结点,无后序前驱
    else //顺左线索向上找p的祖先,若存在,再找祖先的左子女
    {
        while(p->ltag ==1 && p->lchild!=NULL)
            p = p->lchild;
        if(p->ltag ==0)
            q = p->lchild;  //p结点的祖先的左子女是其后序前驱
        else
            q = NULL;  //仅有单支树(p是叶子),已到根结点,p无后序前驱
    }
    return q;
}

5-16-二叉树按二叉链表形式存储,设计求二叉树T的WPL的算法
/*
(1) 给出算法的基本设计思想
(2) 给出二叉树结点的数据类型定义
(3) C++语言描述算法,关键之处给出注释
*/

/*
考查二叉树的带权路径长度,二叉树的带权路径长度为每个叶结点的深度与权值之积的总和,可以使用先序遍历解决问题。
1)算法的基本设计思想。
基于先序递归遍历的算法思想是用一个static变量记录wpl,把每个结点的深度作为递归函数的一个参数传递。
算法步骤如下:
① 若该结点是叶结点,则变量wpl加上该结点的深度与权值之积。
② 若该结点是非叶结点,则左子树不为空时,对左子树调用递归算法,右子树不为空,对右子树调用递归算法,深度参数均为本结点的深度参数加1。
③最后返回计算出的wpl即可。

PS:当static关键字用于代码块内部的变量的声明时,用于修改变量的存储类型,即从自动变量修改为静态变量,但变量的链接属性和作用域不受影响。用这种方式声明的变量在程序执行之前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。也就是说,它保持局部变量内容的持久。静态局部变量的生存期虽然为整个源程序,但其作用域仍与局部变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后,尽管该变量还继续存在,但不能使用它。
*/
// 二叉树结点的数据类型定义如下:
typedef struct BiTNode{
    int weight; 
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

int WPL(BiTree root){
	return wpl_PreOrder(root,0);
}

int wpl_Preorder(BiTree root,int deep){
	static int wpl=0;					//定义一个static变量存储wp1
	if(root->lchild==NULL && root->rchild==NULL)	//若为叶结点,累积wp1
		wpl += deep*root->weight;
    if(root->lchild !=NULL)				//若左子树不空,对左子树递归遍历
        wp1_PreOrder(root->1child,deep+1);
     if(root->rchild !=NULL)	//若右子树不空,对右子树递归遍历
        wp1_PreOrder(root->rchild,deep+1);
    return wpl;

5-17-将给定的表达式(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。
/*
题目给定条件:二叉树结点定义如下:
typedef struct node{
	char data[10];
	struct node *left,*right;
}
(1) 给出算法的基本设计思想
(2) C++语言描述算法,关键之处给出注释
*/

/*
1)算法的基本设计思想:
表达式树的中序序列加上必要的括号即为等价的中缀表达式。可以基于二叉树的中序遍历策略得到所需的表达式。
表达式树中分支结点所对应的子表达式的计算次序,由该分支结点所处的位置决定。为得到正确的中缀表达式,需要在生成遍历序列的同时,在适当位置增加必要的括号。显然,表达式的最外层(对应根结点)和操作数(对应叶结点)不需要添加括号。
2)算法实现:
将二又树的中序遍历递归算法稍加改造即可得本题的答案。除根结点和叶结点外,遍历到其他结点时在遍历其左子树之前加上左括号,遍历完右子树后加上右括号。
*/
void BtreeToE(BTree *root){
	BtreeToExp(root,1);  //根的高度为1
}

void BtreeToExp(BTree*root,int deep)
{
    if(root==NULL) return;   //空结点返回
    else if(root->left==NULL && root->right==NULL) //若为叶结点
        printf("%s",root->data);
    else{
        if(deep>1) printf("(");  //输出操作数,不加括号
        BtreeToExp(root->left,deep+1); //若有子表达式则加1层括号
        printf("%s",root->data);  //输出操作符
        BtreeToExp(root->right,deep+1);  
        if(deep>1) printf(")");   //若有子表达式则加1层括号
    }
}
5-18-编程以孩子兄弟表示法存储的森林的叶子结点数
/*
当森林(树)以孩子兄弟表示法存储时,若结点没有孩(fch=nul1),则它必是叶子,总的叶子结点个数是孩子子树(fch)上的叶子数和兄弟子树(nsib)上的叶结点个数之和。
*/

typedef struct node
{
    int data;		//数据域
	int node *fch,*nsib;//孩子与兄弟域
}*Tree;
int Leaves(Tree t){  //计算以孩子兄弟表示法存储的森林的叶子数
	if(t==NULL)
		return 0;	 //树空返回0
	if(t->fch==NULL) //若结点无孩子,则该结点必是叶子
		return 1+Leaves(t->nsib);//返回叶子结点和其兄弟子树中的叶子结点数
	else			//孩子子树和兄弟子树中叶子数之和
	    return Leaves(t->fch)+Leaves(t->nsib);
}
5-19-以孩子兄弟链表为存储结构,请设计递归算法求树的深度
/*
由孩子兄弟链表表示的树,求高度的算法思想如下:采用递归算法,若树为空,高度为零;否则,高度为第一子女树高度加1和兄弟子树高度的大者。其非递归算法使用队列,逐层遍历树,取得树的高度。
*/

int Height(CSTree bt){
	//递归求以孩子兄弟链表表示的树的深度
	int hc,hs;
	if(bt==NULL)
		return 0;
	else{	//否则,高度取子女高度+1和兄弟子树高度的大者
		hc = height(bt->firstchild);	//第一子女树高
		hs = height(bt->nextsibling);	//兄弟树高
    if(hc+1>hs)
        return hc+1;
    else
        return hs;
	}
}
5-20-已知一棵树的层次序列及每个结点的度,编写算法构造此时的孩子-兄弟链接
/*
本题与树的层次序列有关。可设立一个辅助数组pointer[]存储新建树的各结点的地址,再根据层次序列与每个结点的度,逐个链接结点。
*/

#define maxNodes 15
void createcSTree_Degree(Csfree&T,int e[],int degree[],int n){
//根据树结点的层次序列e[]和各结点的度degree[]构造树的孩子-兄弟链表
//参数n是树结点个数
CSNode *pointer = new CSNode[maxNodes];//判断pointer[i]为空的语句未写
int i,j,d,k=0;
for(i=0;i<n;i++){		//初始化
	pointer[i]=new csNode;//判断pointer[i]为空的语句未写
	pointer[i]->data=e[i];
	pointer[i]->lchild=pointer[i]->rsibling=NULL;
}
    
for(i=0;i<n;i++){
	d=degree[i];//结点i的度数
	if(d){
		k++;//k为子女结点序号
		pointer[i]->1child=pointer[k];//建立i与子女k间的链接
		for(j=2;j<=d;j++){
			k++;
			pointer[k-1]->rsibling = pointer[k];
        }
    }
	T = pointer[0];
	delete [] pointer;
}
5-21-判断给定的二叉树是否是二叉排序树
/*
对二叉排序树来说,其中序遍历序列为一个递增有序序列。因此,对给定的二叉树进行中序遍历,若始终能保持前一个值比后一个值小,则说明该二又树是一棵二又排序树。
*/
int predt=-32767;//predt为全局变量,保存当前结点中序前驱的值,初值为-无穷。
int JudgeBST(BiTree bt){
	int b1,b2;
	if(bt==NULL)//空树
    	return 1;
	else{
		b1=JudgeBST(bt->1child);//判断左子树是否是二又排序树
		if(b1==0 || predt>=bt->data)//以若左子树返回值为0或前驱大于等于当前结点
			return 0; //不是二叉排序树
		predt=bt->data;//保存当前结点的关键字
		b2=JudgeBST(bt->rchild);//判断右子树
		return b2;   //返回右子树的结果
    }
}
5-21-设计一个算法,求出指定结点在给定二叉排序树中的层次
/*
算法思想:设二又树采用二又链表存储结构。在二叉排序树中,查找一次就下降一层。因此,查找该结点所用的次数就是该结点在二又排序树中的层次。采用二叉排序树非递归查找算法,用n保存查找层次,每查找一次,n就加1,直到找到相应的结点。
*/

int level(BiTree bt,BSTNode *p){
	//本算法计算给定结点在二叉排序树中的层次
	int n=0;//统计查找次数
	BiTree t=bt;
	if(bt!=NULL){
		n++;
		while(t->data!=p->data){
			if(t->data < p->data)//在左子树中查找
				t = t->lchild;
			else 				 //在右子树中查找
				t = t->rchild;
			n++;//层次加1
    	}
    }
    return n;
}
5-22-利用二叉树遍历的思想编写一个判断二叉树是否平衡二叉树的算法
/*
设置二叉树的平衡标记balance,标记返回二又树bt是否为平衡二叉树,若为平衡二叉树,
则返回1,否则返回0:h为二又树bt的高度。采用后序遍历的递归算法:
1)若bt为空,则高度为0,balance=1。
2)若bt仅有根结点,则高度为1,balance=1。
3)否则,对bt的左、右子树执行递归运算,返回左、右子树的高度和平衡标记,bt的高度
为最高子树的高度加1。若左、右子树的高度差大于1,则balance=0;若左、右子树的
高度差小于等于1,且左、右子树都平衡时,balance=1,否则balance=0。
*/

void Judge AVL(BiTree bt,int &balance,int sh){
	//本算法判断一个给定的二叉树是否为平衡二叉树
	int b1=0,br=0,hl=0,hr=0;//左、右子树的平衡标记和高度
	if(bt==NULL{//空树,高度为0
		h=0;
		balance=1;
	}
	else if(bt->1child==NULL&6bt->xchild==NULL{//仅有根结点,则高度为1
		h=1;
		balance=1;
	}
	else{
		Judge _AVL(bt->1child,bl,h1);//递归判断左子树
		Judge_AVL(bts>rchild,br,hr);//递归判断右子树
		h=(h1>hr?hl:hr)+1;
		if(abs(hl-hr)<2//若子树高度差的绝对值<2,则看左、右子树是否都平衡
			balance=bl&&br;//66为逻辑与,即左、右子树都平衡时,二叉树平衡
		else
			balance=0;
	}
}
5-23-设计一个算法,求出给定二又排序树中最小和最大的关键字。
/*
在一棵二又排序树中,最左下结点即为关键字最小的结点,最右下结点即为关键字最大的结点,本算法只要找出这两个结点即可,而不需要比较关键字。
*/

int MinKey(BSTNode *bt){
    //求出二叉排序树中最小关键字结点
    while(bt->lchild != NULL)
        bt=bt->lchild;
    return bt->data;
}
          
int MaxKey(BSTNode *bt){
    //求出二叉排序树中最大关键字结点
    while(bt->rchild != NULL)
    	bt = bt->rchild;
    return bt->data;
}
5-24-设计一个算法,从小到大输出二叉排序树中所有值小于 k 的关键字
/*
由二叉排序树的性质可知,右子树中所有的结点值均大于根结点值,左子树中所有的结点值均小于根结点值。为了从大到小输出,先遍历右子树,再访问根结点,后遍历左子树。
*/

void OutPut(BSTNode *bt, int k)
{
    //本算法从大到小输出二叉排序树中所有值不小于k的关键字
    if(bt==NULL)
    	return;
    if(bt->rchild != NULL)
    	OutPut(bt->rchild,k);	//递归输出右子树结点
    if(bt->data >= k)
    	printf("%d",bt->data);	//只输出大于等于k的结点值
    if(bt->lchild !=NULL)
    	OutPut(bt->lchild,k);//递归输出左子树的结点
}
5-25-编写一个递归算法,在一棵有n个结点的、随机建立起来的二又排序树上查找第k (1<k<n) 小的元素,并返回指向该结点的指针。要求算法的平均时间复杂度为O(log2n),二又排序树的每个结点中除data、lchild、rchild等数据成员外,增加一个count成员,保存以该结点为根的子树上的结点个数。
/*
在以t为根的子树上寻找第k小的元素,返回其所在结点的指针。k从11开始计算,在树结点中增加一个count数据成员,存储以该结点为根的子树的结点个数。
最大查找长度取决于树的高度。由于二叉排序树是随机生成的,其高度应是O(log2n),时间复杂度为O(log2n)。
*/
BSTNode *Search_Small(BSTNode*t,int k){
    if(k<1 || k>t->count) return NULL;
    if(t->lchild==NULL){
        if(k==1) return;
        else return Search_Small(t->rchild,k-1);
    }
    else{
        if(t->lchild->count == k-1) 
            return t;
        if(t->lchild->count > k-1) 
            return Search_Small(t->lchild,k);
        if(t->lchild->count < k-1)
        	return Search_Small(t->rchild,k-(t->lchild->count+1));
}

第六章-图

6-1-写出从图的邻接表表示转换成邻接矩阵表示的算法
/*
算法的基本思想:设图的顶点分别存储在数组v[n]中。首先初始化邻接矩阵。遍历邻接表,在依次遍历顶点v[i]的边链表时,修改邻接矩阵的第i行的元素值。若链表边结点的值为,则置arcs[i][j]=1。遍历完邻接表时,整个转换过程结束。此算法对于无向图、有向图均适用。
*/

void Convert(ALGraph &G,int arcs[M][N]{
    //此算法将邻接表方式表示的图G转换为邻接矩阵arcs
    for(i=0; i<n; i++){		//依次遍历各顶点表结点为头的边链表
    	p =(G->v[i]).firstarc;	//取出顶点i的第一条出边
    	while(p!=NULL){			//遍历边链表
            arcs[i][p->data]=1;
            p=p->nextarc;		//取下一条出边
	}
  }
}
6-2-试设计一个算法,判断一个无向图G是否为一棵树。若是一棵树,则算法返回true,否则返回false。
/*
一个无向图G是一棵树的条件是,G必须是无回路的连通图或有n-1条边的连通图。这里采用后者作为判断条件。对连通的判定,可用能否遍历全部顶点来实现。可以采用深度优先搜索算法在遍历图的过程中统计可能访问到的顶点个数和边的条数,若一次遍历就能访问到n个顶点和n-1条边,则可断定此图是一棵树。
*/

bool isTree(Graph& G){
for(i=1;i<=G.vexnum;i++)
	visited[i]=FALSE;		 //访问标记visited[]初始化
int Vnum=0,Enum=0;			 //记录顶点数和边数
DFS(G,1,Vnum,Enum,visited);
if(Vnum==G.vexnum && Enum==2*(G.vexnum-1))
	return true;			 //符合树的条件
else
	return false;			 //不符合树的条件
void DFS(Graph& G,int v,int& Vnum,int& Enum,int visited[]){
	//深度优先遍历图G,统计访问过的顶点数和边数,通过Vnum和Enum返回
	visited[v]=TRUE;
    Vnum++;					 //作访问标记,顶点计数
    int w=FirstNeighbor(G,v);//取v的第一个邻接顶点
	while(w !=-1){			 //当邻接顶点存在
        Enum++;				 //边存在,边计数
        if(!visited[w])  	//当该邻接顶点未访问过
        	DFS(G,w,Vnum,Enum,visited);
        w=NextNeighbor(G,v,w);
    }
}
6-3-写出图的深度优先搜索DFS算法的非递归算法(图采用邻接表形式)。
/*
在深度优先搜索的非递归算法中使用了一个栈s来记忆下一步可能访问的顶点,同时使用了一个访问标记数组visited[i]来记忆第i个顶点是否在栈内或曾经在栈内,若是则它以后不能再进栈。图采用邻接表形式。
注意:由于使用了栈,使得遍历的方式从右端到左端进行,不同于常规的从左端到右端,但仍然是深度优先遍历。
*/

void DFS_Non_RC(AGraph& G,int v){
    //从顶点v开始进行深度优先搜索,一次遍历一个连通分量的所有顶点
    int w;							// 顶点序号
	InitStack(S);					// 初始化栈s
	for(i=0; i<G.vexnum; i++)
    	visited[i]=FALSE;			//初始化visited
    Push(S,v); visited[v]=TRUE;     // v入栈并置Qisited[v]
    while(!IsEmpty(S)){
    	k=Pop(S);					// 栈中退出一个顶点
   		visit(k);					// 先饰问,再将其子结点入栈
    	for(w=FirstNeighbor(G,k); w>=0; w=NextNeighor(G,k,w))
            						// k所有邻接点
            if(!visited[w]){		// 未进过栈的顶点进栈
            	Push(S,w);
            	visited[w]=true;	// 作标记,以免再次入栈
            }//if
    }//while
}
6-4-分别采用基于深度优先遍历和广度优先遍历算法判别以邻接表方式存储的有向图中是否存在由顶点 vi 到顶点 vj 的路径 (i≠j) 。注意,算法中涉及的图的基本操作必须在此存储结构上实现
/*
两个不同的遍历算法都采用从顶点v,出发,依次遍历图中每个顶点,直到搜索到顶点vj,若能够搜索到vj,则说明存在由顶点vi到顶点j的路径。
*/

//深度优先遍历算法的实现如下
int visited[MAXSIZE]={0};			//访问标记数组
int Exist_Path_DFS(ALGraph G,int i,int j){
    
    int p;							//顶点序号
    if(i==j)
        return 1;					//i就是j
    else{
        visited[i]=1;				//置访问标记
        for (p=FirstNeighbor(G,i);p>=0;p=NextNeighbor(G,i,p)){
            k = p.adjvex;
            if(!visited[p] && Exist_Path_DFS(G,p,j))
                return 1;
           }//for
        }//else 
     return 0;
}
               

//广度优先遍历算法的实现如下
int visited[MAXSI2E]={0};		//访问标记数组
int Exist_Path_BFS(ALGraph G,int i,int j){
//广度优先判断有同图G中顶点vi到顶点vj是否有路径,是则返回1,否则返回0
    InitQueue(Q);
    EnQueue(Q,i);				//顶点i入队
    while(!isEmpty(Q)){			//非空循环
        DeQueue(Q,u);			//队头顶点出队
        visited[u]=1;			//置访问标记
    for(p=FirstNeighbor(G,i);p;p=NextNeighbor(G,i,p)){
                                //检查所有邻接点
            k=p.adjvex;
            if(k==j)			//若k==j,则查找成功
                return 1;
        if(!visited[k])			//否则,顶点k入队
            EnQueue(Q,k);
    	}//for
    }//while
    return 0;
}
6-5-假设图用邻接表表示,设计一个算法,输出从顶点 Vi 到顶点Vj 的所有简单路径。
/*
本题采用基于递归的深度优先遍历算法,从结点u出发,递归深度优先遍历图中结点,若访问到结点v,则输出该搜索路径上的结点。为此,设置一个path数组来存放路径上的结点(初始为空),d表示路径长度(初始为-1)。
*/

void FindPath(AGraph *G,int u,int v,int path[],int d){
    int w,i;
    ArcNode *p;
    d++;   				 	//路径长度增1
    path[d]=u;			 	//将当前顶点添加到路径中
    visited[u]=1; 		 	//置已访问标记
    if(u==V)				//找到一条路径则输出
    	print(path[]);		//输出路径上的结点
    p=G->adjlist[u].firstarc;//p指向v的第一个相邻点
    while(p!=NULL){
        w=p->adjvex;		//若顶点w未访问,递归访问它
    if(visited[w]==0)
    	FindPath(G,w,V,path,d);
    	p = p->nextarc;		//p指向v的下一个相邻点
    }
    visited[u] = 0;			//恢复环境,使该顶点可重新使用
}

第七章-查找

7-1-写出折半查找的递归算法。初始调用时,low为1,high为 ST.length
/*
算法的基本思想:根据查找的起始位置和终止位置,将查找序列一分为二,判断所查找的关键字在哪一部分,然后用新的序列的起始位置和终止位置递归求解。
算法把规模为n的复杂问题经过多次递归调用转化为规模减半的子问题求解。时间复杂度为O(log2n),算法中用到了一个递归工作栈,其规模与递归深度有关,也是O(log2n)。
*/
typedef struct{		//查找表的数据结构
    int *elem;		//存储空间基址,建表时按实际长度分配,0号留空
    int length;		//表的长度
}SSTable;

int BinSearchRec(SSTable ST,int key,int low,int high){
	//在有序表中递归折半查找其关键字为key的元素,返回其在表中序号
    if(low>high)
    	return 0;
    mid=(low+high)/2;		 	//取中间位置
    if(key>ST.elem[mid])		//向后半部分查找
    	Search(ST,key,mid+1,high);
    else if(key<ST.elem[mid])	//向前半部分查找
    	Search(ST,key,low,mid-1);
    else						//查找成功
   		return mid;
}
7-2-线性表中各结点的检索概率不等时,可用如下策略提高顺序检索的效率:若找到指定的结点,则将该结点和其前驱结点(若存在)交换,使得经常被检索的结点尽量位于表的前端。试设计在顺序结构和链式结构的线性表上实现上述策略的顺序检索算法。
/*
算法的基本思想:检索时可先从表头开始向后顺序扫描,若找到指定的结点,则将该结点和其前趋结点(若存在)交换。采用顺序表存储结构的算法实现如下:
*/

int SeqSrch(int R[],int k){
	//顺序查找线性表,找到后和其前面的元素交换
	int i=0;
	while((R[i].key!=k) &&(i<n))
        i++;						//从前向后顺序查找指定结点
		if(i<n && i>0){				//若找到,则交换
			temp=R[i];
            R[i]=R[i-1];
            R[i-1]=temp;
			return --i;				//交换成功,返回交换后的位置
		else 
            return -1;		  	   //交换失败
}	

第八章-排序

8-1-试重新编写考点精析中的快速排序的划分算法,使之每次选取的枢轴值都是随机地从当前子表中选择的。
/*
这类题目比较简单,为方便起见,可直接先随机地求出枢轴的下标,然后将枢轴值与A[low]交换,而后的思想就与前面的划分算法一样。
*/

int Partition2(int A[],int low,int high){
	int rand_Index=low+rand() %(high-low+1);
	Swap(A[rand Index],A[low]);//将枢轴值交换到第一个元素
	int pivot=A[low];			//置当前表中的第一个元素为枢轴值
	int i=low;					//使得表A[low…i]中的所有元素小于pivot,初始为空表
for(int j=low+1;j<=high;j++)//从第毫个元素开始寻找小于基准的元素
	if(A[j]<pivot)				//找到后,交换到前面
		swap(A[++i],A[j]);
	swap(A[i],A[low]);			//将基准元素插入到最终位置
	return i;					//返回基准元素的位置
}
8-2-试编写一个算法,使之能够在数组L[1…n]中找出第 k 小的元素(即从小到大排序后处于第k个位置的元素)
/*
本题最直接的做法是用排序算法对数组先进行从小到大的排序,然后直接提取L(k)便得到了第k小元素,但其平均时间复杂度将达O(nlog2n)以上。此外,还可采用小顶堆的方法,每次堆顶元素都是最小值元素,时间复杂度为O(n+klog2n)。下面介绍一个更精彩的算法,它基于快速排序的划分操作。
*/
int kth_elem(int a[],int low,int high,ing k)
{
    int pivot = a[low];
    int low temp=low;//由于下面会修改low与high,在递归时又要用到它们
    int high_temp=high;
    while(low<high){
    	while(low<high && a[high]>=pivot)
    		--high;
        a[low] =a[high];
        while(low<high && a[low]<=pivot)
            ++low;
        a[high]=a[low];
    }
    a[low]=pivot;
    //上面即为快速排序中的划分算法
    //以下就是本算法思想中所述的内容
    if(low==k)			//由于与k相同,直接返回pivot元素
    	return a[low];
    else if(low>k)		//在前一部分表中递归寻找
    	return kth_elem(a,low_temp,low-1,k);
    else				//在后一部分表中递归寻找
    	return kth_elem(a,low+1,high_temp,k-low);
}
8-3-已知由n(n>2)个正整数构成的集合A={ak l 0<=k<n},将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2,A1和A₂中的元素之和分别为S1和S₂。设计一个尽可能高效的划分算法,满足|n1-n2l最小且|S1-S2|最大。
/*
1)算法的基本设计思想
由题意知,将最小的L[n/2]个元素放在A1中,其余的元素放在A2中,分组结果即可满足题目要求。仿照快速排序的思想,基于枢轴将n个整数划分为两个子集。根据划分后枢轴所处的位置i分别处理:
① 若i = L[n/2],则分组完成,算法结束。
② 若i < L[n/2],则枢轴及之前的所有元素均属于A1,继续对i之后的元素进行划分。
③ 若i > L[n/2],则枢轴及之后的所有元素均属于A2,继续对i之前的元素进行划分。基于该设计思想实现的算法,无须对全部元素进行全排序。其平均时间复杂度是O(n),空间复杂度是O(1)。
*/

int setPartition(int a[],int n){
	int pivotkey,low=0,low0=0,high=n-1,high0=n-1,flag=1,k=n/2,i;
	int s1=0,s2=0;
	while(flag){
		piovtkey = a[low];				//选择枢轴
		while(low<high){				//基于枢轴对数据进行划分
		while(low<high && a[high]>=pivotkey)-high;
		if(low!=high) a[low]=a[high];
		while(low<high && a[low]<=pivotkey)
            ++low;
		if(low!=high)
            a[high]=a[low];
        }
		a[low]=pivotkey;
		if(low==k-1)//若枢轴是第n/2小元素,划分成功
			flag=0;
		else{	//是否继续划分
			if(low<k-1){
				low0 = ++low;
				high = high0;
           	}
            else{
                    high0 = --high;
                    low=low0;
                }
      	 }	
    }
        for(i=0;i<k;i++) s1 += a[i];
        for(i=k;i<n;i++) s2 += a[i];
		return s2-s1;
}
8-4-荷兰国旗问题:设有一个仅由红、白、蓝三种颜色的条块组成的条块序列,请编写一个时间复杂度为O(n)的算法,使得这些条块按红、白、蓝的顺序排好,即排成荷兰国旗图案。
/*
算法思想:顺序扫描线性表,将红色条块交换到线性表的最前面,蓝色条块交换到线性表的最后面。为此,设立三个指针,其中,j为工作指针,表示当前扫描的元素,i以前的元素全部为红色,k以后的元素全部为蓝色。根据所指示元素的颜色,决定将其交换到序列的前部或尾部。
初始时i=0,k=n-1,算法的实现如下。
*/

typedef enum(RED,WHITE,BLUE}	color;	//设置枚举数组
void Flag_Arrange(color a[],int n){
	int i=0,j=0,k=n-1;
    while(j<=k)
	switch(a[j]){				//判断条块的颜色
        case RED:Swap(a[i],a[j]);i++; j++; break;
		//红色,则和i交换
        case WHITE:j++;break;
        case BLUE:Swap(a[j],a[k]);	k--;
		//蓝色,则和k交换
		//这里没有j++语句以防止交换后a[j]仍为蓝色的情况
	}
}
8-5-编写双向冒泡排序算法,在正反两个方向交替进行扫描,即第一趟把关键字最大的元素放在序列的最后面,第二趟把关键字最小的元素放在序列的最前面,如此反复进行。
/*
这种排序方法又称双向起泡。奇数趟时,从前向后比较相邻元素的关键字,遇到逆序即交换,直到把序列中关键字最大的元素移动到序列尾部偶数趟时,从后往前比较相邻元素的关键字,遇到逆序即交换,直到把序列中关键字最小的元素移动到序列前端。程序代码如下:
*/

void BubbleSort(int A,int n){
//双向起泡排序,交替进行正反两个方向的起泡过程
    int low = 0,high = n-1;
    bool flag = true;//一趟冒泡后记录元素是否交换标志
    while(low<high && flag){//循环跳出条件,当flag为false 说明已没有逆序
    	flag=false;//每趟初始置flag为false
    for(i=low;i<high;i++)//从前向后起泡
    	if(a[i]>a[i+1]){	//发生逆序
    		swap(a[i],a[i+1]);//交换
    		flag=true;	//置flag
   		 }
    high--;				//更新上界
    for(i=high;i>low;i--)//从后往前起泡
        if(a[i]<a[i-1]){	//发生逆序
            swap(a[i],a[i-1]);	//交换
            flag=true;		   //置flag
        }
    low++;				   	   //修改下界
}
8-6-已知线性表按顺序存储,且每个元素都是不相同的整数型元素,设计把所有奇数移动到所有偶数前边的算法(要求时间最少,辅助空间最少)
/*
本题可采用基于快速排序的划分思想来设计算法,只需遍历一次即可,其时间复杂度为O(n),空间复杂度为O(1)。假设表为L[1..n],基本思想是:先从前向后找到一个偶数元素L(i),再从后向前找到一个奇数元素L(j),将二者交换;重复上述过程直到i大于。
*/

void move(int A[],int len){
	//对表A按奇偶进行一趟划分
	int i=0,j=len-1;//i表示左端偶数元素的下标;j表示右端奇数元素的下标
	while(i<j){
		while(i<j && A[i]%2!=0) i++;//从前向后找到一个偶数元素
		while(i<j && A[j]%2!=1)	j--;//从后向前找到一个奇数元素
		if(i<j){
			Swap(A[i],A[j]);//交换这两个元素.
			i++;
            j--;
        }
    }
}
8-7-编写一个算法,在基于单链表表示的待排序关键字序列上进行简单选择排序
/*
算法的思想是:每趟在原始链表中摘下关键字最大的结点,把它插入到结果链表的最前端。
由于在原始链表中摘下的关键字越来越小,在结果链表前端插入的关键字也越来越小,因此最后形成的结果链表中的结点将按关键字非递减的顺序有序链接。
假设单链表不带表头结点。
*/

void selectSort(LinkedList& L) {
	//对不带表头结点的单链表工执行简单选择排序
	LinkNode *h=L,*p,*q,*r,*s;
	L=NULL;
	while(h!=NULL){		//持续扫描原链表
		p = s = h;
        q = r = NULL;
		//指针s和r记忆最大结点和其前驱;p为工作指针,q为其前驱
		while(p !=NULL){	//扫描原链表寻找最大结点s
		if(p->data>s->data){ 
            s = p; 
            r = q;
        }					//找到更大的,记忆它和它的前驱
		q = p;
        p = p->1ink; 		//继续寻找
	}	
	if(s==h)
		h = h->1ink; 		//最大结点在原链表前端
	else
		r->1ink = s->1ink;	//最大结点在原链表表内
	s->1ink=L;
    L=s;					//结点s插入到结果链前端
    }
}
8-8-试设计一个算法,判断一个数据序列是否构成一个小根堆。
/*
将顺序表L[1..n]视为一个完全二叉树,扫描所有分支结点,遇到孩子结点的关键字小于根结点的关键字时返回false,扫描完后返回true。
*/

bool IsMinHeap(int A[]int len){
    if(len%2 == 0{			//len为偶数,有一个单分支结点
    	if(A[len/2]>A[len])			//判断单分支结点
    		return false;
    for(i=len/2-1; i>=1; i--)	//判断所有双分支结点
    	if(A[i]>A[2*i] || A[i]>A[2*i+1])
    		return false;
    else{						//1en为奇数时,没有单分支结点
    	for(i=len/2; i>=1; i--)		//判断所有双分支结点
    		if(A[i]>A[2*i] || A[i]>A[2*i+1])
    			return false;
    }
    return true;
}
8-9-设顺序表用数组A[]表示,表中元素存储在数组下标1~m+n的范围内,前m个元素递增有序,后n个元素递增有序,设计一个算法,使得整个顺序表有序。
/*
(1) 给出算法的基本设计思想
(2) 根据设计思想,采用C/C++描述算法,关键之处给出注释。
(3) 说明你所设计算法的时间复杂度与空间复杂度。
*/

/*
算法的基本设计思想如下:将数组A[1..m+n]视为一个已经过m趟插入排序的表,则从m+l趟开始,将后n个元素依次插入前面的有序表中。
时间复杂度由m和n共同决定,从上面的算法不难看出,在最坏情况下元素的比较次数为O(mn),而元素移动的次数为O(mn),所以时间复杂度为O(mn),由于算法只用到了常数个辅助空间,所以空间复杂度为O(1)。
*/

void Insert_Sort(int A[],int m,int n){
    int i,j;
    for(i = m+1;i<=m+n;i++{	//依次将A[m+1.m+n]插入有序表
        A[0]=A[i];					// 复制为哨兵
        for(j=i-1;A[j]>A[0];j--) 	// 从后往前插入
            A[j+1] = A[j];			// 元素后移
        A[j+1] = A[0];			    // 插入
    }
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值