单链表

链表与顺序表的比较:

        (1)顺序表便于访问查询,具有随机存取特性,链表便于插入删除。

        (2)顺序表的存储密度为1,链表的存储密度小于1。

                     存储密度=节点中数据元素所占的存储量 / 节点所占存储量

初始定义结构体

typedef struct LNode
{
	int data;
	struct LNode* next;
}LinkNode;

一.建立单链表

1.头插法建立单链表 O(n)

void CreateListF(LinkNode*& L, int a[], int n)
{
	LinkNode* s;
	L = (LinkNode*)malloc(sizeof(LinkNode));
	L->next = NULL;                           //创建头节点,将其next域置为NULL
	for (int i = 0; i < n; i++)               //循环创建数据节点s,并将其插入到L中
	{
		s = (LinkNode*)malloc(sizeof(LinkNode));     //创建数据节点s
		s->data = a[i];

		//将s节点插入原首节点前,头节点后
		s->next = L->next;
		L->next = s;
	}
}

2.尾插法建立单链表 O(n)

关键:建立一个节点r,r开始时指向头节点,插入数据后一直指向尾节点。

void CreateListR(LinkNode*& L, int a[], int n)
{
	LinkNode* s, * r;
	L = (LinkNode*)malloc(sizeof(LinkNode));     //创建头节点
	r = L;                                       //r始终指向尾节点,开始时指向头节点
	for (int i = 0; i < n; i++)                  //循环建立数据节点s,并将其插入到L中
	{
		s = (LinkNode*)malloc(sizeof(LinkNode));//创建数据节点s
		s->data = a[i];                         //为s赋予数据
		r->next = s;                            //在L尾端插入s
		r = s;                                  //更新L尾端指针
	}
	r->next = NULL;                             //将尾节点的next域置为NULL
}

二.单链表的基本运算 

1.初始化单链表 O(1)

void InitList(LinkNode*& L)
{
	L = (LinkNode *)malloc(sizeof(LinkNode));
	L->next = NULL;                      //创建头节点,将其next域置为NULL
}

2.销毁线性表  O(n)

void DestroyList(LinkNode*& L)
{
	LinkNode* pre = L, * p = L->next;     //pre指向节点p的前驱节点
	while (p != NULL)                     //遍历单链表
	{
		free(pre);                        //释放pre节点
		pre = p;                          //pre、p同步后移一个节点
		p = pre->next;
	}
	free(pre);                            //循环结束时p为NULL,pre指向尾节点,释放它
}

3.判断线性表是否为空表 O(1)

bool ListEmpty(LinkNode* L)
{
	return(L->next == NULL);
}

4.求线性表长度  O(n)

int ListLength(LinkNode* L)
{
	int n = 0;
	LinkNode* p = L;               //p指向头节点,n置为0(即头结点的序号为0)
	while (p->next != NULL)
	{
		n++;
		p = p->next;
	}
	return n;                      //循环结束时,p指向尾节点,其序号n为节点的个数
}

5.输出线性表  O(n)

void DisplayList(LinkNode* L)
{
	LinkNode* p = L->next;       //p指向首节点
	while (p != NULL)
	{
		printf("%d", p->data);    //p不为NULL,就输出p节点的data域
		p = p->next;
	}
	printf("\n");
}

注意:

LinkNode* p=L             为p指向头节点

LinkNode* p=L->next     为p指向首节点 

6.按序号求链表中的元素 O(n)

bool GetElem(LinkNode* L, int i, int& e)
{
	int j = 0;
	LinkNode* p = L;                //p指向头节点,j置为0(即头结点的序号为零)
	if (i <= 0)return false;        //i错误返回假
	while (j < i && p!= NULL)
	{
		j++;
		p = p->next;
	}                              //找到第i个节点,循环结束后p可能指向第i个节点,也可能指向NULL
	if (p == NULL)
	{
		return false;
	}
	else
	{
		e = p->data;
		return true;
	}
}

7.按元素查找位置  O(n)

int LocateElem(LinkNode* L, int e)
{
	int i = 1;
	LinkNode* p = L->next;                 //p指向首节点,i置为1(即首节点的序号为1)
	while (p != NULL && p->data != e)      //查找data值为e的节点,当p的data为e或为查找到时时循环结束
	{
		p = p->next;
		i++;
	}
	if (p == NULL)
	{
		return (0);
	}
	else
	{
		return i;
	}
}

 8.插入数据元素 O(n)

bool ListInsert(LinkNode*& L, int i, int e)
{
	int j = 0;
	LinkNode* p = L, * s;           //p指向头节点,j置为0(即头节点的序号为0),s为创建要被插入元素做准备
	                                //p的作用是标记第i-1个节点的地址,j是标记元素下标,p根据j找到第i-1个节点地址。
	if (i < 0)return false;         //i错误返回false
	while (j < i - 1 && p != NULL)
	{
		j++;
		p = p->next;
	}                               //查找到第i-1个节点,查找第i-1个结点的好处是便于插入操作
									//如若查找的是第i个节点,则需在其前面插入,单链表无法指向其前方的元素
	if (p == NULL)                  //未找到第i-1个节点p,返回false
	{
		return false;
	}
	else
	{
		s = (LinkNode*)malloc(sizeof(LinkNode));
		s->data = e;                               //创建新节点s,将其data域置为e
		s->next = p->next;
		p->next = s;                               //将s节点插入到p之后
		return true;
	}
}

