数据结构算法题程序(二)

前言

  书接上文(数据结构新手,如有错误,欢迎指正)
  运行环境和基本程序可移步:郝斌数据结构-线性表之顺序表程序

题目

第四题

  从有序顺序表中删除其值在给定值s 与t之间(要求s<t)的所有元素,若s或t不合理或顺序表为空,则显示出错信息并退出运行。

我的版本:

/*有序表的元素是按照升序或降序排列好的*/
/*只需要找到需要删除的s和t所在位置*/
bool Del_s_t(struct arr *pArr,int s,int t){
	int i,j;
	int k;
	if(s>=t||is_empty(pArr)){
		return false;
	}                           //不合理的情况退出运行
	for(i=0;i<pArr->cnt;i++){
		if(pArr->pBase[i]<s){
			k++;                //记录满足条件的元素个数
		}else{
			break;
		}
	}
	for(j=pArr->cnt-1;j>0;j--){//cnt-1是最有一个元素的数组下标
		if(pArr->pBase[j]>t){
			k++;                //记录满足条件的元素个数
		}else{
			break;
		}
	}
	for(;j<pArr->cnt;i++,j++){
		pArr->pBase[i] = pArr->pBase[j+1];//j+1的目的是将等于t的元素删掉
	}
	pArr->cnt = k;              //将多余元素删除
	return true;
}

参考答案:

bool Del_s_t_two(struct arr *pArr,int s,int t){
	int i,j;
	if(s>=t||pArr->cnt==0){
		return false;
	}
	for(i=0;i<pArr->cnt&&pArr->pBase[i]<s;i++);//i直接停在小于s的第一个元素下标上
	if(i>=pArr->cnt){
		return false;//检错,例如出现数组最大元素5,删除区间在5-7的情况;
	}
	for(j=i;j<pArr->cnt&&pArr->pBase[j]<=t;j++);//j直接停在大于t的第一个元素下标上
	for(;j<pArr->cnt;i++,j++){
		pArr->pBase[i] = pArr->pBase[j];
	}
	pArr->cnt=i;//脑抽了!!!i就是数组长度,无需再用k计数了。
	return true;
}

第五题

  从顺序表中删除其值在给定值s与t之间(包含s和t,要求s<t)的所有元素,若s或t不合理或顺序表为空,则显示出错信息并退出运行。
基本思路:
  不再是有序表了,首先遍历数组的全部元素,找到满足小于s和大于t的元素。用k计数,将满足条件的元素存入k的连续下标数组内。k的值就是新数组的长度。

bool Del_s_t_upgrade(struct arr *pArr,int s,int t){
	int i,k=0;
	if(is_empty(pArr)||s>=t){
		return false;
	}
	for(i=0;i<pArr->cnt;i++){
		if(pArr->pBase[i]<s||pArr->pBase[i]>t){
			pArr->pBase[k] = pArr->pBase[i];
			k++;
		}
	}
	pArr->cnt = k;
	return true;
}

参考答案:

bool Del_s_t_upgrade(struct arr *pArr,int s,int t){
	int i,k=0;
	if(is_empty(pArr)||s>=t){
		return false;
	}
	for(i=0;i<pArr->cnt;i++){
		if(pArr->pBase[i]>=s||pArr->pBase[i]<=t){
			k++;//记录在s和t之间的元素
		}else{
		    pArr->pBase[i-k]=pArr->pBase[i];//将在s和t之外的元素按顺序排列
		}
	}
	pArr->cnt -= k;             //这样判断反而有点麻烦了
	return true;
}

第六题

  从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。

/*有序表中,值相同的元素一定在连续的位置*/
/*我的思路,将相邻的两个元素对比,不同则存入第一个元素*/
bool Del_same(struct arr *pArr){
	int i;
	int k=0;
	if(is_empty(pArr)){
		return false;
	}
	for(i=0;i<pArr->cnt-1;i++){
		if(pArr->pBase[i]!=pArr->pBase[i+1]){
			pArr->pBase[k]=pArr->pBase[i];
			k++;
		}
	}
	pArr->cnt=k;
	return true;
}

BUG:
缺少最后一个元素
参考答案:

bool Del_same(struct arr *pArr){
	int i,j;
	if(is_empty(pArr)){
		return false;
	}
	//将第一个元素作为被比较的对象
	for(i=0,j=1;j<pArr->cnt;j++){
		if(pArr->pBase[i]!=pArr->pBase[j]){//查找下一个和上一个元素值不同的元素
			pArr->pBase[++i]=pArr->pBase[j];//找到后将j元素覆盖i元素的后一个元素。
		}
	}
	pArr->cnt = i+1;//更新当前数组的有效计数。
	return true;
}

第七题

  将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。
我的思路:错得比较离谱。
参考答案:
思路:创建三个while循环体,从小到大排序,第一个循环体比较两个表中的数据大小,将小的一方存入array3中,同时对满足条件的有序表下标自增1,两个参与比较的有序表有一方遍历完就退出循环,第二第三个while循环用来查缺补漏,此时两个有序表中剩余的元素都是较大的,将剩余元素一一遍历存入array3即可。
关于后两个while循环的顺序问题,顺序不影响执行效果,因为从第一个while循环退出时,必然有一个有序表被遍历完了。
该算法的方法非常典型,需要牢固掌握!

bool Sum_A_B(struct arr *pArr1,struct arr *pArr2,struct arr *pArr3){
	int i=0,j=0;
	/*以下判断用来排出bug*/
	if(pArr1->cnt+pArr2->cnt>pArr3->len){
		return false;
	}
	while(i<pArr1->cnt&&j<pArr2->cnt){
		if(pArr1->pBase[i]<=pArr2->pBase[j]){
			append_arr(pArr3,pArr1->pBase[i++]);
		}
		else{
			append_arr(pArr3,pArr2->pBase[j++]);	
		}
		show_arr(pArr3);
	}
	while(i<pArr1->cnt){
		append_arr(pArr3,pArr1->pBase[i++]);
		show_arr(pArr3);
	}
	while(j<pArr2->cnt){
		append_arr(pArr3,pArr2->pBase[j++]);
		show_arr(pArr3);
	}
	return true;
}

算法执行过程:

/*主函数测试程序*/
int main(void){
	struct arr array1;
	struct arr array2;
	struct arr array3;
	int i,j=0;
	int min=0;
	init_arr(&array1,20);
	init_arr(&array2,20);
	init_arr(&array3,40);
	for(i=0;i<1;i++){
		for(j=1;j<=3;j++){
			append_arr(&array1,j);
			append_arr(&array1,j);
		}
	}
	for(i=0;i<1;i++){
		for(j=4;j<=7;j++){
			append_arr(&array2,j);
			append_arr(&array2,j);
		}
	}
	show_arr(&array1);
	show_arr(&array2);
	Sum_A_B(&array1,&array2,&array3);
	show_arr(&array3);
}

第八题

  已知在一维数组A[m+n]中依次存放两个线性表(a1,a2, a3,…, am)和(b1, b2, b3,…, bn,)。编写一个函数,将数组中两个顺序表的位置互换,即将(b1, b2, b3…, bn,)放在(a1, a2,a3,…,am)的前面。
我的思路:完全没思路。。
参考答案:

//left和right可以对一个顺序表内的任意切片片段进行排序
//left指定位置;right-left指定长度
void Reverse(struct arr *pArr,int left,int right){
	int i;
	int temp=0;
	int mid = (left+right)/2;//一般的逆序输出法的中值,取前半元素和后半元素对调
	//这里mid增强了函数的适用性,可对不同区域的数据切片进行逆序。
	//排除bug
	if(left>=right||right>pArr->cnt){
		return;
	}
	for(i=0;i<=mid-left;i++){
		temp = pArr->pBase[left+i];
		pArr->pBase[left+i]=pArr->pBase[right-i];
		pArr->pBase[right-i]=temp;
		show_arr(pArr);
	}
}
void Exchange(struct arr *pArr,int m,int n){
	Reverse(pArr,0,m+n-1);//将全部数据原地逆序输出,第二个(n)逆序为第一个顺序表,第一个(m)逆序为第二个顺序表
	Reverse(pArr,0,n-1);//前n个数据再逆序一次,
	Reverse(pArr,n,m+n-1);//后m个数据再逆序一次
}

测试程序:

int main(void){
	int i,j;
	struct arr array;
	init_arr(&array,20);
	for(i=0;i<6;i++){
	append_arr(&array,i);	
	}
	for(j=8;j<20;j++){
	append_arr(&array,j);	
	}
	show_arr(&array);
	printf("\n");
	Exchange(&array,6,12);//满足left小于right
	show_arr(&array);
}

输出结果:
在这里插入图片描述

第九题

  线性表(a1, a2, a3,.…,an,)中的元素递增有序且按顺序存储于计算机内。要求设计一个算法,完成用最少时间在表中查找数值为x的元素,若找到,则将其与后继元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。

什么是二分法查找(折半查找)?
参考博文:【查找】折半查找
  在有序数组中查找某一特定的元素,其基本思想是减少查找次数,每次比较后折半查找区间。这种方法要求被查表必须是有序顺序表。相比顺序查找法所用时间更短。
对于一个有序递增的序列:
        (1,2,3,4,5,6,7,8,9)
如果要查找元素8,采用顺序查找法要比较8次,采用二分查找法可以设置三个指针,分别是low指向第一个元素1,mid指向中间元素5,high指向最后一个元素9。
首先,比较mid和8的大小关系,mid<8,说明查找对象在[mid,high]区间内;然后更新三个指针,low指向mid+1=6,mid指向low+(high-low)/2=7,high不变;
        (1,2,3,4,5,6,7,8,9)
mid仍然<8,再取low=mid+1=8,mid=low+(high-low)/2=8,high不变;
mid=8,查找结束。
参考答案:

void SearchExchangeInsert(struct arr *pArr,int x){
	int i,t;
	int low=0,high=pArr->cnt-1,mid;//low和high分别指向顺序表的下界和上界
	while(low<=high){
		mid=(low+high)/2;//取中间位置
		if(pArr->pBase[mid]==x)break;//找到x,退出while循环
		else if(pArr->pBase[mid]<x)low=mid+1;//否则去[mid+1,high]区间查找
		else high = mid-1;	//再否则去[low,mid-1]查找	
	}
	//两个if判断只可能执行一个。
	if(pArr->pBase[mid]==x&&mid!=pArr->cnt-1){//查找的x不是最后一个元素
		t = pArr->pBase[mid];
		pArr->pBase[mid]=pArr->pBase[mid+1];
		pArr->pBase[mid+1] = t;//将x和后继元素交换
	}
	//当查找元素不在顺序表内
	if(low>high){
		for(i=pArr->cnt-1;i>high;i--){
			pArr->pBase[i+1]=pArr->pBase[i];
		}
		pArr->pBase[i+1]=x;
		pArr->cnt+=1;//插入元素后,有效计数+1,不然不显示最后一个元素
	}
}

测试程序:

int main(void){
	int i,j;
	struct arr array;
	init_arr(&array,20);
	for(i=0;i<13;i++){
	append_arr(&array,i);	
	}
	append_arr(&array,16);
	show_arr(&array);
	SearchExchangeInsert(&array,14);
	show_arr(&array);
	SearchExchangeInsert(&array,6);
	show_arr(&array);
}

运行结果

第十题

  【2010统考真题】设将n (n>1)个整数存放到一维数组R中。设计一个在时间和空间两方面都尽可能高效的算法。将R中保存的序列循环左移p ( 0<p<n)个位置,即将R中的数据由(X0,X1,…,Xn-1)变换为(Xp,Xp+t,…,Xn-1,X0,X1,…,Xp-1)。要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度和空间复杂度。
(1)算法思想:
  可将这个问题视为把数组ab转换成数组ba(a代表数组的前p个元素,b代表数组中余下的n-p个元素),先将a逆置得到(a-1)b,再将b逆置得到(a-1)(b-1),最后将整个(a-1)(b-1)逆置得到(a-1b-1)-1=ba.设reverse函数执行将数组元素逆置的操作,对abcdefgh向左循环移动3(p=3)个位置的过程如下:
  reverse(0,p-1)得到cbadefgh;
  reverse(p,n-1)得到cbahgfed;
  reverse(0,n-1)得到defghabc;
其中reserve函数的两个元素分别代表数组中待转换元素的始末位置。
(2)C语言算法描述:

void reserve(struct arr *pArr,int from,int to){
	int i,temp;
	for(i=0;i<(to-from+1)/2;i++){
		temp=pArr->pBase[from+i];
		pArr->pBase[from+i]=pArr->pBase[to-i];
		pArr->pBase[to-i]=temp;		
	}
}
/*---------------------------
参数n是整个线性表的长度;
参数p是左移个数
----------------------------*/
void Converse(struct arr *pArr,int n,int p){
	reserve(pArr,0,p-1);
	reserve(pArr,p,n-1);
	reserve(pArr,0,n-1);
}
/*这个函数实现的功能和第八题一致*/
void Converse(struct arr *pArr,int n,int p){
	reserve(pArr,0,n-1);
	reserve(pArr,n,n+p-1);
	reserve(pArr,0,n+p-1);
}

(3)以上三个reserve函数的时间复杂度分别为O(p/2)、O((n-p)/2)和O(n/2),故设计的算法时间复杂度为O(n),空间复杂度O(1).

测试程序:

int main(void){
	int i,j;
	struct arr array;
	init_arr(&array,20);
	for(i=0;i<18;i++){
	append_arr(&array,i);	
	}
	show_arr(&array);
	printf("\n");
	Converse(&array,18,6);//顺序表中一共18个元素,将前6个元素整体左移6次。
	show_arr(&array);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个文件里包含了数据结构的所有的程序的实现,都是本人亲自写出来并在vs下调通了,绝对没有问,绝对可以通过老师的检查。这些程序都是本人在数据结构的课程设计的时候写的,主要的内容是 1、顺序的建立插入删除查找 链的建立插入删除查找 (包括顺序实现和链式实现) 2、栈的建立插入删除查找 队列的建立插入删除查找 (包括顺序实现和链式实现) 3、串——改进后的KMP 4、稀疏矩阵——三元组转置和乘法 5、叉树遍历、哈夫曼树的建立 6、图的遍历、连通分量和强连通分量、关节点、最小生成树、拓扑排序、关键路径(prim和kruscal算法)、最短路径(有dijstra和floyd算法) 7、折半查找、排序树、平衡叉树、散列函数 8、插入排序、折半插入、冒泡排序、快速排序、简单选择、堆排序路归并排序 另外还有一些测试数据在里面,大家可以试试。 数据结构真的是很重要很重要,这么课要是没有学好,那就不算是计算机系的学生,而且数据结构很重要的就是要理解好那些经典的算法,在以后我们的实践中你会发现,基本上的问都可以归结为那些经典问,纳闷只要我们掌握了那些经典问的经典算法,融会贯通的应用,拿在什么情况下都是游刃有余的。 希望对大家有所帮助,并且希望能够大家能支持支持。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值