单链表中结点删除、排序与反转

57 篇文章 1 订阅
52 篇文章 2 订阅

    在单链表中,假定每个结点的类型用 L i n k L i s t LinkList LinkList表示,它应包括存储元素的数据域,这里用 d a t a data data表示,其类型用通用类型标识符 E l e m T y p e ElemType ElemType表示,还包括存储元素位置的指针域,这里用next表示。 L i n k L i s t LinkList LinkList类型的定义如下:

typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
	ElemType data;
	struct LNode *next; //指向后继结点的指针
	LNode(ElemType x) : data(x), next(NULL) {}
}LinkList;

    这里的单链表,通常指带头结点的单链表。

问题一:删去单链表 L L L中结点 x x x的直接前驱。

    思路:在扫描时用 r r r指向值为 x x x的结点,用 q q q指向 ∗ r *r r结点直接前驱结点,用 p p p指向 ∗ q *q q结点的直接前驱结点,若未找到或r指向第一个结点,则返回 0 0 0,否则删除 ∗ q *q q结点,并返回1。算法如下:

//删除x的直接前驱结点
int delxPre(LinkList *&L,ElemType x){
	LinkList *p=L,*q=p->next,*r;
	if(q!=NULL) r=q->next;
	else return 0;
	while (r!=NULL && r->data!=x)
	{
		p=q;
		q=r;
		r=r->next;
	}
	if (r!=NULL)
	{
		p->next = q->next;
		free(q);
	}
	else
		return 0;
	return 1;

}
问题二:删除单链表中的最小值结点。

    思路:用 ∗ p *p p从头到尾扫描单链表, p r e pre pre指向 ∗ p *p p结点的前驱,用 m i n p minp minp保存值最小的结点指针, m i n p r e minpre minpre指向 ∗ m i n p *minp minp结点的前驱。一边扫描,一边比较,将最小值的结点放到 ∗ m i n p *minp minp中,算法如下:

//删除单链表L中的最小值
void delMinNode(LinkList *&L){
	LinkList *pre=L,*p=pre->next,*minp=p,*minpre=pre;
	while(p!=NULL){ //查找最小值结点*minp及其前驱结点*minpre
		if (p->data < minp->data)
		{
			minp=p;
			minpre=pre;
		}
		pre=p;
		p=p->next;
	}
	minpre->next = minp->next; //删除*minp结点
	free(minp);

}
问题三:已知单链表 L L L(带头结点)是一个递增有序表,设计一个高效算法,删除表中data值在大于或等于max之间的结点(若表中有这样的结点), 同时释放被删结点的空间,这里min和max是两个给定的参数,并分析它的时间复杂度。

    思路:由于单链表 L L L是一个递增有序表,首先找到值域刚好大于min的结点 ∗ p *p p, 再找到值域刚好大于max的结点 ∗ q *q q, 删去 ∗ p *p p结点到 ∗ q *q q结点之前的所有结点并释放它们占用的资源。算法如下:

//删除有序表L中元素值在min~max之间的元素,
//这里的min和max只是给定的两个参数,不表示最大值或最小值
void delMinToMax(LinkList *&L,ElemType min,ElemType max){
	LinkList *p=L->next,*q,*r=L;
	while(p!=NULL && p->data < min){ //p指向第一个大于等于min的结点
		r=p;                      //r为*p的前驱结点
		p=p->next;
	}
	q=p;
	while(q!=NULL && q->data <= max) //q指向第一个大于max的结点
		q = q->next;
	printf("p:%d q:%d\n",p->data,q->data); //删除*p到*q之间的所有结点
	r->next=q;
	r=p->next;
	while(r!=q){ //释放被删除结点的空间
		free(p);
		p=r; r=r->next;
	}

}

    该算法有3个while语句,但最多只扫描单链表一次,其时间复杂度为 O ( 1 ) O(1) O(1)

问题四:已知单链表 L L L(带头结点)是一个非有序表,设计一个高效算法,删除表中data值在大于或等于max之间的结点(若表中有这样的结点), 同时释放被删结点的空间,这里min和max是两个给定的参数,并分析它的时间复杂度。

    思路:用 q q q扫描单链表 L L L p p p指向它的直接前驱结点,若 ∗ q *q q结点满足被删条件,则删除它并将 q q q下移一个结点,否则 p 、 q p、q pq同时下移一个结点。算法如下:

