对于线性表的链式存储可以用一组任意的存储单元来存储数据元素。
通过结构体构造一个特殊类型“结点”,他本身包含一个数据变量用于存储本身的数据信息,另外也包含一个结点类型的指针变量用于存储相邻结点的地址信息,这样就可以通过地址域实现任意结点的链接。没有了顺序存储所具有的弱点。
遇到的问题:
1>对于创建具有多个结点的链表(这里是基于课本运用的头插法,代码注释中有尾插法,输入为正序),创建的第一个结点放在了链表的最后端,然后依次将结点向前链接,这样就导致我们输入的数据逆序排列,为了达到要求的输出效果,我们需要变换输入方式。例如输入一组非递减的数据,我们在输入的时候就要将元素以递减的方式输入,下面的代码就是以这种方式。
2>头结点的数据域为空,在编程的时候一定要分清本身结点和下一个结点,如果初始化定义的结点变量的是头结点,必须指向它的下一个结点取数据,这样定义容易出错,所以在申明结点变量时直接将非头结点的第一个结点赋给变量,这样在后续操作就不容易出错。
3>在链表的操作中没用的结点要记得释放。
内容的详细理解都标注在代码的注释中
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define OK 1
#define TRUE 1
#define ERROR 0
#define FALSE 0
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef int Status;
typedef Status ElemType;
typedef ElemType * List;
typedef struct LNode{
ElemType data; //每一个结点的数据域
struct LNode *next; //每一个结点的指针域:存放下一个结点的地址,实现环环相扣;
}LNode, *LinkList;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void MergeList(LinkList &L1,LinkList &L2,LinkList &L3);
void CreateList(LinkList &L,ElemType n);
Status ListInsert(LinkList &L,ElemType i,ElemType e);
Status ListDelete(LinkList &L,ElemType i,ElemType &e);
int main(int argc, char *argv[]) {
LinkList L1;
LinkList L2;
LinkList L3;
Status i,m,n,u;
printf("创建链表L1的长度为:");
scanf("%d",&n);
printf("输入链表各结点的数据:");
CreateList(L1,n);
printf("\n");
LinkList b5=L1->next;
printf("/********切记链表的创建是将结点依次插入链表的尾端向前递进********/\n");
printf("/********依次表现为输入的次序与列表显示的次序是相反的*******/\n");
printf("请输出创建好的链表1:");
for(i=0;i<n;i++){
printf(" %d ",b5->data);
b5=b5->next;
}
printf("\n");
//测试:ListInsert函数;
LinkList a1=L1->next; //从第一个数据域不为空的结点开始
printf("测试:在L1链表的第三个结点前插入数据域为5的结点;\n");
if(ListInsert(L1,3,5)){
printf("请输出插入后的队列:");
for(i=0;i<=n;i++){
printf(" %d ",a1->data);
a1=a1->next;
}
}
printf("\n");
//测试:ListDelete函数;
LinkList b1=L1->next;
printf("测试:删除L1链表的第三个结点:\n");
if(ListDelete(L1,3,u)){
printf("删除结点的数据为:%d\n",u);
}
printf("请输出删除后的队列:");
for(i=0;i<n;i++){
printf(" %d ",b1->data);
b1=b1->next;
}
printf("\n");
printf("\n");
printf("创建链表L2的长度为:");
scanf("%d",&m);
printf("输入链表各结点的数据:");
CreateList(L2,m);
printf("\n");
MergeList(L1,L2,L3);
LinkList a3=L3->next;
LinkList a4=L3->next;
ElemType q=0;
while(a4){
q++;
a4=a4->next;
}
printf("新链表结点的个数:%d\n",q);
printf("请输出归并后L3的元素序列:");
for(i=0;i<q;i++){
printf(" %d ",a3->data);
a3=a3->next;
}
printf("\n");
return 0;
}
//头插法 逆序
void CreateList(LinkList &L,ElemType n){ //创建一个长度为n的单链表
ElemType i;
L=(LinkList)malloc(sizeof(LNode)); //申请一个结点大小的地址空间
L->next=NULL;
LinkList p; //创建一个结点类型的变量(本质是一个结点的地址)
//总体链表创建的过程是逆着进行,先创建最后一个结点,依次在该结点的前边添加新结点。
for(i=n;i>0;i--){
p=(LinkList)malloc(sizeof(LNode));
scanf("%d",&(p->data)); //为增加的结点添加数据域
p->next=L->next; //将新结点插入到表头
L->next=p; //因为头结点仅仅是一个用于添加新结点的存储器,所以每次头结点的指针域都在改变。
}
}
//尾插法:正序
/*void CreateList(LinkList &L,ElemType n){
ElemType i;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
LinkList p,q;
q=L;
for(i=0;i<n;i++){
p=(LinkList)malloc(sizeof(LNode));
p->next=NULL;
scanf("%d",&(p->data));
q->next=p;
q=q->next;
}
}
*/
Status ListInsert(LinkList &L,ElemType i,ElemType e){ //在L的第i个元素前插入元素e
LinkList p=L; //初始化头节点
ElemType j=0;
while(p&&j<i-1){ //寻找第i个结点的前一个结点
p=p->next; //循环完成找到第i-1个结点
++j;
}
if(!p||j>i-1) return ERROR;
LinkList s=(LinkList)malloc(sizeof(LNode)); //增加新结点
s->data=e; s->next=p->next; //新结点的指针域存放原来第i个结点的地址。
p->next=s; //第i-1个结点的指针域存放新节点的地址
return OK;
}
Status ListDelete(LinkList &L,ElemType i,ElemType &e){ //删除链表的第i个结点,用e返回该节点的值。
LinkList p=L; //初始化头节点
ElemType j=0;
while(p&&j<i-1){ //寻找第i个结点的前一个结点
p=p->next; //循环完成找到第i-1个结点
++j;
}
if(!p||j>i-1) return ERROR;
LinkList q=p->next; //定义一个结点变量,用来表示即将删除的第i个结点。
e=q->data; //将删除结点的数据域赋给变量e
p->next=q->next; //改变删除结点的前一个结点的指针域
free(q); //释放删除结点所占用的空间
return OK;
}
void MergeList(LinkList &L1,LinkList &L2,LinkList &L3){ //单链表的归并
LinkList p1,p2,p3;
p1=L1->next;
p2=L2->next;
L3=p3=L2; //这里L1或者L2均可以,只是起到一个头指针赋给L3的作用
while(p1&&p2){ //结束条件有一个链表的指针域为空
if((p1->data)<=(p2->data)){
p3->next=p1; p3=p1; p1=p1->next;
}else{
p3->next=p2; p3=p2; p2=p2->next;
}
}
//将剩余结点拼接
if(p1){
p3->next=p1;
}
if(p2){
p3->next=p2;
}
free(L1); //释放列表1的头结点
}
运行结果: