小猪的数据结构学习笔记(三)
线性表之单链表
本章引言:
上一节中我们见识了第一个数据结构——线性表中的顺序表;
当你把操作的代码自己写几遍就会有点感觉了,如果现在让你写顺序表的
插入算法,你能够想出大概的代码么?如果可以,那么你就可以进入新的章节了;
否则,还是回头看看吧!在本节,我们将迎来线性表的链式表示——单链表
单链表和顺序表有什么优势和劣势呢?单链表的头插法和尾插法有什么不同呢?
请大家跟随笔者的脚步来解析线性表中的单链表把!
路线图解析:
①先要理解顺序表和单链表各自的优点和缺点,考试常考的地方!
②熟记单链表的形式,理解头指针,头结点,尾结点,数据域与指针域!
③熟记单链表的结点结构代码
④区分头指针域头结点!
⑤两种方式创建单链表:头插法和尾插法
⑥单链表的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