//删除非有序表L中元素值在min~max之间的元素,
//这里的min和max只是给定的两个参数,不表示最大值或最小值
void delMinToMax2(LinkList *&L, ElemType min, ElemType max){
	LinkList *p = L, *q = p->next, *r;
	while (q != NULL)
	{ //*q为被删结点,*p是它的前驱
		if (q->data >= min && q->data <= max)
		{
			r = q->next;        //将r指向q的后继
			p->next = q->next;  //将p的next也指向q的后继
			free(q);            //释放q
			q = r;              //q后移
		}
		else{
			p = q;              //p后移
			q = q->next;        //q也后移
		}
	}

}

    该算法中用 q q q指针扫描整个单链表,所以时间复杂度为 O ( n ) O(n) O(n)

问题五:有一个递增单链表(允许出现值相同的结点),设计一个算法删除值域重复的结点,并分析它的时间复杂度。

    思路:由于是有序表,所以相同值域的结点都是相邻的。用 p p p扫描递增单链表,若 ∗ p *p p结点的值域等于其后结点的值域,则删除后者,算法如下:

//删除递增有序表L中重复的结点
void delSame(LinkList *&L){
	LinkList *p=L->next,*q;
	while (p->next!=NULL)
	{
		if (p->data == p->next->data) //找到重复值的结点
		{
			q=p->next;       //q指向这个重复值的结点
			p->next=q->next; //删除*q结点
			free(q);
		}
		else p=p->next; 
	}

}

    该算法的时间复杂度为 O ( n ) O(n) O(n)

问题六:有一个非有序单链表(允许出现值相同的结点),设计一个算法删除值域重复的结点,并分析它的时间复杂度。

    思路:用 p p p扫描单链表,用 r r r扫描 ∗ p *p p结点之后的所有结点, q q q始终指向 ∗ r *r r结点的直接前驱结点,若 r − > d a t a = = p − > d a t a r->data ==p->data r>data==p>data, 则删除 ∗ r *r r结点,否则 q 、 r q、r qr同时下移一个结点,算法如下:

//删除非有序表L中重复的结点
void delSame2(LinkList *&L){
	LinkList *p=L->next,*q,*r,*t;
	while (p!=NULL)
	{
		q=p;
		r=q->next;
		while (r!=NULL)
		{
			if (r->data == p->data) //r指向被删除结点
			{
				t=r->next;
				q->next=t;
				free(r);
				r=t;
			} 
			else
			{
				q=r;
				r=r->next;
			}
		}
		p=p->next;
	}

}

    该算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

问题七:设计一个算法判定单链表 L L L(带头结点)是否递增。

    思路:判定链表 L L L从第二个结点开始的每个结点的值是否比其前驱的值大。若有一个不成立,则整个链表不是递增的,否则是递增的,算法如下:

//判断一个单链表是否递增
int isAscend(LinkList *L){
	LinkList *p=L->next,*q; //p指向第一个结点
	if (p!=NULL)
	{
		while(p->next != NULL)
		{
			q=p->next;  //q指向p的后继
			if(q->data >= p->data) 
				p=q;
			else
				return 0;
		}
	}
	return 1;
}

问题八:有一个带头结点的单链表 L L L, 其 E l e m T y p e ElemType ElemType类型为int, 设计一个算法使其元素递增有序。

    思路:若原单链表中有一个或以上的数据结点,则先构造只含一个数据结点的有序表(只含一个数据结点的单链表一定是有序表)。扫描原单链表余下的结点 ∗ p *p p(直到 p = = N U L L p==NULL p==NULL为止),在有序表中通过比较,找到插入 ∗ p *p p的前驱结点 ∗ q *q q, 然后将 ∗ p *p p插入到 ∗ q *q q之后(这里实际上采用的是直接插入排序方法),算法如下:

