排序的链表实现

目录

朴素直接插入排序

冒泡的链表排序

完整的测试代码


朴素直接插入排序

本节将使用链表来实现直接插入排序。原理跟数组排序一致,就是细节上可能有差异。可能跟网上的有差异,仅供参考。

void Insertsort(List *&h){    //从小到大排序
	List *r,*s;		//前进结点 ,r为待排的首结点、已排的末结点。s为待交换节点
	int i=0;
	List *q,*p; 	//已排好的前进结点 
	r = h->next;    
	s = r->next;    //比较两个前进节点的大小
	int flag = 0;    //判断是否经过了交换
	while (r->next){
		i++;	//计算已排数量 
		if (s->data < r->data){		//若后一个比前一个小 则说明需要交换 
			q = h;	//从头开始比较 
			p = q->next;    //仅为了方便 也可以不写
			for (int j = 0; j < i; ++j){	//寻找交换的位置 
				if (s->data < p->data){	//判断插入位置,比当前最大的小那么就应该在这里					 
					r->next = s->next;    //先让已排的末结点链接到待排的首结点防止断链
                                         //关键句子 就是这个害我调了好久的bug
					s->next = q->next;    //接下来就是头插法
					q->next = s;
					flag = 1;    //另一个关键(bug)点,如果为1的话就说明r不需要前进
                                //让他继续与待排的首结点比较。否则r就可以前进
					break;    //减少循环次数,因为每次只需排一个
				}else{    //已排的前进结点
					q = q->next;    
					p = q->next;
				}
			}
		}
		if (flag == 1){    //这个不能放在上面这个if前 否则需要修改代码,不想调试了😭
                           //不加flag判断的话,r就会一直前进,错过结点,我记得就会死循环好像
			s = r->next;
			flag = 0;      //每次记得重置flag!
		}else{
			r = r->next;	
			s = r->next;	//比较r->next和r->next->next的data
			flag = 0;
		} 	
	}
} 

测试结果:

 、 、 

算法总结:这个基本完全就是按照数组中的排序规则进行排序的,没有经过优化,所以是“朴素”排序,另外链表注意不能断链!!,以及判断符号要及时重置


冒泡的链表排序

源自一个学长,加了一些注释

void BubbleSort(LNode *head){
	LNode *q,*p, *tail;
	int i = 0;
	tail = NULL;
	while (head->next->next != tail){
		p = head;
		q = head->next;
		while (q->next != tail){
			if (q->data > q->next->data){
				p->next = q->next;	
				q->next = q->next->next;
				p->next->next = q;	//上面三步就是交换了结点顺序 手画一下就清楚了 
				q = p->next;	//使其重新回到p后,为了后续前进操作保持一致
							   //如 1532 修改完后p在3上,但if结束后,两者都进一即p是3;q是5
							  //一开始我是想让p=p->next,直接让它是冒泡交换后的大值 但这样子会跳过好多结点 
			}
			p = p->next;
			q = q->next;
		} 
		tail = q;	//如果没有发生交换 则会退出循环 
	}
}

测试结果:

算法总结:q = q->next 是关键操作!!!详细在注释中,画画图理解一下


完整的测试代码

直接把里面的方法换一下即可

#include <stdio.h>
#define MAXSIZE 10

typedef struct Sqlist{
	int data[MAXSIZE];
	int length;
}Sqlist;

void createListR(LNode *&list, int S[], int n){			//尾插法 
	LNode *q,*p;
	list = (LNode*)malloc(sizeof(LNode));
	list->next = NULL;
	p = list;
	for (int i=0; i<n; i++){
		q = (LNode*)malloc(sizeof(LNode));
		q->data = S[i];
		p->next = q;
		p = p->next;
	} 
	p->next = NULL;
}
void createListF(LNode *&list, int S[], int n){			//头插法 
	LNode *r;
	list = (LNode*)malloc(sizeof(LNode));
	list->next = NULL;
	r = list;
	for (int i=0; i<n; ++i){
		r = (LNode*)malloc(sizeof(LNode));
		r->data = S[i];
		r->next = list->next;
		list->next = r;
	}
}

void showLinkList(LNode *list){
	LNode *s;
	s = list->next;
	while (s){
		printf("%d ", s->data);
		s = s->next;
	}
}
/*
    此处加入上述代码即可
*/
int main(){
	printf("相信自己!加油!");
	printf("\n");

	int a[10] = {1, 2, 99, 5, 4, 7, 8};
	LNode *A;
	createListR(A, a, 7);

	showLinkList(A); 
	printf("\n"); 
	BubbleSort(A);  //替换函数名
	showLinkList(A); 
	return 1;
}

 

