单链表的定义和基本操作的实现
一、定义
1.定义单链表
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
2.初始化单链表
①带头结点
bool InitList1(LinkList& L) {
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL)
{
return false;
}
L->next = NULL;
return true;
}
②不带头结点
bool InitList2(LinkList& L) {
L = NULL;
return true;
}
3.单链表判空
①带头结点
bool Empty1(LinkList L) {
if (L->next == NULL) {
return true;
}
else
{
return false;
}
}
②不带头结点
bool Empty2(LinkList L) {
if (L==NULL)
{
return true;
}
else
{
return false;
}
}
4.带头结点单链表与不带头结点单链表区别
- 不带头节点:此时头指针指向第一个节点
h->a1->a2->a3->……
头指针存放的是第一个节点的地址,即h,也就是说(*h)表示的是第一个节点 - 带头结点:此时头指针指向头结点
h->headnode->a1->a2->a3->……
头指针存放的是头结点的地址,也就是说(*h)表示的是头结点 - 带头结点的优点:
1、更快删除/插入第一个结点
2、统一空表和非空表的处理
二、插入
1.按位序插入
①带头结点
平均时间复杂度O(n)
//在第i个位置插入元素e
typedef int Elemtype ;
bool ListInsert(LinkList& L, int i, Elemtype e) {
if (i < 1) {
return false;
}
LNode* p;
int j = 0;//表示当前p指向的是第几个结点
p = L;
while (p != NULL && j < i - 1) {//循环找到第i-1个结点
p = p->next;
j++;
}
if (p=NULL)
{
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
②不带头结点
不带头结点的表插入数据时需要对i=1的时候特殊处理
//在第i个位置插入元素e
typedef int Elemtype ;
bool ListInsert(LinkList& L, int i, Elemtype e) {
if (i < 1) {
return false;
}
if (i == 1)
{
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L = s;
return true;
}
LNode* p;
int j = 1;//表示当前p指向的是第几个结点
p = L;
while (p != NULL && j < i - 1) {//循环找到第i-1个结点
p = p->next;
j++;
}
if (p = NULL)
{
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
2.指定结点的后插元素操作
在p结点之后插入元素e
时间复杂度O(1)
bool InsertNextElem(LNode *p, Elemtype e){
if (p==NULL)
{
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode));
if (s==NULL)
{
return false;
}
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
3.指定结点的前插元素操作
在p结点之前插入元素e
时间复杂度O(1)
bool InsertPriorElem(LNode* p, Elemtype e) {
if (p == NULL)
{
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode));
if (s == NULL)
{
return false;
}
s->next = p->next;
p->next = s;
s->data = p->data;
p->data = e;
return true;
}
4.指定结点前插入结点操作
将s结点插入到p之前
时间复杂度O(1)
bool InsertPriorNode(LNode *p, LNode *s) {
if (p==NULL || s==NULL)
{
return false;
}
s->next = p->next;
p->next = s;
Elemtype e = p->data;
p->data= s->data;
p->data = e;
return true;
}
三、删除
1.按位序删除
按位序删除(带头结点)
时间复杂度O(n)
删除第i个元素的值,用e带回此值
bool ListDelElem(LinkList& L, int i, Elemtype e) {
if (i < 1) {
return false;
}
LNode* p;
int j = 0;//表示当前p指向的是第几个结点
p = L;
while (p != NULL && j < i - 1) {//循环找到第i-1个结点
p = p->next;
j++;
}
if (p = NULL)
{
return false;
}
if (p->next==NULL)
{
return false;
}
LNode* q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
2.指定结点的删除
bool DelNode(LNode* p) {
if (p==NULL)
{
return false;
}
LNode* q = p->next;
p->data = p->next->data;
p->next = q->next;
free(q);
return true;
}
注意:
如果p恰好是最后一个节点,则此法失效,因为p->next=NULL。
这时应另寻它法,给定头指针。找到p的前一个元素,进行删除操作。
四、查找
1.按位查找
时间复杂度O(n)
LNode* GetElem(LinkList L, int i) {
if (i<0)
{
return NULL;
}
LNode* p;
int j = 0;
p = L;//L先指向头结点(第0个结点,不存数据)
while (p!=NULL&&j<i)
{
p = p->next;
j++;
}
return p;
}
注意:
顺序表中按位查找是随机访问,时间复杂度为O(1)
2.按值查找
时间复杂度O(n)
LNode* LocateElem(LinkList L, int e) {
LNode* p = L->next;
while (p!=NULL&&p->data!=e)//从第一个节点开始查找data域为e的点
{
p = p->next;
}
return p;//存在e值返回结点指针,不存在返回NULL
}
五、求链表长度
时间复杂度O(n)
int length(LinkList L) {
int len = 0;
LNode* p = L;
while (p->next!=NULL)
{
p = p->next;
len++;
}
return len;
}
六、单链表的建立
1.头插法
将输入的数据插入到链表的表头,读入数据的顺序与生成链表中的元素相反,L->next=NULL,防止它指向空间中的脏数据。
时间复杂度O(n)
重要应用:链表的原地逆置
LinkList List_HeadInsert(LinkList& L) {
LNode* s;
int x;
L = (LinkList)malloc(sizeof(LNode));//创建头结点
L->next = NULL;//初始化链表
scanf_s("%d", &x);
while (x != 00) {//输入00结束
s= (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf_s("%d", &x);
}
return L;
}
2.尾插法
将输入的数据插入到链表的表尾,读入数据的顺序与生成链表中的元素相同
时间复杂度O(n)
**注意:**必须增加一个尾指针,使其始终指向当前链表的尾结点,便于下一个数的插入。
LinkList List_TailInsert(LinkList& L) {
int x;
L = (LinkList)malloc(sizeof(LNode));//创建头结点
LNode* s, * r = L;
scanf_s("%d", &x);
while (x != 00) {//输入00结束
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;//r指向新的表尾结点
scanf_s("%d", &x);
}
r->next = NULL;//尾结点指针置空
return L;
}
七、总结
- 上述实现了单链表的插入、删除、查找、建立等基础方法
- 注意链表带头结点和不带头结点的区别
- 对指针的运用和while循环的理解
- 理解之后,将基础方法写在头文件里,后续深层次的算法实现,只调用封装函数即可。
…
老铁们,创作不易,顺便点个赞呗,可以让更多的人看到这篇文章,激励一下我这个小白。