//对单链表L从小到大排序
void Sort(LinkList *&L){
	LinkList *p=L->next,*q,*r;
	if (p!=NULL)
	{
		r=p->next;
		p->next=NULL;
		p=r;
		while(p!=NULL){
			r=p->next;
			q=L;
			while(q->next!=NULL && q->next->data < p->data)
				q=q->next;
			p->next=q->next;
			q->next=p;
			p=r;
		}

	}

}
问题九:有一个线性表 ( a 1 , a 2 , ⋯   , a n ) (a_1,a_2,\cdots,a_n) (a1,a2,,an), 采用带头结点的单链表 L L L存储,设计一个算法将其就地逆置。所谓“就地”指算法的辅助空间为 O ( 1 ) O(1) O(1)

    思路:用 p p p指针扫描原单链表,先将头结点 L L L n e x t next next域置为 N U L L NULL NULL而变成一个空链表,然后将 ∗ p *p p结点采用头插法插入到 L L L中,算法如下:

void Reverse(LinkList *&L){
	LinkList *p=L->next,*q;
	L->next=NULL;
	while (p!=NULL)
	{
		q=p->next;
		p->next=L->next; //将*p结点插入到新建链表的前面
		L->next=p;
		p=q;
	}

}

    效果如下:

图(1) 删除元素5的直接前驱结点
图(2) 删除最小元素
图(3) 删除有序表中一段区域之间的元素
图(4) 删除非有序表中一段区域之间的元素
图(5) 有序表中删除重复元素
图(6) 非有序表中删除重复元素
图(7) 判定是否为递增
图(8) 对单链表进行排序(从小到大)
图(9) 将单链表反转

完整代码如下:

#include <stdio.h>
#include <malloc.h>
#include <iostream>

typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
	ElemType data;
	struct LNode *next; //指向后继结点的指针
	LNode(ElemType x) : data(x), next(NULL) {}
}LinkList;

int ListInsert(LinkList *&L, int i, ElemType e){
	int j = 0;
	LinkList *p = L, *s;
	while (j<i - 1 && p != NULL)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
		return 0;
	else{
		s = (LinkList *)malloc(sizeof(LinkList));
		s->data = e;
		s->next = p->next;
		p->next = s;
		return 1;
	}

}

