代码基本来源:2020王道数据结构(侵删)
每个结点除了存放数据元素外,还要存储指向下一个结点的指针
- 优点:不要求大片连续空间,改变容量方便
- 缺点:不可随机存取,要耗费一定空间存放指针
LNode * 与 LinkList 本质上都是指向LNode类型的指针
typedef struct LNode{//定义单链表结点类型ElemType data;//数据域 每个结点存放一个数据元素struct LNode *next;//指针域 指向下一个结点
}LNode, *LinkList;
- 第一个结点存储数据(第一个结点即为链表位序为1的结点)
防止脏数据(指向莫名其妙的区域)
bool InitList(LinkList &L){L = NULL;//空表,暂时还没有任何结点return L;
}
bool IsEmpty(LinkList L){return (L == NULL);/* 等价于下面的代码if (L == NULL)return true;else return false;*/
}
bool ListInsert(LinkList &L,int i, ElemType e){if (i<1)return false;//插入位置非法if (i == 1){//插入第一个结点的操作与其他结点操作不同LNode *t = (LNode *)malloc(sizeof(LNode));t->data = e;//填充新结点t->next = L->next;L = t;//头指针指向新结点return ture;}LNode *p;//p指向当前扫描到的结点p = L;//p指向当前的头结点,认为是第0个结点int j = 1;//当前p指向的是第j个结点while ( (p != NULL) && (j < i-1)){//循环找到第i-1个结点p = p->next;++j;} if (p == NULL) //找不到第i-1个结点return false;//找到第i-1个结点,插入结点LNode *t = (LNode *)malloc(sizeof(LNode));t->data = e;//填充新结点t->next = p->next;p->next = t;return ture;//插入成功
}
- 头结点不存储数据(链表第一个结点不存储数据)
- 头指针指向的结点的下一个结点才开始存放数据
bool InitList(LinkList &L){L = (LNode *)malloc(sizeof(LNode));//分配一个头结点if (L == NULL) //内存不足,分配失败return false;L->next = NULL;//头结点之后暂时还没有结点return true;
}
bool IsEmpty(LinkList L){if (L->next == NULL)return true;else return false;
}
bool ListInsert(LinkList &L,int i, ElemType e){if (i<1)return false;//插入位置非法//下面这段代码相当于查找第i-1个结点的操作//LNode *p = GetElem(L,i-1)LNode *p;//p指向当前扫描到的结点p = L;//p指向当前的头结点,认为是第0个结点int j = 0;//当前p指向的是第j个结点while ( (p != NULL) && (j < i-1)){//循环找到第i-1个结点p = p->next;++j;}//下面的操作相当于后插操作 可调用后插函数实现// return InsertNextNode(p,e)if (p == NULL) //找不到第i-1个结点return false;//找到第i-1个结点,插入结点LNode *t = (LNode *)malloc(sizeof(LNode));t->data = e;//填充新结点t->next = p->next;p->next = t;return ture;//插入成功
}
- 对于单链表,给定结点之后的区域是可知区域,而前面的区域是不可知区域
- 给点结点的前插操作和后插操作不相同
bool InsertNextNode(LNode *p,ElemType e){if (p == NULL)//边界条件判断return false;LNode *t = (LNode *)malloc(sizeof(LNode));if ( s == NULL)//内存不足,分配失败return false;t->data = e;//填充结点t->next = p->next;p->next = t;//将结点t连到p之后return true;
}
给定结点的前插操作(给定插入的数据)
bool InsertPriorNode(LNode *p,ElemType e){if (p == NULL)//边界条件判断return false;LNode *t = (LNode *)malloc(sizeof(LNode));if ( s == NULL)//内存不足,分配失败return false;t->data = p-data;//将p中元素复制到t中p->data = e;//p中元素覆盖为et->next = p->next;p->next = t;//将结点t连到p之后return true;
}
删除表L中第i个位置的元素,并用e返回删除元素的值
bool ListDelete(LinkList &L, int i,ElemType &e){if (i<1)return false;//下面这段代码相当于查找第i-1个结点的操作//LNode *p = GetElem(L,i-1)LNode *p;//指针p指向当前扫描到的结点int j = 0;//当前p指向的是第几个结点p = L;//L指向头结点,头结点假设是第0个结点(不存数据)while( p != NULL && j<i-1 ){//寻找第i-1个结点p = p->next;++j;}if (p == NULL)//找不到第i-1个结点return false;if (p->next == NULL)//第i-1个结点后已无其他结点(删除第i个位置的结点非法)return false;LNode t = p->next;//临时存放所需删除的结点e = t->data;//引用 需返回e的值p->next = t->next;//将所需删除结点在链表中断开free(t);//释放结点的存储空间
}
- 思路1:传出头指针,循环寻找p的前驱结点(O(n))
- 思路2:偷天换日(类似于结点前插)
- 删除指定结点无法就是修改当前结点的前驱结点指向当前结点的后继结点
- 关键点
- 将当前结点的后继结点的数据覆盖到当前结点上,然后再修改当前结点的指向
- 有坑:不要忘记判断p->next是否为NULL(指点结点为表尾)(每一次使用指针的时候都要想一下是否为NULL)————这种情况只能从头开始依次寻找p的前驱结点O(n)
bool DeleteNode(LNode *p){if ( p == NULL)return false;LNode *q = p->next;//指向当前结点p的后继结点if (q == NULL){//删除结点在表尾Find}p->data = p->next->data;//(和后续结点交换数据域)将当前结点p的下一结点的数据覆盖到当前结点p->next = q->next;//将*q所指向结点从链表断开free(q);//释放后继结点的存储空间return true;
}
按位查找:获取表L中指向第I个位置结点的指针
- 成功:返回指向该结点的指针
- 失败(注意):返回NULL
- 查找位置不合法(i<0)
- i = 0 返回指向头结点的指针
- i = 1,2,… 返回指向数据结点的指针
- 查找位置不合法(查找位置超出链表长度)
//按位查找 带头结点(第0个结点)
LNode *GetElem(LinkList L,int i){if (i<0)return NULL;LNode *p;//指针p指向当前扫描到的结点int j = 0;//当前p指向的是第j个结点p = L;//指针p指向头结点,头结点是第j个结点(不存数据)while(p != NULL && j < i){//循环找到第j个结点p = p->next;++j;}return p;
}
按值查找:在表L中查找具有给定关键字值的元素
//按值查找,找到数据域==e的结点
LNode * LocateElem(LinkList L,ElemType){LNode *p = L->next;//从第一个结点开始查找数据域为e的结点while( p != NULL && p-data != e){p = p->next;}return p;//调用程序需判断是否为NULL
}
思路:遍历链表时加一个计数器
//求表的长度
int Length(LinkList L){int len = 0;LNode *p = L-next;//p指向链表的第一个结点while (p != NULL){++len;p = p->next;}return len;
}
- 初始化一个单链表
- 每次取一个数据元素,插入到表尾/表头
综上:利用后插的思想,每次将结点插到尾指针所指结点之后(最终方法)
LinkList List_TailInsert(LinkList &L){int x;//设ElemType为整形LNode *s,*r = L;//s指向临时插入的新结点,r为表尾指针scanf("%d",&x);//输入结点的值while( x != -1 ){//表示结束的值s = (LNode *)malloc(sizeof(LNode));s->data = x;r->next = s;r = s;//指向新的表尾结点scanf("%d",&x);//读入新的数据}r->next = NULL;//尾结点指针置空return L;//返回表头指针
}
LinkList List_TailInsert(LinkList &L){int x;//设ElemType为整形scanf("%d",&x);//输入结点的值while( x != -1 ){//表示结束的值InsertNextNode(&L,x);scanf("%d",&x);//读入新的数据}return L;//返回表头指针
}
- 双链表可进可退
- 存储密度更低一点(需要存放指向前驱结点的指针)
typedef struct DNote{//定义双链表结点类型ElemType daa;//数据域struct DNode *prior,*next;//前驱和后驱指针
}DNote,*DLinkList;
bool InitDLinkList{DLinkList &L}{L = (DNode *)malloc(sizeof(DNode));//分配一个头结点if (L == NULL)//内存不足,分配失败return false;L->prior = NULL;//头结点的prior结点永远指向NULLL->next = NULL;//头结点之后暂时还没有结点return ture;
}
bool IsEmpty{DLinkList L}{if (L->next == NULL)//内存不足,分配失败return false;elsereturn false;
}
bool InsertNextNode{DNode *p,DNode *s}{if (p == NULL || s == NULL)//非法参数return false;s->next = p->next;if (p->next != NULL)//如果p结点有后继结点p->next->prior = s;p-next = s;s->prior = p;return false;
}
bool DeleteNextNode(DNode *p){if (p == NULL)return false;DNode *q = p->next;//指向p的后继结点qif (p->next == NULL)return false;p->next = q->next;if (q->next != NULL)//q结点不是最后一个结点q->next->prior = p;free(q);//释放结点空间return true;
}
void DestoryList(DLinkList &L){//循环释放各个数据结点while(L->next != NULL)DeleteNextNode(L);free(L);//释放头结点L = NULL;//头指针指向NULL
}
while (p->prior != NULL){//对结点p做相应处理p = p->prior;
}
bool InitList(LinkList &L){L = (LNode *)malloc(sizeof(LNode));//分配一个结点if (L == NULL)//内存不足,分配失败return false;L->next = L;//头结点next指向头结点return true;
}
bool IsEmpty(LinkList L){if (L->next == L)//单手抱紧自己return true;else return false;
}
bool IsTail(LinkList L, LNode *p){if (p->next == L)return true;else return false;
}
bool InitList(DLinkList &L){L = (LNode *)malloc(sizeof(LNode));//分配一个结点if (L == NULL)//内存不足,分配失败return false;L->next = L;//头结点next指向头结点L->prior = L;//头结点prior指向头结点return true;
}
bool IsEmpty(DLinkList L){if (L->next == L)//单手抱紧自己return true;else return false;
}
bool IsTail(DLinkList L, LNode *p){if (p->next == L)return true;else return false;
}
bool InsertNextNode{DNode *p,DNode *s}{if (p == NULL || s == NULL)//非法参数return false;s->next = p->next;p->next->prior = s;p-next = s;s->prior = p;return false;
}
bool DeleteNextNode(DNode *p){if (p == NULL)return false;DNode *q = p->next;//指向p的后继结点qp->next = q->next;q->next->prior = p;free(q);//释放结点空间return true;
}
- 分配一整片连续的内存空间,各个结点集中安置
- 利用数组的方式实现链表的功能(结构数组)
#define MaxSize 10//静态链表的最大长度
struct Node{ElemType data;//存储数据元素int next;//下一个元素的数组下标
};
#define MaxSize 10//静态链表的最大长度
typedef struct {//静态链表结构类型的定义ElemType data;//存储数据元素int next;//下一个元素的数组下标
}SLinkList[MaxSize];/* 等价以下代码
#define MaxSize 10//静态链表的最大长度
struct Node{//静态链表结构类型的定义ElemType data;//存储数据元素int next;//下一个元素的数组下标
};//SLinkList 定义 “一个长度为MaxSize 的Node 型数组”
typedef struct Node SlinkList[MaxSize];*/
#define MaxSize 10//静态链表的最大长度
typedef struct {//静态链表结构类型的定义ElemType data;//存储数据元素int next;//下一个元素的数组下标
}SLinkList[MaxSize];/* 等价以下代码
#define MaxSize 10//静态链表的最大长度
struct Node{//静态链表结构类型的定义ElemType data;//存储数据元素int next;//下一个元素的数组下标
};//SLinkList 定义 “一个长度为MaxSize 的Node 型数组”
typedef struct Node SlinkList[MaxSize];*/
本文发布于:2024-01-27 16:59:41,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063460031516.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |