单链表
通过一组任意的存储单元来存储线性表中的数据元素。
常用操作
Empty();判断单链表是否为空
InitLinkList();单链表的初始化 带头结点
LinkInsert();给定位序后插入结点
InsertNextNode();在指定结点(p)后插入新的节点s,其值为e
InsertPrior();给定节点前插入新节点
ListDelete();按位序删除结点(带头结点)
DeleteNode();删除指定的结点
GetElem(L);给定需要查找的位置,在单链表里进行查找
结构体定义的两种方式在代码里面
class Linklist
{
//定义一个ElemType类型的数据类型
typedef int ElemType;
//1.定义一个单链表节点类型
typedef struct LNode { //Typedef关键字--<数据类型><别名>
ElemType data; //定义单链表节点的数据域
struct LNode *next; //定义单链表的指针域
}LNode, *LinkList; //用Linklist指向struct LNode的指针 L表示这是一个指向第一个单链表的指针
/*//2.第二种定义结构体的方式
typedef struct LNode { //Typedef关键字--<数据类型><别名>
ElemType data; //定义单链表节点的数据域
struct LNode *next; //定义单链表的指针域
}LNode;
typedef struct LNode Lnode;
typedef struct LNode *Linklist;
*/
//判断单链表是否为空
bool Empty(LinkList L) {
if (NULL)
{
return false;
}
else
{
return true;
}
}
/*//单链表的初始化 不带头结点
LinkList InitLinkList(LinkList &L) {
struct LNode *L = NULL; //空表暂时还没有数据,防止脏数据 不可以给L赋int类型的值
return L;
}*/
//单链表的初始化 带头结点
bool InitLinkList(LinkList &L) {
LNode * L;
LinkList L;
LNode *L = ( LNode *)malloc(sizeof( LNode));
Empty(L);
L->next = NULL;
return true;
}
bool LinkInsert(LinkList &L, int i, ElemType e) {
if (i < 1)
return false;
LNode *p; //指针p指向当前扫描到的结点
int j = 0; //当前p指向的结点的位序
p = L; //将L的节点位置给P的结点位置,头结点是第0个结点
while (p != NULL && j < i - 1) { //循环找到第i-1个结点 找到插入的节点的位置
p = p->next;
j++;
}
if (p==NULL) //判断结点是否为空如果为空则返回空值
{
return false;
}
return InsertNextNode(p,e); //下方写的在指定结点的后插操作
}
bool InsertNextNode(LNode *p,ElemType e) { //在指定结点(p)后插入新的节点s,其值为e
if (p == NULL) {
return false;
}
LNode *s = (LNode *)malloc(sizeof(LNode));
if (s==NULL) //内存分配失败
{
return false;
}
s = p;
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
/*1.第一种实现方式 将头指针也传入进去通过循环找到 要插入的p结点的前驱结点的i位置,再用按位插入的方式
bool InsertPrior(LinkList L, LNode *p, ElemType e) {
}
*/
//2. 第二种实现方式 在指定结点中插入一个新结点 然后将前后两个结点的值互换位置
bool InsertPrior(LNode *p, ElemType e) {
if (p == NULL) {
return false;
}
LNode *s = (LNode *)malloc(sizeof(LNode));
if (s == NULL) //内存分配失败
{
return false;
}
s->data = p->data;
p->data = e;
s->next = p->next;
p->next = s;
return true;
}
//按位序删除结点(带头结点)
bool ListDelete(LinkList L,int i,ElemType &e) {
if (i<1)
{
return false;
}
LNode *p;
int j = 0;
p = L;
while (p!= NULL&&j<i-1)
{
p = p->next;
j++;
}
if (p->next==NULL||p==NULL)
{
return false;
}
LNode *q = p->next;
e = q->data;
p->next = q->next;
free(q); //如果q指针不释放 那么则会造成内存泄漏 成为野指针 且值会存在
return true;
}
bool DeleteNode(LNode *p) { //删除指定的结点 令删除结点后面结点的值直接给目标结点,再去删除后面结点,则可完成要求
//如指定删除后面的结点为NULL,则无法实现操作。
LNode *q = p->next; //此实现方式时间复杂度为O(1)
p->data = p->next->data;
p->next = q->next;
free(q);
return true;
}
bool GetElem(LinkList L ,int i) { //给定需要查找的位置,在单链表里进行查找 返回0或1;
int j=0;
if (i<1 ) //判断输入的位序是否合法
{
return NULL;
}
LNode *p;
p = L;
while (p!=NULL&&j<i) //循环查找所给位点;
{
p = p->next;
j++;
return true;
}
return p;
}
LNode* LocateElem(LinkList L,ElemType e){ //按值查找
LNode *p = L->next;
while (p!=NULL&&p->data!=e)
{
p = p->next;
}
return p;
}
int Length(LinkList L) { //统计表长
int len = 0;
LNode *p = L->next;
while (p ->next= NULL)
{
p = p->next;
len++;
}
return len;
}
};
感想
1.按位查找
注意与顺序表对比
单链表不具备随机扫描的特性,只能依次扫描
2.按值查找
3.求单链表的长度
4.key
三种基础操作的时间复杂度都是O(n)
如何循环扫描各个结点的代码逻辑
注意边界条件的处理