单链表的算法设计

以查找为基础的算法设计

  • 按照条件进行结点查找
  • 进行插入或者删除操作

删除特定元素问题

设计一个算法,删除一个单链表L中元素值最大的结点,题目保证最大值结点是唯一的。

解:
查找最大值:用p指针遍历链表,maxp记录最大值结点
删除结点:用maxp记录最大值的前趋结点,同时再增加pre指针实现maxp和maxpre的同步移动
在这里插入图片描述

void delmaxnode( linkList *l ) {
	linkList *p = l->next, *pre = l;
	linkList *maxp = p, *maxpre = pre;
	while( p != NULL ){
		if( maxp->value < p->value ){
			maxp = p;
			maxpre = pre;
		}
		pre = p;
		p = p->next;
	}
	maxpre->next = maxp->next;
	free( maxp );
}

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

链表排序问题

有一个带头结点的非空单链表L,设计一个算法使其元素递增有序排列

解:
采用插入排序法的思想,将链表分为有序的部分和无序的部分。其中,只有一个数据结点的序列可认为有序。
在这里插入图片描述
遍历无序的序列,将元素插入到有序的序列,并根据条件选择左插还是右插。
在这里插入图片描述

void
sort( linkList *l )
{
	linkList *p, *pre, *q;
	p = l->next->next;			// p指向L的第2个数据结点
	l->next->next = NULL;		// 构造只含一个数据结点的有序表, 将L拆分成两部分
	while( p != NULL ){
		q = p->next;			// q保存*p结点后继结点的指针
		pre = l;				// 从有序表开头进行比较, pre指向插入*p的前趋结点
		while( pre->next != NULL && pre->next->next->data < p->data ){
			pre = pre->next;	//在有序表中找插入*p的前趋结点*pre
		}
		p->next = pre->next; 
		pre->next = p;
		p = q;					//扫描原单链表余下的结点
	}
}

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

以建表为基础的算法设计

链表逆置问题

假设一个带头结点的单链表L
L = ( a 1 , a 2 , . . . , a n ) L = (a_1, a_2, ...,a_n) L=(a1,a2,...,an)
设计一个算法将所有结点逆置,即
L = ( a n , a n − 1 , . . . , a 1 ) L = (a_n, a_{n-1}, ..., a_1) L=(an,an1,...,a1)
解法一:
将L的头结点单独拆分出来,采用头插法重新建表

void reverse(linkList* l) {
	linkList* p = l->next, * q;
	l->next = NULL;
	while (p != NULL) {
		q = p->next;
		p->next = l->next;
		l->next = p;
		p = q;
	}
}

解法二:
修改指针域,用三个指针指向一个结点本身和它前后的两个结点

void reverse2(linkList* l) {
	linkList* current, * rear, * front;
	rear = l->next;				//一系列的准备工作
	current = rear->next;
	rear->next = NULL;
	front = current->next;
	while (1) {
		current->next = rear;
		if (front == NULL) {
			break;
		}
		rear = current;
		current = front;
		front = front->next;
	}
	l->next = current;
}

链表拆分问题

设一个带头结点的单链表L
L = ( a 1 , b 1 , a 2 , b 2 , . . . , a n , b n ) L=(a_1, b_1,a_2,b_2,...,a_n,b_n) L=(a1,b1,a2,b2,...,an,bn)
设计一个算法将其拆分成两个带头结点的单链表L1和L2
L 1 = ( a 1 , a 2 , . . . , a n ) L 2 = ( b n , b n − 1 , . . . , b 1 ) L_1 =(a_1,a_2,...,a_n)\\ L_2=(b_n,b_{n-1},...,b_1) L1=(a1,a2,...,an)L2=(bn,bn1,...,b1)
要求L1使用L的头结点

解:
重组L的所有结点

  • L1中的相对顺序和L相同,采用尾插法建表
  • L2中的相对顺序和L中相反,采用头插法建表
void
split( linkList *l, linkList **l2 )
{
	linkList *p;
	( *l2 ) = ( linkList* )malloc( sizeof( linkList ) );
	( *l2 )->next = NULL;

	for( l = l->next; l != NULL && l->next != NULL; l = p ){
		p = l->next->next;
		l->next->next = ( *l2 )->next;
		( *l2 )->next = l->next;
		l->next = p;
	}
}

荷兰国旗问题

设有一个仅由红(0)、白(1)、蓝(2)这三种颜色的条块组成的条块序列。假设该序列采用单链表存储,设计一个时间复杂度为O(n)的算法,使得这些条块按红、白、蓝的顺序排好,即排成荷兰国旗图案。

解:
用p指针扫描结点,根据p->data值将该结点插入到3个单链表L、L1、L2(L1和L2不带头节)中,最后按L、L1、L2的顺序连接起来。
在这里插入图片描述

void
move( linkList *l )
{
	linkList *l1 = NULL, *l2 = NULL;
	linkList *r, *r1, *r2, *p;
	p = l->next;
	r = l;

	while( p != NULL ){
		if( p->value == 0 ){
			r->next = p;
			r = p;
		}else if( p->value == 1 ){
			if( l1 == NULL ){	// l1不带头节点, 要对特殊处理第一个结点
				l1 = p;
				r1 = p;
			}else{
				r1->next = p;
				r1 = p;
			}
		}else{
			if( l2 == NULL ){
				l2 = p;
				r2 = p;
			}else{
				r2->next = p;
				r2 = p;
			}
		}
		p = p->next;
	}

	r->next = r1->next = r2->next = NULL;
	r->next = l1;		//L的尾结点和L1的首结点连接起来
	r1->next = l2;		//L1的尾结点和L2的首结点连接起来
}

参考链接

https://www.icourse163.org/learn/WHU-1001539003?tid=1002049010#/learn/announce

posted @ 2020-07-08 15:51  LanceHansen  阅读(168)  评论(0编辑  收藏  举报