void DispList(LinkList *L){
	LinkList *p = L->next;
	while (p != NULL){
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");

}

//删除x的直接前驱结点
int delxPre(LinkList *&L, ElemType x){
	LinkList *p = L, *q = p->next, *r;
	if (q != NULL) r = q->next;
	else return 0;
	while (r != NULL && r->data != x)
	{
		p = q;
		q = r;
		r = r->next;
	}
	if (r != NULL)
	{
		p->next = q->next;
		free(q);
	}
	else
		return 0;
	return 1;

}


//删除有序表L中元素值在min~max之间的元素,
//这里的min和max只是给定的两个参数,不表示最大值或最小值
void delMinToMax(LinkList *&L, ElemType min, ElemType max){
	LinkList *p = L->next, *q, *r = L;
	while (p != NULL && p->data < min){ //p指向第一个大于等于min的结点
		r = p;                      //r为*p的前驱结点
		p = p->next;
	}
	q = p;
	while (q != NULL && q->data <= max) //q指向第一个大于max的结点
		q = q->next;
	printf("p:%d q:%d\n", p->data, q->data); //删除*p到*q之间的所有结点
	r->next = q;
	r = p->next;
	while (r != q){ //释放被删除结点的空间
		free(p);
		p = r; r = r->next;
	}

}

//删除非有序表L中元素值在min~max之间的元素,
//这里的min和max只是给定的两个参数,不表示最大值或最小值
void delMinToMax2(LinkList *&L, ElemType min, ElemType max){
	LinkList *p = L, *q = p->next, *r;
	while (q != NULL)
	{ //*q为被删结点,*p是它的前驱
		if (q->data >= min && q->data <= max)
		{
			r = q->next;
			p->next = q->next;
			free(q);
			q = r;
		}
		else{
			p = q;
			q = q->next;
		}
	}

}

//删除递增有序表L中重复的结点
void delSame(LinkList *&L){
	LinkList *p = L->next, *q;
	while (p->next != NULL)
	{
		if (p->data == p->next->data) //找到重复值的结点
		{
			q = p->next;       //q指向这个重复值的结点
			p->next = q->next; //删除*q结点
			free(q);
		}
		else p = p->next;
	}

}

//删除非有序表L中重复的结点
void delSame2(LinkList *&L){
	LinkList *p = L->next, *q, *r, *t;
	while (p != NULL)
	{
		q = p;
		r = q->next;
		while (r != NULL)
		{
			if (r->data == p->data) //r指向被删除结点
			{
				t = r->next;
				q->next = t;
				free(r);
				r = t;
			}
			else
			{
				q = r;
				r = r->next;
			}
		}
		p = p->next;
	}

}

//判断一个单链表是否递增
int isAscend(LinkList *L){
	LinkList *p = L->next, *q; //p指向第一个结点
	if (p != NULL)
	{
		while (p->next != NULL)
		{
			q = p->next;  //q指向p的后继
			if (q->data >= p->data)
				p = q;
			else
				return 0;
		}
	}
	return 1;
}

//对单链表L从小到大排序
void Sort(LinkList *&L){
	LinkList *p = L->next, *q, *r;
	if (p != NULL)
	{
		r = p->next;
		p->next = NULL;
		p = r;
		while (p != NULL){
			r = p->next;
			q = L;
			while (q->next != NULL && q->next->data < p->data)
				q = q->next;
			p->next = q->next;
			q->next = p;
			p = r;
		}

	}

}

//删除单链表L中的最小值
void delMinNode(LinkList *&L){
	LinkList *pre = L, *p = pre->next, *minp = p, *minpre = pre;
	while (p != NULL){ //查找最小值结点*minp及其前驱结点*minpre
		if (p->data < minp->data)
		{
			minp = p;
			minpre = pre;
		}
		pre = p;
		p = p->next;
	}
	minpre->next = minp->next; //删除*minp结点
	free(minp);

}

//将L逆置
void Reverse(LinkList *&L){
	LinkList *p = L->next, *q;
	L->next = NULL;
	while (p != NULL)
	{
		q = p->next;
		p->next = L->next; //将*p结点插入到新建链表的前面
		L->next = p;
		p = q;
	}

}


int main()
{
	LinkList *Ls = (LinkList*)malloc(sizeof(LinkList));
	Ls->next = NULL;
	int i = 0;
	// 	for (i=1;i<10;i++)
	// 	{
	// 		ListInsert(Ls,i,i*i-1);
	// 	}

	ListInsert(Ls, 1, 2);
	ListInsert(Ls, 2, 7);
	ListInsert(Ls, 3, 5);
	ListInsert(Ls, 4, 4);
	ListInsert(Ls, 5, 23);
	ListInsert(Ls, 6, 18);
	ListInsert(Ls, 7, 30);
	ListInsert(Ls, 8, 80);
	ListInsert(Ls, 9, 63);

	// 	ListInsert(Ls,1,2);
	// 	ListInsert(Ls,2,7);
	// 	ListInsert(Ls,3,5);
	// 	ListInsert(Ls,4,5);
	// 	ListInsert(Ls,5,5);
	// 	ListInsert(Ls,6,18);
	// 	ListInsert(Ls,7,40);
	// 	ListInsert(Ls,8,40);
	// 	ListInsert(Ls,9,80);


	// 	ListInsert(Ls,1,7);
	// 	ListInsert(Ls,2,3);
	// 	ListInsert(Ls,3,30);
	// 	ListInsert(Ls,4,30);
	// 	ListInsert(Ls,5,30);
	// 	ListInsert(Ls,6,18);
	// 	ListInsert(Ls,7,45);
	// 	ListInsert(Ls,8,45);
	// 	ListInsert(Ls,9,63);


	printf("原线性表: \n");
	DispList(Ls);

	//删除元素15的直接前驱结点
	delxPre(Ls,5);
	printf("删除元素5的直接前驱结: \n");
	DispList(Ls);

	//删除有序表10到65之间元素
	//printf("删除后:\n");
	//delMinToMax(Ls,10,65);

	//删除非有序10到65之间的元素
	//delMinToMax2(Ls,10,65);

	//delSame(Ls);
	//delSame2(Ls);

	// 	if (isAscend(Ls))
	// 	{
	// 		printf("L为升序!\n");
	// 	}
	// 	else
	// 		printf("L不为升序!\n");

	//printf("排序后:\n");
	//Sort(Ls);
	//printf("删除最小值之后: \n");
	//delMinNode(Ls);

	//printf("反转后:\n");
	//Reverse(Ls);

	//DispList(Ls);


	system("pause");
	return 0;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值