数据结构题集-第二章-线性表-有序未去重顺序表的交集

news/2024/11/16 19:51:22/

有序未去重顺序表的交集

  • 说明
  • 2.27 假设两个元素依值递增有序排列的线性表A和B
    • 解1
    • 解2

说明

  • 本文参照严蔚敏《数据结构(C语言版)题集》一书中包含的问答题和算法设计题目,提供解答和算法的解决方案。
  • 请读者在自己已经解决了某个题目或进行了充分的思考之后,再参考本解答,以保证复习效果。
  • 由于作者水平所限,本解答中一定存在不少这样或者那样的错误和不足,希望读者们在阅读中多动脑、勤思考,争取发现和纠正这些错误,写出更好的算法来。

2.27 假设两个元素依值递增有序排列的线性表A和B

分别表示两个集合,
且同一表中可能存在值相同的元素,
现要求构成一个线性表C,
利用A表空间存放表C,
其元素为A和B中元素的交集,
且表C中的元素也依值递增有序排列,
要求新生成的表C中的元素值各不相同。
试对线性表编写求C的算法

解1

不应该先删除表A和表B中多余的值相同的元素,
应采用“下标平行移动,一次扫描完成”的策略,
只要进一步考虑,和表B中结点值相同的表A的结点值,
是否和表C中当前最后一个结点的值相同,
如果算法成功,是不需要对表C中的元素执行去重操作的:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10#define MAX_TEST_LENGTH 8
#define MAX_TEST_ELEM 5
typedef int ElemType;
typedef struct{ElemType *elem; // 存储空间基址int length; // 当前长度int listsize; // 当前分配的存储容量
} SqList; // 顺序表类型
typedef struct LNode{ElemType data;struct LNode *next;
} LNode, *LinkList; // 线性链表类型Status InitList_Sq(SqList *pL){// 构造一个空的线性表(*pL).elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));if(!(*pL).elem) exit(OVERFLOW); // 存储分配失败(*pL).length = 0; // 空表长度为0(*pL).listsize = LIST_INIT_SIZE; // 初始存储容量return OK;
}// InitList_SqStatus ListInsert_Sq(SqList *pL, int i, ElemType e){// 在顺序线性表L中第i个位置之前插入新的元素e// i的合法值范围:[1,ListLength_Sq(L)+1]if(i<1 || i>((*pL).length+1)) return ERROR; // i值不合法if((*pL).length>=(*pL).listsize){ // 当前存储空间已满,增加分配ElemType *newbase = (ElemType *)realloc((*pL).elem,((*pL).listsize+LISTINCREMENT)*sizeof(ElemType));if(!newbase) exit(OVERFLOW); // 存储分配失败(*pL).elem = newbase; // 新基址(*pL).listsize += LISTINCREMENT; // 增加存储容量}ElemType *p = NULL;ElemType *q = &((*pL).elem[i-1]); // q为插入位置for(p=&((*pL).elem[(*pL).length-1]);p>=q;--p) *(p+1) = *p; // 插入位置及之后的元素右移*q = e; // 插入e++((*pL).length); // 表长增1return OK;
}// ListInsert_SqStatus FreeList_Sq(SqList *pL){// 释放线性表if(NULL!=(*pL).elem){free((*pL).elem);return OK;}else{return ERROR;}
}// FreeList_Sqint InsertOrderSqList(SqList *pL,int x){//把x插入递增有序表pL中,并返回插入的位置(数组下标)int i;if((*pL).length+1>(*pL).listsize) return OVERFLOW;(*pL).length++;for(i=(*pL).length-1;(*pL).elem[i]>x&&i>=0;i--)(*pL).elem[i+1]=(*pL).elem[i];(*pL).elem[i+1]=x;return i+1;
}//InsertOrderSqList int cmp(const void *x,const void *y){// qsort函数需要调用的比较函数ElemType *a=(ElemType *)x; ElemType *b=(ElemType *)y;return (*a-*b);
}void display_list(SqList L){int i;for(i=1;i<=L.length;i++){printf("[%d]=%d\t",i-1,L.elem[i-1]);if(i%7==0) putchar('\n');}
}void intersect_sqlist(SqList *pA,SqList B,SqList **ppC){//未去重且元素递增排列的线性表A和B中元素交集合并存入无重复元素的C中,//C利用A的空间存储,且仍保持有序//因为线性表是顺序存储的,而为了利用A空间,C又不能拷贝A中元素//所以,C只能作为A的一个别名,传入的pC必须为SqList类型的空指针ElemType b_cur_same_v;int i,j,k,d,c_len,a_next_n_c,b_cur_idx,b_next_diff_idx;i=j=k=c_len=b_cur_idx=a_next_n_c=0;while(i<pA->length&&b_cur_idx<=B.length){//两表探索的边界有差异//首先每次大循环需要对A进行前期去重k=i;for(j=i+1;(j<pA->length)&&(pA->elem[j]==pA->elem[i]);j++);if(j>i+1){d=j-i-1; //d只能在这个局部使用while(j<pA->length) pA->elem[++k]=pA->elem[j++];pA->length -= d;}if(i==0){b_cur_same_v=B.elem[b_cur_idx];b_next_diff_idx=b_cur_idx+1;while(b_next_diff_idx<B.length&&B.elem[b_next_diff_idx]==b_cur_same_v) b_next_diff_idx++;b_cur_idx=b_next_diff_idx;}while(b_cur_idx<B.length&&b_cur_same_v<pA->elem[i]){b_cur_same_v=B.elem[b_cur_idx];b_next_diff_idx=b_cur_idx+1;while(b_next_diff_idx<B.length&&B.elem[b_next_diff_idx]==b_cur_same_v) b_next_diff_idx++;b_cur_idx=b_next_diff_idx;}	if(b_cur_same_v==pA->elem[i]){for(j=i;j<pA->length;j++) pA->elem[j-a_next_n_c]=pA->elem[j];pA->length-=a_next_n_c;i=i-a_next_n_c+1; //下标回退并且加1a_next_n_c=0;c_len++;}else{a_next_n_c++;i++;}}pA->length=c_len;(*ppC)=pA;
}void sorted_list_to_set(SqList *pL){//在顺序线性表中去除重复元素,成为集合int i,j,k,d;for(i=0;i<pL->length;i++){k=i;for(j=i+1;(j<pL->length)&&(pL->elem[j]==pL->elem[i]);j++);if(j>i+1){d=j-i-1;while(j<pL->length){pL->elem[++k]=pL->elem[j++];}pL->length -= d;}}
}Status create_sorted_sqlists(SqList *pA, SqList *pB){int i;//int A[22]={1,4,5,5,5,5,5,5,5,5,5,10,10,10,10,10,11,11,16,18,18,18};//int B[28]={0,0,0,0,0,5,5,5,5,5,5,9,9,9,9,9,9,9,11,11,11,11,11,11,17,17,17,17};//A先超限表示已遍历完,B先超限还有一个没考察完//int A[4]={13,13,13,13};//int B[10]={4,4,7,11,11,11,13,13,13,13};//int A[10]={4,4,7,11,11,11,13,13,13,13};//int B[4]={13,13,13,13};int A[10]={4,4,4,4,5,5,5,5,5,5};int B[6]={5,10,10,13,17,17};for(i=0;i<10;i++){pA->elem[i] = A[i];}pA->length=10;printf("\nA:\n");display_list(*pA);for(i=0;i<6;i++){pB->elem[i] = B[i];}pB->length=6;printf("\nB:\n");display_list(*pB);return OK;
}Status rand_create_sorted_sqlists(SqList *pA, SqList *pB){int dupli_rand_count,dupli_rand_num;time_t t;int count;srand((unsigned)time(&t)); //初始化随机数发生器count = rand()%MAX_TEST_LENGTH;while(count--){dupli_rand_count=rand()%10;dupli_rand_num=rand()%MAX_TEST_ELEM;while(dupli_rand_count--){if(OK!=ListInsert_Sq(pA,1+rand()%((*pA).length+1),dupli_rand_num))return ERROR; // 随机找一个合法位置插入新随机元素}}qsort((*pA).elem,(*pA).length,sizeof(ElemType),cmp);printf("\nA:\n");display_list(*pA);count = rand()%MAX_TEST_LENGTH;while(count--){dupli_rand_count=rand()%10;dupli_rand_num=rand()%MAX_TEST_ELEM;while(dupli_rand_count--){if(OK!=ListInsert_Sq(pB,1+rand()%((*pB).length+1),dupli_rand_num))return ERROR; // 随机找一个合法位置插入新随机元素}}qsort((*pB).elem,(*pB).length,sizeof(ElemType),cmp);printf("\nB:\n");display_list(*pB);return OK;
}int main(){SqList La,Lb;SqList *pC; //由于题目指出需要利用A空间存结果,所以直接让指针指向,相对于La不需初始化和销毁if(OK!=InitList_Sq(&La)) return ERROR;if(OK!=InitList_Sq(&Lb)) return ERROR;//if(OK!=create_sorted_sqlists(&La,&Lb)) return ERROR;if(OK!=rand_create_sorted_sqlists(&La,&Lb)) return ERROR;intersect_sqlist(&La,Lb,&pC);printf("\nC:\n");display_list(*pC); //pC作为一个别名,只有这么一点用if(OK==FreeList_Sq(&La))printf("\nFree SqList A success!\n");if(OK==FreeList_Sq(&Lb))printf("\nFree SqList B success!\n");return 0;
}

