单链表的算法设计
以查找为基础的算法设计
- 按照条件进行结点查找
- 进行插入或者删除操作
删除特定元素问题
设计一个算法,删除一个单链表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,an−1,...,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,bn−1,...,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