链表排序(冒泡、归并)

排序的算法很多,这里说明连接件的冒泡排序和归并排序的实现,以升序为例。
冒泡排序的原理:每一趟比较相邻两个连接件的数据部件,如果大值部件在前驱,则交换两个连接件的位置,使之成为后继,直至所有数据部件的值都按照升序排列。
归并排序的原理:递归划分连接件链表,直至最小划分,然后升序合并划分的连接件链表,直至所有的划分都被合并。
数据部件的比较通过调用回调函数的返回值来判断,回调函数有具体应用实现。
 

typedef 
int  ( * LinkSortCall)(LINKPTR plk1,LINKPTR plk2, void *  param);
/*
功能:定义连接件的排序的回调函数
参数:plk1,plk2为数据连接件指针,param为回传参数
返回:0为(plk1)=(plk2),1为(plk1)>(plk2),-1为(plk1)<(plk2)
*/

 
XDL_API 
void  BubbleSortLink(LINKPTR root,LinkSortCall pf, void *  parm);
/*
功能:对链表进行冒泡排序
参数:root为根连接件指针,pf为排序回调函数,param为回调参数
返回:无
*/

 
XDL_API 
void  MergeSortLink(LINKPTR root,LinkSortCall pf, void *  parm);
/*
功能:对链表进行归并排序
参数:root为根连接件指针,pf为排序回调函数,param为回调参数
返回:无
*/

 
void  BubbleSortLink(LINKPTR root,LinkSortCall pf, void *  parm)
{
       LINKPTR prev,next;
       
int tag = 1;
 
       assert(root 
&& root->tag == lkRoot);
       assert(pf);
 
       
while(tag)
       
{
              tag 
= 0;
              prev 
= GetFirstLink(root);
              
while(prev)
              
{
                     next 
= GetNextLink(prev);
                     
if(next == NULL)
                            
break;
 
                     
if((*pf)(prev,next,parm) > 0)
                     
{
                            SwitchLinkToNext(prev);
                            tag 
= 1;
                     }
else
                            prev 
= next;
              }

       }

}

 
// 划分连接件
int  _DivLink(LINKPTR root1,LINKPTR root2)
{
       
int count,tag;
 
       tag 
= LinkCount(root1);
       count 
= tag / 2;
       
while(count--)
       
{
              InsertLink(root2,LINK_FIRST,DeleteLink(root1,LINK_LAST));
       }

       
return tag;
}

 
// 合并连接件
void  _MrgLink(LINKPTR root1,LINKPTR root2,LinkSortCall pf, void *  parm)
{
       LINKPTR next1,next2;
       LINKPTR plk;
 
       next1 
= GetFirstLink(root1);
       next2 
= GetFirstLink(root2);
       
       
while(next1 != NULL && next2 != NULL)
       
{
              
if((*pf)(next1,next2,parm) > 0)
              
{
                     plk 
= GetNextLink(next2);
                     InsertLinkBefore(root1,next1,DeleteLink(root2,next2));
                     next2 
= plk;
              }
else
              
{
                     next1 
= GetNextLink(next1);
              }

       }

 
       
while(next2 != NULL)
       
{
              plk 
= GetNextLink(next2);
              InsertLink(root1,LINK_LAST,DeleteLink(root2,next2));
              next2 
= plk;
       }

       
}

 
void  MergeSortLink(LINKPTR root,LinkSortCall pf, void *  parm)
{
       LINK lk;
       
       InitRootLink(
&lk);
 
       
if(_DivLink(root,&lk) > 1)
       
{
              MergeSortLink(root,pf,parm);
              MergeSortLink(
&lk,pf,parm);
              _MrgLink(root,
&lk,pf,parm);
       }

}
collected by barenx
<think> 我们需要实现一个链表冒泡排序。由于链表不能像数组那样随机访问,我们需要通过指针遍历链表,并交换相邻节点的值(或者交换节点本身,但通常交换值更简单)。 步骤: 1. 确定链表的长度,以便知道需要多少轮冒泡。 2. 使用两层循环:外层循环控制轮数,内层循环进行相邻节点的比较和交换(值交换)。 3. 在内层循环中,使用两个指针遍历链表,比较相邻节点的值,如果顺序不对就交换它们的值。 注意:我们也可以选择交换节点本身,但这更复杂,因为需要修改节点的next指针以及可能涉及头节点的更新。这里我们采用交换值的方式。 假设链表节点定义如下: ```cpp struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; ``` 冒泡排序函数: 我们使用一个指向头节点的指针,然后进行排序。 但是,注意:冒泡排序在每一轮中,都会将当前未排序部分的最大值“冒泡”到末尾。在链表中,我们可以通过减少内层循环的遍历次数来优化(因为每一轮都会将当前最大值放到最后,所以下一轮就可以少遍历一个节点)。 具体步骤: 1. 计算链表长度(n)。 2. 外层循环:从0到n-1(表示n-1轮,因为最后一个元素不需要再比较)。 3. 内层循环:使用两个指针(prev和current),从头部开始,每次比较相邻的两个节点,如果prev->val > current->val,则交换它们的值。然后两个指针都向后移动一位。内层循环的终止条件是已经比较到当前轮数确定的末尾(因为每一轮末尾都会多一个已排序的元素)。 另一种方式:不使用计算长度,而是每次内层循环遍历直到一个标记的末尾(初始为nullptr,每轮结束后将末尾前移)。但这里我们使用计算长度的方法。 代码实现: ```cpp #include <iostream> using namespace std; struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; // 计算链表长度 int getLength(ListNode* head) { int len = 0; ListNode* cur = head; while (cur) { len++; cur = cur->next; } return len; } // 链表冒泡排序(交换节点值) void bubbleSort(ListNode* head) { if (head == nullptr || head->next == nullptr) return; int n = getLength(head); ListNode* cur = nullptr; for (int i = 0; i < n - 1; i++) { cur = head; for (int j = 0; j < n - i - 1; j++) { // 比较相邻两个节点的值 if (cur->val > cur->next->val) { // 交换值 int temp = cur->val; cur->val = cur->next->val; cur->next->val = temp; } cur = cur->next; } } } // 辅助函数:打印链表 void printList(ListNode* head) { ListNode* cur = head; while (cur) { cout << cur->val << " "; cur = cur->next; } cout << endl; } // 辅助函数:创建链表 ListNode* createList(int arr[], int n) { if (n == 0) return nullptr; ListNode* head = new ListNode(arr[0]); ListNode* cur = head; for (int i = 1; i < n; i++) { cur->next = new ListNode(arr[i]); cur = cur->next; } return head; } // 测试代码 int main() { int arr[] = {4, 2, 1, 3, 5}; int n = sizeof(arr) / sizeof(arr[0]); ListNode* head = createList(arr, n); cout << "Original List: "; printList(head); // 4 2 1 3 5 bubbleSort(head); cout << "Sorted List: "; printList(head); // 1 2 3 4 5 // 释放链表内存(简单起见,这里没有写释放代码,实际使用时需要释放) return 0; } ``` 注意:这个实现是原地排序,只交换节点的值,不改变节点之间的链接关系。 但是,如果链表节点很大(比如数据域很大),交换值的开销可能会比较大。另一种方法是交换节点之间的链接关系(即重新连接节点),但这会更复杂,需要记录前一个节点的前驱(因为交换两个节点需要修改它们前一个节点的next指针)。这里我们只实现了交换值的版本。 相关问题: §§相关问题§§: 1. 如何实现链表归并排序?请写出代码。 2. 在链表中,如何只交换节点(而不是节点的值)来实现冒泡排序? 3. 冒泡排序的时间复杂度是多少?在链表和数组上实现冒泡排序有什么不同? 4. 在链表中,如何优化冒泡排序以减少比较次数? </think>### 链表实现冒泡排序详解 #### 实现方法 以下是使用C++实现链表冒泡排序的完整代码。该实现通过交换节点值(而非节点链接)完成排序,保持链表结构不变: ```cpp #include <iostream> using namespace std; // 链表节点定义 struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(nullptr) {} }; // 冒泡排序函数 void bubbleSortList(ListNode* head) { if (!head || !head->next) return; // 空链表或单节点直接返回 bool swapped; ListNode* end = nullptr; // 已排序部分结束位置 do { swapped = false; ListNode* curr = head; while (curr->next != end) { if (curr->val > curr->next->val) { // 交换相邻节点值 swap(curr->val, curr->next->val); swapped = true; } curr = curr->next; // 移动到下一节点 } end = curr; // 标记已排序位置 } while (swapped); // 本轮无交换时结束 } // 创建链表 ListNode* createList(int arr[], int n) { if (n == 0) return nullptr; ListNode* head = new ListNode(arr[0]); ListNode* cur = head; for (int i = 1; i < n; i++) { cur->next = new ListNode(arr[i]); cur = cur->next; } return head; } // 打印链表 void printList(ListNode* head) { ListNode* cur = head; while (cur) { cout << cur->val << " "; cur = cur->next; } cout << endl; } // 测试代码 int main() { int arr[] = {4, 2, 1, 3, 5}; int n = sizeof(arr)/sizeof(arr[0]); ListNode* head = createList(arr, n); cout << "Original: "; printList(head); // 输出: 4 2 1 3 5 bubbleSortList(head); cout << "Sorted: "; printList(head); // 输出: 1 2 3 4 5 return 0; } ``` #### 关键实现原理 1. **双层循环控制**: - 外层循环(`do-while`)控制排序轮数 - 内层循环遍历未排序部分 - 使用`swapped`标志优化(无交换时提前终止) 2. **指针移动逻辑**: ```cpp while (curr->next != end) { // 遍历至已排序边界 if (curr->val > curr->next->val) { swap(curr->val, curr->next->val); swapped = true; } curr = curr->next; // 移动到下一节点 } end = curr; // 更新已排序边界 ``` 3. **边界处理**: - `end`指针标记已排序部分的起始位置 - 每轮结束后将`end`前移,减少比较次数 - 初始`end=nullptr`表示从链表末尾开始 #### 时间复杂度分析 | 情况 | 时间复杂度 | 说明 | |------|-------------|------| | 最优 | O(n) | 已排序链表,一轮扫描后结束 | | 最差 | O() | 完全逆序链表,需完整n-1轮 | | 平均 | O() | 随机链表 | #### 链表与数组冒泡排序对比 | 特性 | 链表实现 | 数组实现 | |------|----------|----------| | 内存访问 | 顺序访问 | 随机访问 | | 交换开销 | 仅交换值(O(1)) | 交换元素(O(1)) | | 边界控制 | 指针移动 | 索引控制 | | 适应性 | 动态数据结构 | 静态连续内存 |
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值