算法粗略思想如下:
首先有去重地定位A中最新元素
此后查找B中元素
若B下标对应当前值和A最新去重值不同
假设B的现阶段值是连续重复的
还要记录B当前值后在B中往后继续比较寻找到下一个不同值的下标
如果B的这些可能重复且还没有移动到下一个的值比A最新去重值小
那就让B的最新下标增加到其下一个值的最初位置
此时A的下标可以不变
B的每次下标移动都需记录对应新初值且寻找下一个不同的
相当于B有一个值记录和一个不同值的新下标记录
直到B的移动到其值记录大于等于A的最新去重值如果是大于即不相等则B后面更大且不等
得知A的最新去重值不属于交集
此时本应该立即将A中后缀全移动一个位置覆盖前面成为新表
其实不然
可以让A的前期去重下标移动不仅记录目前首个已确定非交集元素下标
还需继续往后重复以上步骤直到发现新的元素相等的情况
这样才能以最新相等的交集元素为首
将其后缀全部替代非交集的那一部分
这个替代过程的关键是只能将A的用来记录交集个数的最新表长加一
也就是不管后面还有什么整个搬动替代只能让交集个数加一
由于pA的原表长用来同步去重和遍历
还需要增加一个变量专门记录pC将会得到的交集个数以结束之后赋予它所指向的表A
为了验证这个算法的缺陷,经历了足够但还不充分的测试