9.删除数据元素 O(n)

bool ListDelete(LinkNode*& L, int i)
{
	int j = 0;
	LinkNode* p = L, * q;                //p指向头节点,j置为0(头结点的序号为0)
	if (i <= 0)return false;             //i错误返回false
	while (j < i - 1 && p != NULL)       
	{
		j++;
		p = p->next;
	}                                    //查到到第i-1个节点,用p指向该节点,查找到第i-1个节点的好处是便于下面的删除操作
	q = p->next;                         //用q指向p的下一个节点
	if (p == NULL || q == NULL)          //如果p=NULL,则证明未找到i-1个节点如果q==NULL,则证明要删除的节点为NULL(不存在)
	{
		return false;
	}
	else
	{
		p->next = q->next;
		free(q);                         //删除第i个节点
		return true;
	}
}

三.单链表的应用示例 

1.有一个带头结点的单链表L=(a1,b1,a2,b2,······,an,bn),设计一个算法将其拆分成两个带头结点的单链表L1与L2,其中L1=(a1,a2,a3,·······an),L2=(bn,bn-1,······b1),要求L1使用L的头节点。

思路:整体建表法。利用原单链表L中的所有节点通过改变其指针域重组成两个单链表L1和L2。由于L1中节点的相对顺序与L中的相同,所以采用尾插法建立单链表L1;由于L2中的节点相对顺序与L中的相反,所以采用头插法建立单链表L2。

时间复杂度:O(n)

//L1使用尾插法,L2使用头插法
void split(LinkNode*& L, LinkNode*& L1, LinkNode*& L2)
{
	LinkNode* p = L->next, * q, * r1;               //p指向第一个数据节点
	L1 = L;                                         //L1利用原来的L的头节点
	r1 = L1;                                        //r1始终指向L1尾节点
	L2 = (LinkNode*)malloc(sizeof(LinkNode));       //创建L2的头节点
	L2->next = NULL;                                //置L2的指针域为NULL
	while (p != NULL)
	{
		r1->next = p;
		r1 = p;                                     //采用尾插法将p插入L1

		p = p->next;                                //p移动到下一个节点

		q = p->next;                                //q用来记录p的下一个节点

		p->next = L2->next;
		L2->next = p;                              //将p连接到L2中

		p = q;                                     //p移动到其原来下一个节点位置
	}
	r1->next = NULL;                               //L1尾节点的next域置为空
}

 2.设计一个算法,删除单链表L中元素值最大的节点

思路:双指针法。在单链表中删除一个节点首先要找到它的前驱节点,用指针p遍历整个单链表,pre指向p的前驱节点,在遍历时用maxp指向data域值最大的节点,maxpre指向max节点的前驱节点,当单链表遍历完成后,通过maxpre节点删除其后继节点。

时间复杂度:O(n)

void delmaxnode(LinkNode*& L)
{
	LinkNode* p = L->next, * pre = L, * maxp = p, * maxpre = pre;
	//建立四个节点,p用来遍历,pre用来记录p的前一个节点位置,maxp记录元素值最大的节点位置,maxpre用来记录maxp前一节点位置

	while (p != NULL)//遍历整个单链表
	{
		if (maxp->data < p->data)
		{
			maxp = p; //标记元素最大值的位置
			maxpre = pre;
		}
		pre = p;   //推进遍历过程
		p = p->next;
	}
	//删除该元素
	maxpre->next = maxp->next;
	free(maxp);
}

3.有一个带头结点的单链表L(至少有一个数据节点),设计一个算法实现所有数据节点按data域递增排序。

思路:直接插入法。由于单链表L中有一个以上的数据节点,首先构造一个只含头节点和首节点的有序单链表(只含一个数据节点的单链表一定是有序的)。然后遍历单链表L余下的节点(由p指向),在有序单链表中通过比较找插入节点p的前驱节点(由pre指向它),在pre结点之后插入p节点,直到p==NULL为止。

时间复杂度:O(n*n)

void sort(LinkNode*& L)
{
	LinkNode* p, * pre, * q;
	p = L->next->next;
	L->next->next = NULL;
	while (p != NULL)
	{
		q = p->next;         //用q记录p下一个结点的位置
		pre = L;             //pre找寻L中比p的data域值小的节点的前驱地址

		while (pre->next != NULL && pre->next->data < p->data)//寻找过程
		{
			pre = pre->next;
		}
		//找到后插入
		p->next = pre->next;
		pre->next = p;
		//p回到其下一个位置
		p = q;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值