Bootstrap

【408DS算法题】022进阶-递增输出单链表中的结点值

题目

给定单链表的头结点,按照递增的顺序,输出单链表结点的值。


分析实现

对于单链表,很多操作与顺序表有很大的区别,而各种排序算法也都是针对顺序表的操作的。因此,比较简单的一个思路就算先将单链表转为顺序表,再使用顺序表的排序算法进行排序后输出。

具体实现如下:

#include <iostream>
#include <vector>
#include <algorithm>

void print_list(LNode* head) {
	LNode* cur = head->next;
	vector<int> a;
	// 遍历链表,将结点值存入数组
	while(cur){
		a.push_back(cur->val);
		cur = cur->next;
	}
	// 使用内置快速排序函数进行排序
	sort(a.begin(), a.end());
	// 输出排序后的结点值
	for(int x: a){
		cout << x << " ";
	}
}

不难分析得到,以上实现的 时间复杂度为 O ( n log ⁡ n ) O(n\log{n}) O(nlogn),空间复杂度为 O ( n ) O(n) O(n)

而倘若需要实现空间复杂度为 O ( 1 ) O(1) O(1)的算法,就需要换一种思路。常数的空间复杂度无法存储所有的结点数据,因此只能用链表来实现排序算法。

而考虑到链表不向顺序表一样具有随机存取的特性,并不能实现像是快速排序和归并排序这样高效的算法,但仍可以实现冒泡排序、选择排序、插入排序这类操作朴素的算法。此处选用较易实现的选择排序进行输出

具体实现如下:

#include <iostream>

// 链表实现选择排序
void print_list(LNode* head) {
	LNode *first=head->next;
	LNode *pre, *cur;
	LNode *minN, *minPre;
	while(first){
		cur=first->next, pre=first;
		minN=first, minPre=head;
		while(cur){
			// 更新维护最小值结点指针及其前驱
			if(cur->val < minN->val){
				minN=cur;
				minPre=pre;
			}
			pre=cur;
			cur=cur->next;
		}
		// 输出最小值
		cout<<minN->val<<' ';
		
		// 删除最小值结点
		minPre->next=minN->next;
		delete minN;
		
		// 更新first指针
		first = head->next;
	}
}

这样,以上实现的代码的 时间复杂度增加到了 O ( n 2 ) O(n^2) O(n2),空间复杂度降低到了 O ( 1 ) O(1) O(1)

注意本文的实现中只对选中的最小结点执行了删除操作,并没有将其调整为链表的首部,如需调整则应新增一个指针,并在循环中进行维护使其指向first的前驱结点,具体实现欢迎在评论区讨论(^^ゞ


总结

以上就是递增输出链表中的结点值的两种实现。

如果将链表的结点值存储到顺序表中,可以使用高效的快速排序在 O ( n log ⁡ n ) O(n\log{n}) O(nlogn)的时间复杂度下完成排序并输出;
如果只能使用常数的额外空间,也可以用单链表实现选择排序在 O ( n 2 ) O(n^2) O(n2)的时间复杂度下完成结点值的递增输出。

;