<think>好的,用户想了解如何用归并排序链表进行排序实现方法。首先,我需要回忆一下归并排序的基本原理。归并排序是分治法的典型应用,分为分割和合并两个阶段。对于数组来说,分割需要找到中点,但链表无法随机访问,所以必须用快慢指针法来找中点。这一点我记得没错,引用[1]提到链表更适合归并排序,因为其他算法如快速排序链表上性能不佳,所以用户的问题应该围绕如何分割链表并合并。 接下来,合并两个已排序链表的步骤。这部分应该和数组类似,但链表需要调整指针而不是创建新数组。需要确保合并后的链表正确连接每个节点。引用[4]提到能否在O(1)空间内实现,所以可能要考虑自底向上的方法,避免递归调用带来的栈空间,但用户的问题可能更关注基本实现,先处理递归方法。 然后,具体的实现步骤。分割链表时,快慢指针找到中间节点,然后断开链表为前后两部分。合并时,使用哑节点来简化头节点的处理。需要处理剩余节点,比如当一边链表还有元素时,直接接在合并链表的后面。这部分需要注意指针操作的正确性,避免循环引用或者断链。 代码部分,用户可能需要Python的实现。但Python的链表操作通常用类表示节点,所以需要先定义节点结构。然后实现找到中间节点的函数,分割链表,合并函数,以及递归调用的归并排序函数。需要检查递归终止条件,比如链表为空或只有一个节点时直接返回。 验证正确性方面,考虑不同长度的链表,比如奇数个节点和偶数个节点的情况,确保分割正确。合并过程中,测试两个链表的不同排列,比如一个全小于另一个,或者交替大小的情况。还要注意处理链表的末尾节点的next指针设为None,避免出现环。 引用中提到的问题,比如空间复杂度。递归的归并排序由于递归调用栈,空间复杂度是O(log n),而自底向上的方法可以达到O(1)空间。但用户的问题可能只需要基本实现,所以先提供递归版本,再在相关问题中提到优化方法,比如引用[4]的问题。 最后,确保回答符合用户的要求,包括分步骤解释、代码示例和相关问题。同时引用相关文献,比如引用[1]指出归并排序更适合链表,引用[4]提到时间复杂度和空间的要求。确保所有数学公式用$...$格式,如时间复杂度O(n log n)。</think>### 归并排序实现链表排序方法 归并排序链表的理想排序算法,因为它天然适应链表的顺序访问特性,避免了随机访问带来的性能损失[^1]。以下是分步实现方法: --- #### 步骤1:定义链表节点结构 ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next ``` --- #### 步骤2:分割链表(分治法的核心) 使用**快慢指针法**找到中点并断开链表: ```python def split(head): if not head or not head.next: return head, None # 快指针每次走两步,慢指针每次走一步 slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next # 断开链表为前后两半 mid = slow.next slow.next = None return head, mid # 返回前半段和后半段的头节点 ``` --- #### 步骤3:合并两个有序链表 ```python def merge(l1, l2): dummy = ListNode(-1) # 哑节点简化边界处理 curr = dummy while l1 and l2: if l1.val <= l2.val: curr.next = l1 l1 = l1.next else: curr.next = l2 l2 = l2.next curr = curr.next # 处理剩余节点 curr.next = l1 if l1 else l2 return dummy.next ``` --- #### 步骤4:递归完成归并排序 ```python def merge_sort(head): if not head or not head.next: # 递归终止条件 return head # 分割链表 left, right = split(head) # 递归排序链表 left_sorted = merge_sort(left) right_sorted = merge_sort(right) # 合并排序后的子链表 return merge(left_sorted, right_sorted) ``` --- ### 算法特性 1. **时间复杂度**:$O(n \log n)$,分割和合并各需要$\log n$层操作 2. **空间复杂度**:递归栈空间$O(\log n)$,合并过程仅修改指针无需额外空间[^4] 3. **稳定性**:当元素相等时保持原有顺序 --- ### 关键点解析 1. **快慢指针分割**:避免链表随机访问的劣势,时间复杂度$O(n)$ 2. **哑节点技巧**:简化链表头节点处理的边界条件 3. **原地修改指针**:合并时直接调整节点指针,空间复杂度优于数组归并排序[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值