小猪的数据结构学习笔记(三)
线性表之单链表
本章引言:
上一节中我们见识了第一个数据结构——线性表中的顺序表;
当你把操作的代码自己写几遍就会有点感觉了,如果现在让你写顺序表的
插入算法,你能够想出大概的代码么?如果可以,那么你就可以进入新的章节了;
否则,还是回头看看吧!在本节,我们将迎来线性表的链式表示——单链表
单链表和顺序表有什么优势和劣势呢?单链表的头插法和尾插法有什么不同呢?
请大家跟随笔者的脚步来解析线性表中的单链表把!
路线图解析:
①先要理解顺序表和单链表各自的优点和缺点,考试常考的地方!
②熟记单链表的形式,理解头指针,头结点,尾结点,数据域与指针域!
③熟记单链表的结点结构代码
④区分头指针域头结点!
⑤两种方式创建单链表:头插法和尾插法
⑥单链表的12个基本操作
⑦有趣的算法::如何获得单链表中位置排中间的结点?
正文:
ps:最前面的代表头指针
代码:
<span style="font-family:Microsoft YaHei;">void HeadCreateList(LinkList &L,int n) { LinkList p; int i; //初始化随机数种子,为头指针分配内存空间,并且指针域设置为空 srand(time(0)); L = (LinkList)malloc(sizeof(LNode)); L->next = NULL; //利用循环,随机生成n个数字并且存储到单链表中: for(i = 0;i < n;i++) { //生成新的结点 p = (LNode)malloc(sizeof(LNode)); //生成两位数随机数就是100,三位就是1000,以此类推 p -> data = rand()%100 + 1; p -> next = L - > next; L -> next = p; } } </span>
代码步骤解析:
①初始化头结点,并且让头指针指向头结点,同时让头结点的指针域设置为NULL;
②构造单链表的结点,这里用的是随机生成,当然你也可以自己scanf!
③构造完后,让当前结点的指针域指向L指向的下一个结点
④将头指针指向当前结点
PS:因为每次都是将新节点插入到链表尾部,所以需要加入一个指针R用于始终指向标的尾结点,以便
新节点插入到链表的尾部
代码
<span style="font-family:Microsoft YaHei;">void TailCraeteList(LinkList &L,int n) { LinkList p,r; int i; srand(time(0)); L = (LinkList)malloc(sizeof(LNode)); L -> next = NULL; r = L; for(i = 0;i < n;i++) { L = (LNode)malloc(sizeof(LNode)); p -> data = rand()%100 + 1; //这里要注意,p是中介结点,当前尾结点的next指向p r -> next = p; //r一直指向尾结点,此时p才是尾结点,所以吧p的值赋值给r r = p; } r -> next = NULL; }</span>
代码步骤解析:
①构造一个头结点,让头指针指向头结点,同时把头指针的值赋值给指针R(R一直指向尾结点)
②随机生成n个随机数并且构造结点,同时将r的指针域指向p
③因为现在出现新的尾结点,中介变量p,所以r要再指向p,从而保证p一直指向为节点
④将为节点的指针域设置为NULL
如何获得单链表中位置排中间的结点?
分析:
访问单链表中的元素只能从头到尾一个个地挪动,貌似只有一个方法了,就是在记录单链表的长度了,
然后再除以2得出第几项为中间结点!假如我们不知道长度呢?有没有其他方法呢?答案肯定是有得,
这里提供一个简单的方法:用两个不同的指针,按照不同的移动顺序,暂且称为快慢指针,每次循环
①快指针每次挪动2个节点:p = p->next->next ②慢指针每次挪动1个节点:q = q -> next;
当快指针到达尾部时,此时慢指针刚好指向中间结点!完成!
代码:
<span style="font-family:Microsoft YaHei;">Status GetMidLNode(LinkList L,ElemType &e) { LinkList p,q; p = q = L; while(p -> next -> next != NULL) { if(p -> next -> next != NULL) { p = p -> next -> next; q = q -> next; } else { p = p -> next; } } e = q -> data; return OK; } </span>
代码就不解释了,慢慢理解下把!
=====================================================================
====分割线,上面的是本节的重点和难点,基本操作都很简单,实在有兴趣的再往下看吧=====
=====================================================================
<span style="font-family:Microsoft YaHei;">typedef struct LNode { ElemType data; //数据域 struct Lnode *next; //指针域 }LNode; typedef struct LNode *LinkList; //定义两个是方便使用 </span>
<span style="font-family:Microsoft YaHei;">void InitList(LinkList &L) { L = (LinkList)malloc(sizeof(LNode)); if(!L)exit(ERROR); L -> next = NULL; } </span>
<span style="font-family:Microsoft YaHei;">void ClearList(LinkList &L) { LinkList p = L -> next; L -> next = NULL; //接着就是销毁除头结点外的结点了 while(p) { q = L -> next; //释放首元节点 free(L); //移动到当前的首元节点 L = q; } }</span>
有两种情况:
有头结点: L -> next = NULL,此时表为空表
无头结点: L = NULL ,此时L为头指针
<span style="font-family:Microsoft YaHei;">Status ListEmpty(LinkList L) { //只需要判断头结点的指针域是否为空即可 if(L -> next)return False; else return TRUE; }</span>
<span style="font-family:Microsoft YaHei;">void Destory(LinkList &L) { LinkList q; while(L) { //除了头结点外的结点都删除 //指向首元节点,不是头结点 q = L -> next; free(L); //删除后,重新指向首元 L = q; } }</span>
<span style="font-family:Microsoft YaHei;">int ListLength(LinkList L) { int i = 0; LinkList p = L -> next; //指向首元节点,非头结点 while(p) { i++; p = p -> next; } return i; } </span>
<span style="font-family:Microsoft YaHei;">Status GetElem(LinkList L,int i,ElemType &e) { int j = 1; //指向首元,然后依次后移 //如果到了结尾或者j的值大于i还没找到说明i不合法 LinkList p = L -> next; while(p&&j < i) { j++; p = p->next; } if(!p || j > i)return ERROR; e = p ->data; return OK; }</span>
<span style="font-family:Microsoft YaHei;">int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) { int i = 0; LinkList p = L -> next; while(p) { i++; if(compare(p->data,e))return i; p = p -> next; } return 0; } </span>
<span style="font-family:Microsoft YaHei;">Status BeforeElem(LinkList L,ElemType choose,ElemType &before) { LinkList q,p = L -> next; while(p -> next) { q = p -> next; if(q ->data == choose) { before = p -> data; return OK; } //如果p的后继不为choose,向后移 } return ERROR; }</span>
<span style="font-family:Microsoft YaHei;">Status NextElem(LinkList L,ElemType choose,ElemType &behind) { LinkList p = L -> next; while(p -> next) { if(p ->data == choose) { behind = p -> next -> data; return OK; } p = p -> next; } return ERROR; } </span>
<span style="font-family:Microsoft YaHei;">Status ListInsert(ListList L,int i,ElemType) { int j = 0; LinkList p,q = L; while(q && j < i - 1) { j++; q = q -> next; } if(!p && j > i - 1)return ERROR; p = (LinkList)malloc(sizeof(LNode)); //要先让插入的结点的指针域指向插入位置的后继结点 //再让插入节点的前驱的指针域指向插入结点 //!!!顺序不能乱哦1 p -> data = e; p -> next = q -> next; q -> next = p; return OK; }</span>
<span style="font-family:Microsoft YaHei;">Status ListDelete(LinkList L,int i,ElemType &e) { int j = 0; LinkList p,q = L; while(q -> next && j < i - 1) { j++; q = q -> next; } //判断输入的位置是否合理 if(!q -> next|| j > i - 1) return ERROR; //指向准备删除的结点 p = q -> next; //删除结点的前驱的指针域指向删除结点的后继 q -> next = p ->next; e = p -> data; //释放要删除的结点 free(p); return OK; } </span>
<span style="font-family:Microsoft YaHei;">void ListTraverser(LinkList L,void(*visit)(ElemType)) { LinkList p = L -> next; while(p) { visit(p -> data); p = p -> next; } printf("\n"); }</span>
相关代码下载:
原文地址:http://blog.csdn.net/coder_pig/article/details/38229011