解2

其实该算法并没有上面想象的那么复杂:

void sqlist_intersect(SqList *pA,SqList B,SqList **ppC){//求元素递增排列的线性表A和B的元素的交集并存回A中int i,j,k;i=j=0;k=-1;while(i<pA->length&&j<B.length){if(pA->elem[i]<B.elem[j]) i++;else if(pA->elem[i]>B.elem[j]) j++;else{if((k>-1&&pA->elem[i]!=pA->elem[k])||k==-1)pA->elem[++k]=pA->elem[i];i++;j++;}}pA->length=k+1;(*ppC)=pA;
}

http://www.ppmy.cn/news/1547526.html

相关文章

图形几何之美系列:仿射变换矩阵之先转后偏

“在几何计算、图形渲染、动画、游戏开发等领域&#xff0c;常需要进行元素的平移、旋转、缩放等操作&#xff0c;一种广泛应用且简便的方法是使用仿射变换进行处理。相关的概念还有欧拉角、四元数等&#xff0c;四元数在图形学中主要用于解决旋转问题&#xff0c;特别是在三维…

DB-GPT系列(四):DB-GPT六大基础应用场景part1

一、基础问答 进入DB-GPT后&#xff0c;再在线对话默认的基础功能就是对话功能。这里我们可以和使用通义千问、文心一言等在线大模型类似的方法&#xff0c; 来和DB-GPT进行对话。 但是值得注意的是&#xff0c;DB-GPT的输出结果是在内置提示词基础之上进行的回答&#xff0c…

Java项目实战II基于微信小程序的实习记录(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在高等教育…

spark.default.parallelism 在什么时候起作用,与spark.sql.shuffle.partitions有什么异同点?

spark.default.parallelism 和 spark.sql.shuffle.partitions 是 Spark 中两个控制并行度的配置参数&#xff0c;但它们作用的场景和用途不同&#xff1a; spark.default.parallelism 用途&#xff1a;spark.default.parallelism 用于控制 RDD 中的默认分区数。适用场景&…

音视频入门基础:MPEG2-TS专题(4)——使用工具分析MPEG2-TS传输流

一、引言 有很多工具可以分析MPEG2-TS文件/流&#xff0c;比如Elecard Stream Analyzer、PROMAX TS Analyser、easyice等。下面一一对它们进行简介&#xff08;个人感觉easyice功能更强大一点&#xff09;。 二、Elecard Stream Analyzer 使用Elecard Stream Analyzer工具可以…

【mysql】使用宝塔面板在云服务器上安装MySQL数据库并实现远程连接

前言 使用宝塔Linux面板安装MySQL数据库并实现远程连接 使用宝塔面板安装mysql 宝塔面板&#xff0c;华为云开放3306端口 一些命令 // 命令行连接数据库 mysql -uroot -p // MySQL 5 版本 GRANT ALL ON *.* TO root% IDENTIFIED BY 替换成你的root密码 WITH GRANT OPTION; // …

C++ 数据结构详解

目录 C 数据结构详解 引言 1. 数组 (Array) 示例代码 2. 向量 (Vector) 示例代码 3. 链表 (List) 示例代码 4. 栈 (Stack) 示例代码 5. 队列 (Queue) 示例代码 6. 集合 (Set) 示例代码 7. 映射 (Map) 示例代码 C 数据结构详解 引言 数据结构是计算机科学中的…

集合的介绍与比较器的应用

1.集合&#xff1a; 是一种容器&#xff0c;一种变量类型&#xff0c;跟数组很像 数组的缺点&#xff1a; A.数组的空间长度固定&#xff0c;一旦确定不可以更改。多了浪费&#xff0c;少了报错。 B.使用数组 操作数据的时候&#xff0c;【删除&#xff0c;增加】效率比较低。…