例1、假设有两个线性表LA和LB分别表示两个集合A和B,现要求一个新集合A=A U B(集合的并)。
算法思想:扩大线性表LA,将表LB中不在LA中出现的元素插入到LA中。只要从线性表LB中依次取出每个元素,按值在线性表LA中查找,若没查到则插入之。
算法描述:
void union (Linear_List LA ,Linear_List LB) //假定Linear_List是线性表类型
{
n = ListLength(LA); //求LA的表长
for(i=1;i<=ListLength(LB),i++) //遍历Linear_List LB 的元素
{
x = GetNode(LB,i); //取LB中第i个元素赋值给x
if(LocateNode(LA,x) == 0) //按值查找,与表LA的值一一比对
{
InsertList(LA,++n,x); //在表LA的值之后插入x的值
}
}
}
例2、删除线性表L中重复的元素
算法思想:从表L中的第一个元素(i=1)开始,逐个检查i位置以后的任一位置j,若两元素值相同,则从表中删除第j个元素,。。。。直到i移到当前表L的最后一个位置为止。
算法描述:
void purge (Linear_List L)
{
i =1 ; //假设从第一个元素开始,i=1
while (i<ListLength (L)) // 当 元素i 在表L内
{
x = GetNode(L,i); //取表L第i个元素的值赋值给x
j=i+1; //假设查找从第i+1开始
while(j<=ListLength(L)) //当 元素j 在表L内
{
y=GetNode(L,j); //取表L第j个元素的值赋值给y
if(x==y) //如果x等于y
{
DeleteList(L,j); //删除表L第j个元素
}
else
{
j++; //否则j+1;
}
}
i++; //当j+1后,i相应也+1
}
}
例3、顺序线性表插入操作
算法描述:
void InsertList(seqlist *L ,int i ,DataType x)
//在顺序表L中第i个位置之前插入一个新元素x
{
int j ;
if(i<1 || i>L->length+1)
{
printf("position error");
return ;
}
if(L->length >= ListSize)
{
printf("overflow");
return ;
}
for(j=l->length-1;j>=i-1;j--)
{
L->data[j+1]=L->data[j]; //从最后一个元素开始逐一后移
L->data[i-1]=x; //在第i个元素之前插入新元素x
L->length++; //实际表长+1
}
}
例4、顺序表删除操作
算法描述:
DataType DeleteList (SeqList *L ,int i)
//在顺序表L中删除第i个元素,并返回被删除元素
{
int j ;
DataType x;
//DataType是一个通用类型标识符,在使用时再定义实际类型
if(i<1 || i->length)
{
printf("position error");
exit(0); //出错退出处理
}
x = L->data[i]; //保存被删除元素
for(j=i;j<=L->length ;j++)
{
L->data[j-1]=L->data[j]; //元素前移
}
L->length--; //实际表长减1
return x; //返回被删除的元素
}
例5、已知一长度为n的顺序存储的线性表,试写一算法将该线性表逆置。
算法思想:先以表长的一半作为循环控制次数,将表中最后一个元素同顺数第一个元素交换,将倒数第二个元素同顺数第二个元素交换,依此类推,直至交换完为止。
算法描述:
SeqList Converts (SeqList L)
{
DataType x ;
int i ,k ;
k=L.length/2;
for(i=0;i<k;i++)
{
x=L.data[i]; //把表L第i个元素赋值给x
L.data[i]=L.data[L.length -i-1]; //把表L第L.length-i-1 个元素赋值给 第i个元素
L.data[L.length-i-1]=x; //把值x赋值给表L第L.length-i-1 个元素
}
return L; //返回线性表L
}
例6、试写一算法,实现在顺序表中查找出最大值和最小值的元素及所在位置。
算法思想:如果在查找最大值和最小值的元素时各扫描一遍所有元素,则至少要比较2n次,可以使用一次扫描找出最大值和最小值的元素。另外,在算法中要求带回求得的最大值和最小值元素及其所在位置,可用4个指针变量参数间接得到,也可以用外部变量实现,因为函数本身只可返回一个值。下面是采用指针变量参数来实现。
算法描述:
void MaxMin (SeqList L ,DataType *max ,DataType *min ,int *k ,int *j)
{
int i ;
*max=L.data[0];
*min=L.data[0];
*k=*j=1; //先假设第一个元素既是最大值,也是最小值。
for(i=1;i<L.length ;i++)
{
if(L.data[i]>*max)
{
*max =L.data[i];
*k=i;
}
else if (L.data[i]<*min)
{
*min =L.data[i];
*j=i;
}
}
}
例7、用头插法建立单链表
算法思想:头插法建表是从一个空表开始,重复读入数据,生成新结点,将读入的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。
算法描述:假设线性表中结点的数据域为字符型
LinkList CreateListF()
{
LinkList head;
ListNode *p;
char ch;
head =NULL; //置空单链表
ch=getchar(); //读入第一个字符
while(ch!='\n') //读入字符不是结束标志符时作循环
{
p =(ListNode *)malloc(sizeof(ListNode)); //申请新结点
p->data =ch; //数据域赋值
p->next =head; //指针域赋值
head =p ; //头指针指向新结点
ch =getchar(); //读入下一个字符
}
return head; //返回链表的头指针
}
例8、用尾插法建立(不带头结点)单链表
算法思想:将新结点插入在当前链表的表尾上,因此需要增设一个尾指针rear,使其始终指向链表的尾结点。
算法描述:假设线性表中结点的数据域为字符型
LinkList CreateListR()
{
LinkList head ,rear ;
ListNode *p;
char ch;
head =NULL;
rear=NULL; //置空单链表
ch = getchar(); //读入第一个字符
while(ch!='\n') //读入字符不是结束标识符时作循环
{
p=(ListNode *)malloc(sizeof(ListNode)); //申请新结点
p->data =ch; //数据域赋值
if(head==NULL)
{
head =p ; //新结点*p插入空表
}
else
{
rear->next =p ; //新结点*p插入到非空表的表为结点*rear之后
}
rear =p; //表尾指针指向新的表尾结点
ch =getchar(); //读入下一个字符
}
if(rear!=NULL)
{
rear->next =NULL; //终端结点指针域置空
}
return head;
}
例9、用尾插法建立(带头结点)的单链表
算法思想:为简化算法,方便操作,可在链表的开始结点之前附加一个结点,并称其为头结点,
算法描述:
LinkList CreateListR1()
//尾插法建立带头结点的单链表算法
{
LinkList head =(ListNode *)malloc(sizeof(ListNode)); //申请头结点
ListNode *p,*r ;
DataType ch;
r=head; //尾指针初始指向头结点
while(ch=getchar())!='\n')
{
p=(ListNode *)malloc(sizeof(ListNode)); //申请新结点
p->data =ch;
r->next =p ; //新结点连接到尾结点之后
r= p ; //尾指针指向新结点
}
r->next =NULL; //终端结点指针域置空
return head;
}
例10、查找运算(带头结点),按结点序号查找
算法思想:在单链表中要查找第i个结点,就必须从链表的第1个结点(开始结点,序号为1)开始,序号为0是头结点,p指向当前结点,j为计数器,其初始值为1,当p扫描下一个结点时,计算器加1。当j=i时,指针p所指向的结点就是要找的结点。
算法描述:
ListNode * GetNodei (LinkList head ,int i)
//head是带头结点的单链表的头指针,i为要查找的结点序号
//若查找成功,则返回查找结点的存储地址(位置),否则返回NULL
ListNode *p ;
int j ;
p=head ->next ;
j=1; //使p指向第一个结点,j置1
while(p!=NULL && j<i) //顺指针向后查找,直到p指向第i个结点或p为空为止
{
p=p->next ;
++j;
}
if(j==i)
{
return p ;
}
else
{
return NULL;
}
例11、查找运算(带头结点),按结点值查找
算法思想:在单链表中按值查找结点,就是从链表的开始结点出发,顺链逐个将结点的值和给定值k进行比较,若遇到相等的值,则返回该结点的存储位置,否则返回NULL。按值查找算法要比按序号查找更为简单。
算法描述:
ListNode *LocateNodek (LinkList head ,DataType k)
{
//head为带头结点的单链表的头指针,k为要查找的结点值
//若查找成功,则返回查找结点的存储地址(位置),否则返回NULL
ListNode *p =head->next ; //p指向开始结点
while(p&&p->data! =k) //循环直到p等于NULL或p->data等于k为止
{
p=p->next; //指针指向下一个结点
}
return p; //若找到值为k的结点,则p指向该结点,否则p为NULL
}
例12、插入运算
算法思想:先使p指向ai-1的位置,然后生成一个数据域值为x的新结点*s,再进行插入操作。
算法描述:
void InsertList(LinkList head ,int i ,DataType x)
{
//在以head为头指针的带头结点的单链表中第i个结点的位置上
//插入一个数据域为x的新结点
ListNode *p ,*s ,int j ;
p=head;
j=0;
while (p!=NULL && j<i-1) //使p指向第i-1个结点
{
p=p->next;
++j;
}
if(p==NULL) //插入位置错误
{
printf("ERROR\n ");
return ;
}
else
{
s=(ListNode *)malloc(sizeof(ListNode)); //申请新结点
s->data =x;
s->next =p->next;
p->next =s;
}
}
例13、删除运算
算法思想:删除运算就是将链表的第i个结点从表中删去。由于第i个结点的存储地址是存储在第i-1个结点的指针域next中,因此要先使p指向第i-1个结点,然后使得p->next指向第i+1个结点,再将第i个结点释放掉。
算法描述:
DataType DeleteList(LinkList head, int i)
//在以head为头指针的带头结点的单链表中删除第i个结点
{
ListNode *p,*s ;
DataType x;
int j ;
p=head;
j=0;
while(p! =NULL&&j<i-1) //使p指向第i-1个结点
{
p=p->next;
++j;
}
if(p==NULL) //删除位置错误
{
printf(“位置错误\n”);
exit(0); //出错退出处理
}
else
{
s=p->next; //s指向第i个结点
p->next =s->next; //使p->next指向第i+1个结点
x=s->data; //保存被删除结点的值
free(s);
return x; //删除第i个结点,返回结点值
}
}
例14、试写一算法,将一个头结点指针为a的带头结点的单链表A分解成两个单链表A和B,其中头结点指针分别为a和b,使得A链表中含有原链表A中的序号为奇数的元素,而B链表中含有原链表为偶数的元素,并保持原来的相对顺序。
算法思想:根据题目要求,需要遍历整个链表A,当遇到序号为奇数的结点时将其链接到A表上,序号为偶数结点时,将其链接到B表中,一直到遍历结束。
算法描述:
void split (LinkList a ,LinkList b)
//按序号奇偶分解单链表,注意b在调用该算法前是一个带头结点的空链表
{
ListNode *p ,*r ,*s ;
p=a->next; //p指向表头结点
r=a; //r指向A表的当前结点
s=b; //s指向B表的当前结点
while(p!=NULL)
{
r->next =p ; //序号为奇数的结点链接到A表上
r=p; //r总是指向A链表的最后一个结点
p=p->next;
if(p)
{
s->next =p ; //序号为偶数的结点链接到B表中
s=p; //s总是指向B链表的最后一个结点
p=p->next; //p指向原链表A中的偶数序号的结点
}
}
r->next =s->next =NULL;
}
例15、假设头指针为La和Lb的单链表(带头结点)分别为线性表A和B的存储结构,两个链表都是按结点数据值递增有序的。试写一算法,将这两个单链表合并为一个有序链表Lc。
算法思想:根据题目要求需设立三个指针pa、pb、pc,其中pa和pb分别指向La表和Lb表中当前待比较插入的结点,而pc则指向Lc表当前的最后一个结点,若pa->data<=pb->data,则将pa所指向的结点之后,否则将pb所指向的结点链接到pc所指的结点之后。
算法描述:
LinkList MergeList(LinkList La ,LinkList Lb) //归并两个有序链表La和Lb为有序链表Lc
{
ListNode * pa , *pb ,*pc ;
ListList Lc;
pa =La ->next ;
pb =Lb ->next ; //pa和pb分别指向两个链表的开始结点
Lc =pc =La ; //用La的头结点作为Lc的头结点
while (pa!=NULL&&pb! =NULL)
{
if(pa->data<=pb->data)
{
pc->next =pa ;
pc=pa;
pa=pa->next;
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next =pa!=NULL?pa:pb; //插入链表剩余部分
free (Lb); //释放Lb的头结点
return Lc; //返回合并后的表
}
例16、已知有一个结点数据域为整型的,且按从大到小顺序排列的头结点指针为L的非空单循环链表,试写一算法插入一个结点(其数据域为x)至循环链表的适当位置,使之保持链表的有序性。
算法思想:要解决这个算法问题,首先就是要解决查找插入位置问题,查找就要使用循环语句,从表头开始逐个结点比较。判断整个循环链表是否比较结束,就是判断当前结点的指针域是否等于头指针。另外,由于链表是递减有序的,在查找插入位置时,循环条件是当前结点值是否要大于要插入的结点值,所以,若用q指向表中当前结点,那么循环条件应该是q->data>x&&q!=L ,还要使用一个指针p指向插入位置的前驱结点。
算法描述:
void InsertList(LinkList L ,int x ) //将值为x的新结点插入到有序循环链表中适当的位置
{
ListNode *s ,*p ,*q ;
s =(ListNode *)malloc (sizeof(ListNode)); //申请结点存储空间
s->data =x ;
p=L;
q=p->next; //q指向开始结点
while(q->data>x&&q!=L) //查找插入位置
{
p=p->next; //P指向q的前驱结点
q=p->next; //q指向当前结点
}
p->next =s; //插入*s结点
s->next =q;
}
例17、假设有一个头结点指针为head的循环双向链表,其结点类型结构包含三个域:prior、data和next。其中data为数据域,next为指针域,指向其后继结点,prior也为指针域,其值为空(NULL),因此该双向链表其实也是一个单循环链表。试写一算法,把其表修改成真正的双向循环链表。
算法思想:根据题目要求,就是要将表中的每个prior域填上相应的前驱结点地址。只要弄清楚双向循环链表的结构,其算法就非常简单。其实只需要以p指向头结点开始,循环使用语句p->next->prior=p ;和 p=p->next;即可实现题目要求。
算法描述:
void trans(DLinkList head)
{
DLNode *p ;
p =head ; //使p指向头结点
while(p->next! =head) //依次从左向右,对每个结点的prior赋值
{
p->next->prior =p ; //p所指结点的直接后继结点的前驱就是p
p=p->next; //p指针指向下一个结点
}
head—>prior =p ; //head 的前驱指向表的终端结点
}
例18、设有两个顺序表A和B,且都递增有序。试写一算法,从A中删除与B中相同的那些元素(也就是计算A-B)。
算法思想:扫描整个B表,顺序取表中每一个元素,然后与表A中从某下标开始的元素进行比较(因为两表都是有序的,不必每次从头开始,用一个变量k标识上一次比较结束的位置),当B中某个元素值大于或等于A中某个元素时,比较结束。记住A表的当前下标值K,之后再比较两元素值是否相等,若相等,则从表A中删除该元素,而后继续B中的下一个元素与A中第k个元素开始向后比较;否则,继续,直到B中所有元素比较完为止。
算法描述:
void SubList(SeqList *A ,SeqList B)
{
int i,j,k;
k=1; //记住A表的下标位置
for(i=1;i<=B.length ;i++)
{
for(j=k,j<=A->length ;j++)
{
if (B.data[i]>=A->data[j]) //因为表是有序的,不必比较到最后
{
k=j;
break; //用k记住A表的当前位置,以便下一次比较从k开始
}
else
{
continue;
}
if(B.data[i]==A->data[k]) //相等表示有重复的
{
DeleteList(A,k); //调用删除函数,删除A中第k个元素
}
}
}
}
例19、已知head是指向一个带头结点的单链表的头指针,p指向该链表的任一结点。试写一算法,将p所指向的结点与其后继结点位置交换。
算法思想:如果p存在后续结点,看它是否为头结点,如果是,则交换后还要改变该链表的head;若不是头结点,则直接交换,如果p指向最后一个结点,说明其没有后续结点,则不交换。
算法描述:
void ExchangeNode(LinkList head ,ListNode *p) //交换p所指结点与其后续结点存储位置
{
ListNode *q ,*r ,*s ;
q = p->next; //q指向p的后续结点
if(q!=NULL) //表示p不是指向最后一个结点
{
if(p==head) //若p指向头结点,则将该链表的前两个结点交换位置
{
head=head->next;
s=head->next;
head-next =p ;
p->next =s;
}
else //若p指向第二个结点之后的结点
{
r=head ; //查找p的前驱结点
while(r->next! =p)
{
r=r->next;
}
r->next =q; //交换p和q的位置
p->next =q->next;
q->next=p;
}
}
else
{
printf("p不存在后续结点,无须进行交换");
}
}
例20、已知两单链表A和B分别表示两个集合,其元素值递增有序。试写一算法,求A和B的交集C,要求C同样以元素值递增的单链表形式存储。
算法思想:两集合的交集是指两个单链表的元素值相同的结点的集合,为了操作方便,先让单链表C带头结点。
算法描述:
void Bemixed(LinkList a , LinkLink b ,LinkList c) //求A和B的交集C,分别用单链表表示集合
{
ListNode *p,*q,*r,*s ;
c=(ListNode *)malloc(sizeof(ListNode)); //生成c表头结点
r=c;
p=a;
q=b;
while(p!=NULL&&q!=NULL)
{
if(p->data<q->data) //因为表是有序的,A表元素值小,则后移一个位置
{
p= p->next;
}
else
if(p->data>q->data) //同上理由,B表元素值小
{
q=q->next;
}
else //找到一个值相同的结点,在c中生成一个新结点
{
s=(ListNode *)malloc(sizeof(ListNode));
s->data =p->data;
s->next =NULL;
r->next =s; //把s结点链接到C表的尾部
r=s; //r始终指向C表的最后一个结点
p=p->next; //使p、q指向其后续结点
q=q->next;
}
}
}
例21、设有一个带头结点的双向循环链表,head为链表的头指针。试写一算法,实现在值为x的结点之前插入一个值为y的结点。
算法思想:因为链表是双向循环链表,所以不需要用指针指向x之前的结点,但要先从开始结点用循环找到要插入的位置,也就是x结点的位置。
算法描述:
void DinsertB(DlinkList head ,DataType x ,DataType y)
//在带头结点的双循环链表的值为x的结点之前插入一个结点
{
DlistNode *p ,*s ;
s=(DlistNode *)malloc(sizeof(DlistNode));
s->data =y;
p=head->next;
while(P!=head&&p->data!=x)
{
p=p->next;
if(p==head)
{
printf("没有值为x的结点");
}
else
{
s->prior =p->prior;
s->next=p;
p->prioe=s;
p->prior->next=s;
}
}
}