数据结构C语言综合应用题
引用型参数和指针的区别:
这里之所以使用引用传参的方式,是因为这样可以有多个返回值(用地址引用去开辟一片新的内存-然后不管函数怎么给value赋值,也只是改变指针的指向地址,并不会改变原来的数据,通过引用变量去获取两个返回值(一个是隐式的),这是一种很好的编程方法),而使用函数返回值的话只能有一个返回值
#include
#include
//C语言综合应用题
bool delMin(sqlList &L,Elemtype &value){
//删除顺序表L中最小值元素点,并通过引用型参数value返回其值
//若删除成功,则返回true; 否则返回false
if(L.length==0){
return false;
}
value=L.data[0];
int pos=0;
for(int 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; //此时的value即为最小值
}
扫描顺序表的前半部分元素。对于元素L.data[i] (0<=i 始终只是多开辟了一个 temp的 空间 解法1是典型的错误算法–会强行增加时间复杂度,因为如果这样做,后面所有元素都需要前移,正确的还得看解法2 解法2:用K记录顺序表L中不等于x的元素个数(即需要保存的元素个数),边扫描边统计k,并将不等于x的元素向前移动k个位置,最后修改L的长度。 解法1(按照上题的思路) 解法2–根据有序表的特性,要删除的元素必须是相连的整体。 所以我们只需要先寻找值大于等于s的第一个元素(第一个删除的元素),然后寻找值大于t的第一个元素(最后一个删除的元素的下一个元素),要将这段元素删除,只需直接将后面的元素前移。 解法1就是我们上面一题的解法1,这个解法可以说是万能解法 解法2–从前向后扫描顺序表L,用k记录下元素值在s到t之间元素的个数(初始时k=0). 对于当前扫描的元素,若其值不在s到t之间,则前移k个位置;否则执行k++。由于这样每个不在s到t之间的元素仅移动一次,所以算法效率高。 本题代码如下: 算法思路2–注意是有序顺序表,值相同的元素一定在连续的位置上,用类似于直接插入排序的思想,初始时将第一个元素视为非重复的有序表。之后依次判读后面的元素是否与前面非重复有序表的最后一个元素相同,若相同则继续向后判断,若不同则插入道前面的非重复有序表的最后,直到判断道表尾为止。–更好的办法(用指针) 若不是有序顺序表,而是无序的表,可以用散列表(哈希表)实现,可以直接将数据元素(也就是值)作为关键字(也就是key),先遍历顺序表,将其放在散列表中,然后自动删除重复的key只保留一个即可。因为hash表是元素各异的,不能有重复的,唯一对应的。 –算法思路1:因为两个都是有序的,所以只需要进行一次首尾相连,但是需要注意的是大小的问题,来决定连接的方式,还需要判断每一个顺序表是从小到大排列的还是从大到小排列的 如果不知道有序顺序表的顺序是从大到小还是从小到大,就不是一个函数能够完成的,需要加判断函数来判断是正常工作还是需要用到顺序表的inverse函数才可以 --算法思路: 先将数组A[m+n]中的全部元素(a1,a2,a3,…,am.b1,b2,b3,…,bn)原地逆置为(bn,bn-1,bn-2,…, b1,am,am-1,am-2,—,a1), 再对前n个元素和后m个元素分别使用逆置算法,即可得到(b1,b2,b3,—,bn.a1,a2,a3,a3,…,am),从而实现顺序表的位置互换. –算法思路:首选既然元素是递增有序的,最快的方法肯定是二分查找,如果找不到的话也可以将其插入到两个相邻构成区间的中间。 解法1(使用递归法,但是好像这种只适用于肯定能找到x,应该也可以改造成可以找到,但是我懒) 解法2**(常用)(**不使用递归,但可以通过多重判断解决此题) 形象化的流程可以理解为: 算法的时间复杂度为O(n),空间复杂度为O(1); –也是一种较为常见的经典算法 标准答案的算法设计思想– 分别求两个升序序列A、B的中位数,设为a和b,求序列A、B的中位数的过程如下: 空间复杂度为O(1); 时间复杂度在最坏的情况下推导如下: 算法可分为以下两步: 时间复杂度为O(n+n)=O(n),空间复杂度为O(1);#include
3. 对长度为n的顺序表L,编写一个时间复杂度为O(n)、空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素。
int count=0; //可以作为全局变量
bool DelAll(Sqlist &L,Elemyype x){
//记录有多少个值为x的数据元素
if(L.length==0){
print("该表为空!!")
return false;
}
for(i=0;i<L.length;i++){
if(x==L.data[i]){
L.data[i]=L.data[i+1];
L.length--;
count++;
}
}
return true;
}
count=0; //清零
bool del_x_1(Sqlist &L,Elemtype x){
//本算法实现删除顺序表L中所有值为x的数据元素
int k=0; //记录值不等于x的元素个数
for(i=0;i<L.length;i++){
if(L.data[i]!=k){
L.data[k]=L.data[i];
k++;
}
}
L.length=k; //顺序表L的长度等于k
}
4. 从有序顺序表中删除其值在给定值s与t之间(s
bool del_spe(Sqlist &L,Elemtype s,Elemtype t){
if(L.length==0 || s>=t){
return false;
}
int k=0; //记录所有不在这个元素的元素
for(i=0;i++;i<L.length){
if(i<=S || i>=t){
L.data[k]=L.data[i];
k++;
}
}
L.length=k;
}
bool Del_s_t2(SqList &L,Elemtype s,Elemtype t){
int i,j;//记录 起始值和结束值
if(s>=t || L.length==0){
return false;
}
for(i=0;i<L.length&&L.data[i]<s;i++);{
//寻找值大于等于s的第一个元素
if(i>=L.length)
return false; //安全性检测
}
for(j=i;j<L.length&&L.data[j]<=t;j++); //寻找值大于t的第一个元素
for(;j<L.length;i++,j++) //i从当前位置(也就是小于等于s的最后一个元素)向后增加
L.data[i]=L.data[j]; //前移填补位置
L.length=i;
return true;
}
5. 从顺序表中删除其值为给定值s与t之间(要求s < t)的所有元素,如果s或t不合理或顺序表为空,则显示出错信息并退出运行
bool del_spe(Sqlist &L,Elemtype s,Elemtype t){
if(L.length==0 || s>=t){
return false;
}
int k=0; //记录所有不在这个元素的元素
for(i=0;i++;i<L.length){
if(i<=S || i>=t){
L.data[k]=L.data[i];
k++;
}
}
L.length=k;
}
bool Del_s_t(SqList &L,Elemtype s,Elemtype t){
//删除顺序表L中值在给定值s与t之间(要求s
6. (重点)从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。 算法思路1–直接使用while循环或者if语句的方式检测重复元素的个数,用计数器记录重复元素的个数,监测完后;
bool Del_same(Sqllist &L){
//删除顺序表中相同的元素,使其中数据元素保持互异性
if(L.length==0)
return false;
int count=0; //记录总的相同的元素的数量
int i=0;
int k=0; //临时计数相同的元素
for(;i<L.length;i++){
while(L.data[i]==L.data[i+1]){
k++;
count++;
i=i+1;
}
L.data[i+1]=L.data[length-k];
k=0;
}
L.length-=count;
return true;
}
bool Delete_Same(Sqlist &L){
if(L.length==0)
return false; //安全性检测
int i,j; //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
}
L.length=i+1;
return true;
}
7. (重点) 将两个有序(都是从小到大)顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表
bool Merge(Sqlist A,sqlist B,Sqlist &C){
//将有序顺序表A和表合并成一个新的有序顺序表C 新的有序顺序表是从小到大的
if(A.length+B.length>C.maxSize) //安全性检测
return 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;
}
8.(重点)已知在一维数组A[m+n]中依次存放两个线性表(a1,a2,a3,…,am)和(b1,b2,b3,—,bn).试编写一个函数,对数组中两个顺序表的位置互换,即将(b1,b2,b3,…,bn)放在(a1,a2,a3,…am)的前面
typedef int DataType;
void Reverse(DataType A[],int left,int right,int arraySize){
//逆转(aleft,aleft+1,aleft+2...,aright)为(aright,aright-1,...,aleft)
if(left>=right || right >= arraySize)
return;
int mid=(left+right)/2;
for(int i=0;i<mid-left;i++){
Datatype temp=A[left+i];
A[left+i]=A[right-i];
A[right-i]=temp;
}
}
void Exchange(DataType A[],int m,int n,int arraySize){
/* 数组A[m+n]中,从0到m-1存放顺序表(a1,a2,a3,---,am), 从m到m+n-1存放顺序表
(b1,b2,b3,---,bn) */
Reverse(A,0,m+n-1,arraySize);
Reverse(A,0,n-1,arraySize);
Reverse(A,n,m+n-1,arraySize);
}
9.线性表(a1,a2,an)中的元素递增有序且按顺序存储于计算机内,要求设计一算法,完成用最少时间在表中查找数值为x的元素,若找到则将其与后继元素位置相交换,若找不到则将其插入表中并使表中元素仍递增有序。
typedef int DataType;
int left=0;
int right=L.length;
DataType find_binary(sqlist &L,int left,int right,DataType x){
int mid=left+right/2;
int temp=0;
while(x!=L.data[mid]){
if(x>L.data[mid]){
find_binary((sqlist &L),mid,L.length-1,x);
}
else
find_binary((sqlist &L),0,mid-1,x);
}
temp=L.data[mid+1];
L.data[mid+1]=L.data[mid];
L.data[mid]=temp;
return mid;
}
void SearchExchangeInsert(ElemType A[],ElemType x){
int low=0,high=n-1,mid; //low和gigh指向顺序表下界和上界的下标
while(low<=high){
mid=(low+high)/2; //找中间位置
if(A[mid]==x) break; //找到x,退出while循环
else if(A[mid]<x) low=mid+1; //到中点mid的右半部去查
else high=mid-1; //到中点mid的左半部去查
} //下面两个if语句只会执行一个
if(A[mid]==x&&mid!=n-1){
//若最后一个元素与x相等,则不存在其与后继交换
t=A[mid]; A[mid]=A[mid+1]; A[mid+1]=t; //的操作
}
if(low>high){
//查找失败,插入数据元素x
for(i=n-1;i>=high;i--) A[i+1]=A[i];//后移元素
A[high]=x; //插入x
}
}
Reverse()函数的两个参数需要表示数组中待转换元素的始末位置void Reverse(int R[],int from,int to){
int i,temp;
for(i=0;i<(to-from+1)/2;i++){
temp=R[from+i];
R[from+i]=R[to-i];
R[to-i]=temp;
}
}
void converse(int R[],int n,int p){
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1);
}
算法思想2:–补充-- 可以借助辅助数组来实现。创建大小为p的辅助数组S,将R中前p分整数依次暂存在S中,同时将R中后n-p个整数左移,然后将S中暂存的p个数依次放回到R中的后续单元。时间复杂度为O(n),空间复杂度为O§;
1. 算法的基本设计思想–这个算法实现的关键是要合并出把两个升序的顺序表合成一个升序的顺序表,可以采用工作指针–引用变量i,j分别遍历两个顺序表,并进行逐步比较,当一个顺序表被遍历完后,直接将另一个顺序表未遍历完的元素加到末尾即可。但这种方式至少都要遍历n次,算法的时间复杂度为O(n);
int M_search(int A[],int B[],int n){
int s1=0,d1=n-1,m1=0,s2=0,d2=n-1,m2=0;
//分别表示序列A和B的首位数、末位数和中位数
while(s1!=d1||s2!=d2){
m1=(s1+d1)/2;
m2=(s2+d2)/2;
if(A[m1]==B[m2]) //满足条件1
return A[m1];
if(A[m1]<A[m2]){
//满足条件2
if((s1+d1)%2==0){
//若元素为奇数
s1=m1; //舍弃A中间点以前的部分且保留中间点
d2=m2; //舍弃B中间点以后的部分且保留中间点
}
else{
s1=m1+1; //舍弃A中间点及A中间点以前的部分
d2=m2; //舍弃B中间点以后的你部分且保留中间点
}
}
else{
//满足条件3
if((s2+d2)%2==0){
//若元素个数为奇数
d1=m1;
s2=m2;
}
else{
//若元素个数为偶数
d1=m1;
s2=m2+1;
}
}
}
return A[s1]<B[s2]?A[s1]:B[s2];
}
算法的基本设计思想–从前往后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数,确认Num是否是主元素。这个算法的核心就是找到数组中出现次数最多的元素,然后再去判断其是否是主元素即可。
int Majority(int A[],int n){
int i,c,count=1; //c用来保存候选主元素,count用来计数
c=A[0]; //设置A[0]为候选主元素
}
for(i=1;i<n;i++) //查找候选主元素
if(A[i]==c)
count++; //对A中的候选主元素计数
else
if(count>0) //处理不是候选主元素的情况
count--;
else{
//更换候选主元素,重新计数
c=A[i];
count=1;
}
if(count>0)
for(i=count=0;i<n;i++) //统计候选主元素的实际出现次数
if(A[i]==c)
count++;
if(count>n/2) return c; //确认候选主元素
else return -1; //不存在主元素
}
本题如果采用先排好序再统计的方法[时间复杂度可为O(nlog2n)],只要解答正确,最高可拿11分。即便是写出O(n2)的算法,最高也能拿10分,因此对于统考算法题,花费大量时间去思考最优解法是得不偿失的。
算法设计思想1–因为只需要在时间上尽可能高效,所以采用用空间换时间的方法,也就是设计一个辅助数组B[n],分别对应正整数1到n,值得注意的事,当数组A中出现了小于等于0或者大于n的值时,会导致1-n中出现空余位置,返回结果必然为1-n中,因此对于A中出现了小于等于0或大于n的值可以不采取任何操作。