一、 实验任务
编写算法实现下列问题的求解。
(1)在第i个结点位置插入值为x的结点。
实验测试数据基本要求:
第一组数据:单链表长度n≥10,x=100, i分别为5,n,n+1,0,1,n+2
第二组数据:单链表长度n=0,x=100,i=5
(2)删除单链表中第i个元素结点。
实验测试数据基本要求:
第一组数据:单链表长度n≥10,i分别为5,n,1,n+1,0
第二组数据:单链表长度n=0, i=5
(3)在一个递增有序的单链表L中插入一个值为x的元素,并保持其递增有序特性。
实验测试数据基本要求:
单链表元素为 (10,20,30,40,50,60,70,80,90,100),
x分别为25,85,110和8
(4)将单链表L中的奇数项和偶数项结点分解开(元素值为奇数、偶数),分别放入新的单链表中,然后原表和新表元素同时输出到屏幕上,以便对照求解结果。
实验测试数据基本要求:
第一组数据:单链表元素为 (1,2,3,4,5,6,7,8,9,10,20,30,40,50,60)
第二组数据:单链表元素为 (10,20,30,40,50,60,70,80,90,100)
(5)求两个递增有序单链表L1和L2中的公共元素,放入新的单链表L3中。
实验测试数据基本要求:
第一组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10,18,20,30)
第二组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (2,4,5,7,8,9,12,22)
第三组
第一个单链表元素为 ()
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10)
(6)删除递增有序单链表中的重复元素,要求时间性能最好。
实验测试数据基本要求:
第一组数据:单链表元素为 (1,2,3,4,5,6,7,8,9)
第二组数据:单链表元素为 (1,1,2,2,2,3,4,5,5,5,6,6,7,7,8,8,9)
第三组数据:单链表元素为 (1,2,3,4,5,5,6,7,8,8,9,9,9,9,9)
(7)递增有序单链表L1、L2,不申请新结点,利用原表结点对两表进行合并,并使得合并后成为一个集合,合并后用L1的头结点作为头结点,删除多余的结点,删除L2的头结点。要求时间性能最好。
实验测试数据基本要求:
第一组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10,18,20,30)
第二组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (2,4,5,7,8,9,12,22)
第三组
第一个单链表元素为 ()
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10)
二、 顺序表扩展实验
非必做内容,有兴趣的同学选做,
(1)(递增有序)单链表表示集合A、B,实现:
C=AB,C=AB,C=A-B
A=AB,A=AB,A=A-B
(2)(递增有序)单链表表示集合A、B,判定A是否B的子集。
(3)(2009)(15分)已知一个带有表头结点的单链表,结点结构如下图。假设该链表只给出了头指针list。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,算法输出该结点的data值,并返回1;否则,只返回0。要求:
描述算法的基本设计思想
描述算法的详细实现步骤
根据设计思想和实现步骤,采用程序设计语言描述算法(使用C 或C++语言实现),关键之处请给出简要注释。
- 三、算法设计
(3)递增插入:从前往后搜索,同时当前指针为前一个结点,当该节点的数小于x时,插入该元素。
(4)分为奇偶:建立两个新表1和2,对原表进行处理。在遍历过程中,当找到奇数时尾插入到新表1中;找到偶数时用尾插法插入到新表2中。
(5)有序C = A交B:每次取A和B中的一个最小元素,依次插入 C表即可。
当A->data < B->data时,A表中当前元素较小,插入C表。A表指针后移, 继续取A表的下一个元素,而B表指针不变;
当A->data > B->data时,B表中当前元素较小,插入C表。B表指针后移, 继续取B表的下一个元素,而A表指针不变;
当A->data == B->dat时,A、B表元素相同,或同时插入C表,或插入 一个。同时插入,执行A、B表指针后移,同时取下一 个元素。
重复执行上述判断、插入过程,直到A和B表中 有一个结束。
(6)删除重复元素:将元素分成:已经处理元素和待处理元素。分别用p, q表示。初始化 p = L->next, q = p->next。
当p->data == q->data,说明q指示的是p的重复元素,继续处理 q 的下一个元素,即执行q = q->next。
当p->data < q->data时,因为 L 递增,所以剩下情况只能是p->data < q->data,q为目标元素。如果 p->next = q,说明q紧随 p,无需移动元素,直接pq后移即可。若不同q元素复制到 p->next。无论那种情况,都需要同时后移pq。循环执行上述操作,直到表尾。
(7)A = A 并 B:p,q分别指向L1,L2,u指向被删除的节点;
当p->next->data > q->next->data时,A表中当前元素较小,q向后移一位。
当p->next->data < q->next->data时,B表中当前元素较小,因为递增性,B指示的元素不可能在A中,将该元素留在原表中。
当p->next->data == q->next->data时,A、B表元素相同删除B中的重复元素
重复执行上述判断、插入过程,直到A和B表中 有一个结束。若A表剩余直接就在其中,若B表剩余,则全满足。A尾结点p连接B首节点,删除B头结点
(8)A = A 并 B(见第7题)
A = A 交 B:设置两个指针pa、pb分别指向A、B表当前处理的元素
当pa->next->data==pb->data时,pa->next 是交集结点,同时后移 pa、pb。
当pa->next->data<pb->data时,pa->next 不是交集结点,删除 pa->next 结点,pa 和 pb 不变
当pa->next->data>pb->data时,pa->next 可能在 pb 后面,pa 不变pb 后移。
重复以上过程,直至 A、B 中至少一个表结束。 如果 A 表未结束,pa->next 及后面所有结点都是非交集结点,删除
A = A 差 B:设置两个指针p,q分别指向 A、B 表当前处理的元素
当p->next->data == q->next->data,则是交集元素,不是A-B 中元素,直接 删除A表元素。
当p->next->data > q->next->data, A的元素可能在 B 表的元素后面,p不动,移动q。
当p->next->data < q->next->data,A指示的元素不可能在 B 中出现,故比为 A-B 中元素。直接后移p。
重复以上过程,直至 A、B 中至少一个表结束。若B 表已结束,剩下元素全为 A-B 中元素。若A表已结束,则直接结束。
C = A 并 B:设置三个指针p, q, R分别指向A、B、C表当前处理的元素;
当p->data < q->data时,A表中当前元素较小,因为递增性,p指示的元素
不可能在B中,将该元素插入C表,A表后移。
当p->data > q->data时,B表中当前元素较小,因为递增性,q指示的元素
不可能在A中,将该元素加入C中,B表后移。
当p->data == q->data时,A、B表元素相同,插入一个到C中,同时取下一个元素。
重复执行上述判断、插入过程,直到A和B表中 有一个结束。若A表剩余直接插入C中,若B表剩余,则直接插入C中。
C = A 交 B:(见第5题)。
C = A 差 B:设置三个指针p, q, R分别指向A、B、C表当前处理的元素;
当p->data == q->data,则是交集元素,不是A-B 中元素,直接跳过,p,q后移。
当p->data > q->data,p指示的元素可能在 B 表 q指示的元素后面,p不动,移动q。
当p->data < q->data,p指示的元素不可能在 B 中出现,故比为 A-B 中元素。需要写入C中,然后p后移。
重复以上过程,直至 A、B 中至少一个表结束。若B 表已结束,剩下元素全为 A-B 中元素,全部迁移到目标位置。若A表已结束,则直接结束。
(9)是否为子集:设用pa和pb分别为A和B中元素的结点
当pa->data > pb->data时,即A表中当前元素大于B表中当前元素,因而需要继续 在B表中搜索,即要执行pb后移一个元素并继续搜索。
当pa->data == pb->data时,A表中当前元素在B表中,即查找成功, 同时后移。
当pa->data < pb->data时,即A表中当前元素小于B表中当前元素,因而肯定小于 其后面的所有元素,所以该元素肯定不在B中,所以不是子集,返回0。
重复执行上述判断过程,直到pa和pb中至少有一个指向表尾之后为止。此时,A表未结束。可返回结论1。若B表结束,可返回0。
(10)算法设计:利用双指针,使得第一个指针先向下走k个位置,此时两个指针之间间距为k。然后,让两个指针一起走,每次都向下移动一个位置,当先走的指针走到链表尾,后走的指针所处位置即为倒数第k个位置。
算法详细实现:定义两个指针p和q,都指向头节点。定义一个变量i=0,代表p走了几步;在p不为空并且i<k时,让p向下走k步。判断i是否等于k,如果不等于,返回0,否则让p和q一起向后走,直到p到达链表尾,q所处位置即为倒数第k个位置。
#define MAXLEN 100
#include <iostream>
using namespace std;
typedef struct slNode{
int data;
struct slNode *next;
}node;
typedef struct lNode{
int data;
struct lNode *link;
}Node;
//初始化一个新单链表
void initList(node *&L){
L = new node;
L->next = NULL;
}
//读取数组元素到单链表中
void creatList(node *&L, int a[], int n){
int i;
L = new node;
node * R;
R = L;
L->next = NULL;
for(i = 0; i < n; i++){ //将数组元素读入单链表中
node *s = new node;
s->next = R->next;
s->data = a[i];
R->next = s;
R = s;
}
}
void destroyList(node *&L){
node *p,*q;
p = L; //初始化时p指向头结点
while(p!=NULL) //p不为空时执行
{
q = p->next; //q变量记录p的下一个节点
delete p; //释放p结点
p = q; //把q赋值给p
}
}
//展示单链表元素
void displayL(node *L){
node *p = L->next;
if(p == NULL){
cout << "表为空!";
}
while(p != NULL){ //遍历打印出单链表元素
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
//基本插入元素
bool listInsert(node *L, int x, int i){
bool flag = 0;
node *s, *p = L;
int j = 0;
while(j != i -1 && p != NULL){
p = p->next;
j++;
}
if(p == NULL){
cout << "序号错误!" << endl;
}else{
s = new node;
s->data = x;
s->next = p->next;
p->next = s;
flag = 1;
}
return flag;
}
//基本删除元素
bool listDelete(node *L, int i){
bool flag = 0;
node *s, *p = L;
int j = 0;
while(j != i -1 && p != NULL){
p = p->next;
j++;
}
if(p == NULL || p->next == NULL)
cout << "序号" << i << "超出范围,删除失败!" << endl; //超出范围,不能删除
else{
s = p->next;
p->next = s->next;
delete s;
flag = 1;
}
return flag;
}
//递增有序插入
void Insert(node *L, int x){
node *s, * p = L;
while(p->next != NULL && p->next->data<x){
p = p->next;
}
s = new node;
s->data = x;
s->next = p->next;
p->next = s;
}
//分为奇偶单链表
void part(node *L, node *A, node *B){
node *s, * p = L->next;
node *R, *Q;
R = A;
Q = B;
while(p != NULL){
if(p->data % 2 == 0) //为偶数,进入A中
{
s = new node;
s->data = p->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next;
}else{ //为奇数,进入B中
s = new node;
s->data = p->data;
s->next = Q->next;
Q->next = s;
Q = s;
p = p->next;
}
}
}
//C = A 交 B
void merge(node *A, node *B, node *C){
node *p, *q, *s, *R;
p = A->next;
q = B->next;
R = C;
while (p != NULL && q != NULL){
if (p->data < q->data){//A小于B,A元素可能在B元素后面,A元素后移
p = p->next;
}else if (p->data > q->data){
q = q->next;//B小于A,B元素可能在A元素后面,B元素后移
}else{//交集元素,加入C
s = new node;
s->data = q->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next;
q = q->next;
}
}
}
//删除重复元素
int DeleteRepeatData(node *&L){
int sum;
sum = 0;
node *s, *q, *p = L->next;
q = p->next;
while(q != NULL){
if(p->data == q->data) //为重复元素,q 后移
q = q->next;
else{ //因为 L 递增,所以剩下情况即 p->data < q->data
if((p->next) != q){//如果p->next = q,说明q紧随 p,无需移动元素,直接两指针后移
p->next->data = q->data;
sum++;
}
p = p->next; //无论那种情况,都需要同时后移pq
q = q->next;
}
}
destroyList(p->next);
p->next = NULL;
return sum;
}
//A = A 并 B
void Add(node *L1, node *L2){
node *p,*q,*u; //p,q分别指向L1,L2,u指向被删除的节点
p = L1; //初始化时指向头结点L1
q = L2; //初始化时指向头结点L2
while(p->next != NULL && q->next != NULL)
{
if(p->next->data == q->next->data)
{
u = q->next; //把要删除的节点指针赋值给u
q->next = u->next; //连接要删除结点的前后结点
delete u; //如果L1,L2有重复元素,删除L2中的重复元素
p = p->next; //p向后移一位,q不移
}
else if((p->next->data) > (q->next->data))
{
q = q->next; //如果(p->next->data)>(q->next->data),q向后移一位
}
else{
p = p->next; //如果(p->next->data)<(q->next->data),p向后移一位
}
}
if(p->next != NULL) //如果p没有指向尾结点,使它指向尾结点
{
while(p->next != NULL)
{
p = p->next;
}
}
p->next = L2->next; //L1尾结点p连接L2首节点
delete L2; //删除L2头结点
}
//A = A 交 B
void InterSet(node *A, node *B){
node* pa, *pb, *u, *p;
pa=A; //Pa 指向 A
pb=B->next; //pb 指向 B 的首元素结点
while(pa->next && pb) //A、B 都有结点,循环处理
{
if(pa->next->data<pb->data){ //pa->next 不是交集结点,删除 pa->next 结点,pa 和 pb 不变
u=pa->next;
pa->next=u->next;
delete u;
}else if(pa->next->data>pb->data) //pa->next 可能在 pb 后面,pa 不变,pb 后移
pb=pb->next;
else //pa->next->data==pb->data 情况,pa->next 是交集结点,同时后移 pa、pb
{
pa=pa->next;
pb=pb->next;
}
}
//如果 A 表未结束,pa->next 及后面所有结点都是非交集结点,删除
if(pa->next) //置 A 表结束
{
u=pa->next;
pa->next=NULL;
p=u;
//删除 A 表剩下的非交集结点
while(p){
u=p;
p=p->next;
delete u;
}
}
}
//A = A 差 B
void SetSubtraction(node *&A, node *&B){
node *s;
node *p = A;
node *q = B;
while(p->next != NULL && q->next != NULL){
if(p->next->data == q->next->data){
s = p->next;
p->next = s->next;
delete s;
}else if(p->next->data > q->next->data){
q = q->next;
}else{
p = p->next;
}
}
}
//C = A 并 B
void MergeList(node *A, node *B, node *C){
node *p, *q, *s, *R;
p = A->next;
q = B->next;
R = C;
while(p != NULL && q != NULL){
if(p->data == q->data){ //A、B表元素相同,插入一个。同时后移
s = new node;
s->data = q->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next;
q = q->next;
}else if(p->data < q->data){ //A表中当前元素较小,插入C表。A表后移 ,
s = new node;
s->data = p->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next; //继续取A表的下一个元素,而B表不变;
}else{ //B表中当前元素较小,插入C表。B表后移,
s = new node;
s->data = q->data;
s->next = R->next;
R->next = s;
R = s;
q = q->next; //继续取B表的下一个元素,而A不变;
}
}
//下面处理一个表结束,另一个表未结束情况
while(p){
s = new node;
s->data = p->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next;
}
while(q){
s = new node;
s->data = q->data;
s->next = R->next;
R->next = s;
R = s;
q = q->next;
}
}
//C = A 差 B
void cha(node *A, node *B, node *C){
node *p, *q, *s, *R;
p = A->next;
q = B->next;
R = C;
while (p != NULL && q != NULL){
if (p->data < q->data){//A小于B,A元素不可能在B元素后面,插入C,A元素后移
s = new node;
s->data = p->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next;
}else if (p->data > q->data){
q = q->next;//B小于A,B元素可能在A元素后面,B元素后移
}else{
p = p->next;
q = q->next;
}
}
while(p != NULL){
s = new node;
s->data = p->data;
s->next = R->next;
R->next = s;
R = s;
p = p->next;
}
}
//判断是否为子集
bool ZiJi(node *A, node *B){
node *pa, *pb;
pa = A->next; //pa 和 pb 分别指向 A 和 B 表的首元素结点
pb = B->next;
while(pa != NULL && pb != NULL){
if(pa->data == pb->data){ //pa 指示元素在 B 中,因是集合,pa、pb 同时后移一个结点
pa = pa->next;
pb = pb->next;
}else if(pa->data > pb->data)
pb = pb->next; //pa 指示元素可能在 B 中 pb 指示的元素后面,后移 pb
else
return 0; //此时,pa->data<pb->data,pa 指示元素不可能在 B 中,A 非 B子集
}
//下面根据 A、B 的结束情况,判定 A 是否 B 的自己
if(pa == NULL)
return 1; //A 到表尾,不管 B 表如何,A 全部元素在 B 中,是 B 的子集
else
return 0; //B 到表尾,A 未到表尾,A 后面元素不在 B 中,A 非 B 的子集
}
int function(Node *list, int k){
Node *p , *q;
p = list;
q = list->link;
int i = 0;
while(i < k && p->link){
p = p->link;
i++;
}
// 链表不够长,没等p走k步就结束了
if(!p){
return 0;
}
while(p->link){
p = p->link;
q = q->link;
}
cout << q->data << endl;;
return 1;
}
void creatList2(Node *&L, int a[], int n){
int i;
L = new Node;
Node * R;
R = L;
L->link = NULL;
for(i = 0; i < n; i++){ //将数组元素读入单链表中
Node *s = new Node;
s->link = R->link;
s->data = a[i];
R->link = s;
R = s;
}
}