数据结构笔记13:小小指针,大有用处。(单链表的定义和基本操作的实现)

一、定义

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.带头结点单链表与不带头结点单链表区别

  1. 不带头节点:此时头指针指向第一个节点
      h->a1->a2->a3->……     
      头指针存放的是第一个节点的地址,即h,也就是说(*h)表示的是第一个节点
  2. 带头结点:此时头指针指向头结点
      h->headnode->a1->a2->a3->……    
      头指针存放的是头结点的地址,也就是说(*h)表示的是头结点
  3. 带头结点的优点:
    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;
}

七、总结

  1. 上述实现了单链表的插入、删除、查找、建立等基础方法
  2. 注意链表带头结点和不带头结点的区别
  3. 对指针的运用和while循环的理解
  4. 理解之后,将基础方法写在头文件里,后续深层次的算法实现,只调用封装函数即可。

老铁们,创作不易,顺便点个赞呗,可以让更多的人看到这篇文章,激励一下我这个小白。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值