本文共 8253 字,大约阅读时间需要 27 分钟。
这章需要注意一下,整个章节是这样的
所以,其实这章是个讨论的过程,最初我们给出一个链表的定义,并且实践了他的各种操作,我们会发现这个结构存在一些弊端,在做不同行为的时候,操作不统一,为了统一操作,最终我们根据需求,重新定义了链表的存储结构,统一了各项操作。
本文内容与课本此章节的顺序并不一一对应。typedef struct LDataNode{ int x; struct LDataNode *next;//结点指针}LDataNode;typedef struct LHeadNode{ LDataNode *next; //头 LDataNode *tail; //尾}LHeadNode,*LinkList;
很多同学不太理解,为什么这个存储结构要一遍一遍的改,很多个版本,其实是为了统一化操作。
如果你也不清楚什么叫统一操作,那我举个例子。 我们最终采用的这个结构,你会发现,头结点由于存放关于链表的信息,所以不存放数据,但是还有一个数据节点也没存东西,为什么不存呢,岂不是浪费了?这里就体现了统一操作的思想。 你想一下,如果第一个位置也存放数据,那么当链表为空的时候,头尾指针没地方指,所以要置为空,当来了第一个元素,链表不为空了,那头尾指针都要指向这个元素,而来了第二个元素的时候,我们只需要更新尾指针就好了,不用更新头指针了。发现了没,同样是添加元素,第一个和其他元素,操作不一样,也就意味着我们要特殊判断,而我们牺牲第一个结点,不存数据,就把操作统一化了,你就会发现,不论添加第几个元素,操作都一样。 不止这一个例子,还有别的类似的操作,总之就记住,我们优化存储结构,都是为了操作统一化。
ps:当然了,并没有规定你写链表必须写这样的结构。如果你完全理解了这几个结构的优缺点,你就可以灵活运用了,根据实际情况,选择最合适的结构。
下面我写的算法,都基于本文给出的存储结构来写!,(而非第28页的存储结构)int InitList(LinkList *head){ //初始化链表 *head =(LinkList)malloc(sizeof(LHeadNode)); LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(*head==NULL||vhNode==NULL){ return -1;//申请失败 } vhNode->next=NULL; (*head)->next=vhNode; (*head)->tail=vhNode; return 0;}
由于头结点的指针域中有了尾指针,所以使我们在链表末尾添加元素变得很容易。
int AddList(LinkList *head,int a){ //链表末尾添加元素 LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(vhNode==NULL){ return -1;//申请失败 } vhNode->x=a; vhNode->next=NULL; (*head)->tail->next=vhNode;//挂到末尾 (*head)->tail=vhNode;//尾指针更新 return 0;}
注意,遍历的时候我们传的参数,与其他操作不一样,由于遍历只用访问元素,不需要对结点做出改变,所以不需要双重指针!单重指针就够了。
int LTraverse(LinkList head){ //遍历输出链表 LDataNode *p=head->next->next;//注意 第一个数据节点不存数据 所以 第二个才是开头 while(p){ printf("%d ",p->x); p=p->next; } printf("\n"); return 0;}
/******author: 1900language: C******/#include#include #include typedef struct LDataNode{ int x; struct LDataNode *next;}LDataNode;typedef struct LHeadNode{ LDataNode *next; LDataNode *tail;}LHeadNode,*LinkList;int InitList(LinkList *head){ //初始化链表 *head =(LinkList)malloc(sizeof(LHeadNode)); LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(*head==NULL||vhNode==NULL){ return -1;//申请失败 } vhNode->next=NULL; (*head)->next=vhNode; (*head)->tail=vhNode; return 0;}int AddList(LinkList *head,int a){ //链表末尾添加元素 LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(vhNode==NULL){ return -1;//申请失败 } vhNode->x=a; vhNode->next=NULL; (*head)->tail->next=vhNode;//挂到末尾 (*head)->tail=vhNode;//尾指针更新 return 0;}int LTraverse(LinkList head){ //遍历输出链表 LDataNode *p=head->next->next; while(p){ printf("%d ",p->x); p=p->next; } printf("\n"); return 0;}//删除链表中第i个元素int DeleteList(LinkList *head,int i,int *e){ if(i<1) return -1;//不合法 int num=1; LDataNode *p=(*head)->next;//指向第0个结点 现在p->next就是第一个 while(num!=i&&p->next!=NULL){ p=p->next; num++; } if(p->next==NULL)return -1;//不合法 *e=p->next->x; p->next=p->next->next;//删除 return 0;}int main(){ LinkList head;//初始化一个链表 InitList(&head); //赋值为1-10 for(int i=1;i<=10;i++){ AddList(&head,i); } LTraverse(head);//遍历链表 int e; DeleteList(&head,3,&e);//删除第三个元素 printf("%d\n",e);//打印被删除的元素 LTraverse(head);//遍历删除后的链表}
与删除第i个元素类似,循环条件改改就行了。
无论是删除值为e的元素还是删除第i个元素,首先就是要找到它,所以,关于在链表中查找元素的算法,我就不单独写了。//删除链表中值为e的元素int DeletList_value(LinkList *head,int e){ LDataNode *p=(*head)->next;//指向第0个结点 现在p->next就是第一个 while(p->next->x!=e){ p=p->next; } if(p->next==NULL)return -1;//没找到 p->next=p->next->next;//删除 return 0;}
注意,这里的在第i个元素前面插入元素e,不包括在末尾插入元素的操作。
//第i个元素前面插入元素eint InsertList(LinkList *head,int i,int e){ if(i<1) return -1;//不合法 int num=1; LDataNode *p=(*head)->next;//指向第0个结点 现在p->next就是第一个 while(num!=i&&p->next!=NULL){ p=p->next; num++; } if(p->next==NULL)return -1;//不合法 LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(vhNode==NULL){ return -1;//申请失败 } vhNode->x=e; vhNode->next=p->next; p->next=vhNode; return 0;}
写法不唯一!
循环链表与普通链表的区别只是最后一个元素指向头结点,在判断结束的时候判断是否指向头结点。 这里给出其初始化函数。int InitList(LinkList *head){ *head =(LinkList)malloc(sizeof(LHeadNode)); LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(*head==NULL||vhNode==NULL){ return -1; } vhNode->next=(*head)->next;//最后一个元素指向开头 (*head)->next=vhNode; (*head)->tail=vhNode; return 0;}
双向链表与单链表相比,就是增加了前驱指针,方便向前寻找前驱。
下面这个例子,实现了通过前驱指针倒着遍历整个链表/******author: 1900language: C******/#include#include #include typedef struct LDataNode{ int x; struct LDataNode *next; struct LDataNode *pre;}LDataNode;typedef struct LHeadNode{ LDataNode *next; LDataNode *tail;}LHeadNode,*LinkList;int InitList(LinkList *head){ *head =(LinkList)malloc(sizeof(LHeadNode)); LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(*head==NULL||vhNode==NULL){ return -1; } vhNode->next=NULL; vhNode->pre=NULL;//首结点前驱为空 也可以置为他自己 根据需要来设置 (*head)->next=vhNode; (*head)->tail=vhNode; return 0;}int AddList(LinkList *head,int a){ //链表末尾添加元素 LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(vhNode==NULL){ return -1; } vhNode->x=a; vhNode->next=NULL; vhNode->pre=(*head)->tail;//设置前驱 (*head)->tail->next=vhNode; (*head)->tail=vhNode; return 0;}int d_LTraverse(LinkList head){ //利用前驱指针遍历链表 LDataNode *p=head->tail; while(p->pre!=NULL){ printf("%d ",p->x); p=p->pre; } printf("\n"); return 0;}int LTraverse(LinkList head){ //遍历输出链表 LDataNode *p=head->next->next; while(p){ printf("%d ",p->x); p=p->next; } printf("\n"); return 0;}int main(){ LinkList head;//初始化一个链表 InitList(&head); //赋值为1-10 for(int i=1;i<=10;i++){ AddList(&head,i); } LTraverse(head);//遍历链表 d_LTraverse(head);//利用前驱指针 遍历链表}
我们用链表表示多项式的时候,是存下每一项的系数和指数
做加法时候,指数相同,直接把系数相加就好了,指数不同不能加。 下面给一个实现例子。 用链表实现这两个多项式相加。/******author: 1900language: C需求:两个一元多项式做加法要求:每个多项式有五项,输入每一项的系数和指数,且按照指数升序输入,最终打印相加后的多项式各项系数和指数。******/#include#include #include typedef struct LDataNode{ int x;//系数 int z;//指数 struct LDataNode *next;}LDataNode;typedef struct LHeadNode{ LDataNode *next; LDataNode *tail;}LHeadNode,*LinkList;int InitList(LinkList *head){ //初始化链表 *head =(LinkList)malloc(sizeof(LHeadNode)); LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(*head==NULL||vhNode==NULL){ return -1;//申请失败 } vhNode->next=NULL; (*head)->next=vhNode; (*head)->tail=vhNode; return 0;}int AddList(LinkList *head,int a1,int a2){ //链表末尾添加元素 LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode)); if(vhNode==NULL){ return -1; } vhNode->x=a1;//系数 vhNode->z=a2;//指数 vhNode->next=NULL; (*head)->tail->next=vhNode; (*head)->tail=vhNode; return 0;}int LTraverse(LinkList head){ LDataNode *p=head->next->next; while(p){ printf("系数:%d 指数: %d\n",p->x,p->z); p=p->next; } printf("\n"); return 0;}Add_LinkList(LinkList *H1,LinkList *H2,LinkList *H3){ LDataNode *p=(*H1)->next->next; LDataNode *q=(*H2)->next->next; while(q&&p){ if(q->z==p->z){ AddList(H3,q->x+p->x,q->z); q=q->next;p=p->next; } else if(q->z>p->z){ AddList(H3,q->x,q->z); q=q->next; } else if(q->z z){ AddList(H3,p->x,p->z); p=p->next; } } while(q){ AddList(H3,q->x,q->z); q=q->next; } while(p){ AddList(H3,p->x,p->z); p=p->next; } return 0;}int main(){ LinkList H1,H2,H3; InitList(&H1); InitList(&H2); InitList(&H3); int T1=5,T2=5,a1,a2; while(T1--){ //输入第一个多项式 scanf("%d%d",&a1,&a2); AddList(&H1,a1,a2); } while(T2--){ //输入第二个多项式 scanf("%d%d",&a1,&a2); AddList(&H2,a1,a2); } //LTraverse(H1); //LTraverse(H2); Add_LinkList(&H1,&H2,&H3);//做加法运算 LTraverse(H3);//打印最终结果}
转载地址:http://evuf.baihongyu.com/