1.题目: 单链表的排序
描述
给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0 < n \le 1000000<n≤100000
要求:空间复杂度 O(n),时间复杂度 O(nlogn)
2.算法:
1.暴力算法 .数组赋值的方法
2.归并排序(推荐使用) (因为时间复杂度 和空间复杂度 最小)
3.算法思想:
1.暴力算法 .数组赋值的方法
把链表里面的 值 存取 动态数组 vector 里面 再排序 最后赋值)
2.归并排序(推荐使用) (因为时间复杂度 和空间复杂度 最小)
思路:
前面我们做合并两个有序链表不是使用归并思想吗?说明在链表中归并排序也不是不可能使用,合并阶段可以参照前面这道题,两个链表逐渐取最小的元素就可以了,但是划分阶段呢?
常规数组的归并排序是分治思想,即将数组从中间个元素开始划分,然后将划分后的子数组作为一个要排序的数组,再将排好序的两个子数组合并成一个完整的有序数组,因此采用的是递归。而链表中我们也可以用同样的方式,只需要找到中间个元素的前一个节点,将其断开,就可以将链表分成两个子链表,然后继续划分,直到最小,然后往上依次合并。
终止条件: 当子链表划分到为空或者只剩一个节点时,不再继续划分,往上合并。
返回值: 每次返回两个排好序且合并好的子链表。
本级任务: 找到这个链表的中间节点,从前面断开,分为左右两个子链表,进入子问题排序。
怎么找中间元素呢?我们也可以使用快慢双指针,快指针每次两步,慢指针每次一步,那么快指针到达链表尾的时候,慢指针正好走了快指针距离的一半,为中间元素。
具体做法:
step 1:首先判断链表为空或者只有一个元素,直接就是有序的。
step 2:准备三个指针,快指针right每次走两步,慢指针mid每次走一步,前序指针left每次跟在mid前一个位置。三个指针遍历链表,当快指针到达链表尾部的时候,慢指针mid刚好走了链表的一半,正好是中间位置。
step 3:从left位置将链表断开,刚好分成两个子问题开始递归。
step 4:将子问题得到的链表合并,参考合并两个有序链表。
4.代码:
/*************************************************
作者:She001
时间:2022/9/30
题目: 单链表的排序
描述
给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0 < n \le 1000000<n≤100000
要求:空间复杂度 O(n),时间复杂度 O(nlogn)
***************************************************/
//算法:
//1.暴力算法 .数组赋值的方法
// 2.归并排序(推荐使用) (因为时间复杂度 和空间复杂度 最小)
#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
int i;
node *next;
};
void print(node * head)//打印链表
{
node* pp= head;//复制头节点
while(pp!=NULL)//判断这个节点是否为空 链表是否结束
{
cout<<pp->i<<" ";
pp=pp->next;//指向下一个
}
cout<<endl;
}
int lianbiao_num(node * head)//函数的作用 返回链表的个数
{
int i=0;
node* pp=head;
while(pp!=NULL)
{
i++;
pp=pp->next;
}
//cout<<"链表中节点的个数: "<<i<<endl;
return i;
}
node * reverseList(node* head)//翻转链表
{
if(head==NULL)
{
return NULL;
}
node * a = head;
node * b = NULL;
while(a!=NULL)
{
node * c = a->next;
a->next=b;
b=a;
a=c;
}
return b;
}
//暴力算法
//算法思想:(把链表里面的 值 存取 动态数组 vector 里面 再排序 最后赋值)
node * fangfa_1(node * head1)//实现的方法
{
vector<int> nums;
node* a1=head1;
//遍历节点 ,将节点的值加入动态数组
while(a1!=NULL)
{
nums.push_back(a1->i);//把值 放入动态数组
a1=a1->next;
}
a1=head1;
//数组排序
sort(nums.begin(),nums.end());//参数1 动态数组的开头 参数二 动态数组的 结尾
//遍历数组,赋值
int i=0;
while(a1!=NULL)
{
a1->i=nums[i++];//赋值
a1=a1->next;//指向下一个节点
}
return head1;//返回头节点
}
//归并排序(推荐使用) (因为时间复杂度 和空间复杂度 最小)
/*
算法思想:
思路:
前面我们做合并两个有序链表不是使用归并思想吗?说明在链表中归并排序也不是不可能使用,合并阶段可以参照前面这道题,两个链表逐渐取最小的元素就可以了,但是划分阶段呢?
常规数组的归并排序是分治思想,即将数组从中间个元素开始划分,然后将划分后的子数组作为一个要排序的数组,再将排好序的两个子数组合并成一个完整的有序数组,因此采用的是递归。而链表中我们也可以用同样的方式,只需要找到中间个元素的前一个节点,将其断开,就可以将链表分成两个子链表,然后继续划分,直到最小,然后往上依次合并。
终止条件: 当子链表划分到为空或者只剩一个节点时,不再继续划分,往上合并。
返回值: 每次返回两个排好序且合并好的子链表。
本级任务: 找到这个链表的中间节点,从前面断开,分为左右两个子链表,进入子问题排序。
怎么找中间元素呢?我们也可以使用快慢双指针,快指针每次两步,慢指针每次一步,那么快指针到达链表尾的时候,慢指针正好走了快指针距离的一半,为中间元素。
具体做法:
step 1:首先判断链表为空或者只有一个元素,直接就是有序的。
step 2:准备三个指针,快指针right每次走两步,慢指针mid每次走一步,前序指针left每次跟在mid前一个位置。三个指针遍历链表,当快指针到达链表尾部的时候,慢指针mid刚好走了链表的一半,正好是中间位置。
step 3:从left位置将链表断开,刚好分成两个子问题开始递归。
step 4:将子问题得到的链表合并,参考合并两个有序链表。
*/
//合并两段有序链表
node* merge(node* pHead1, node* pHead2) {
//一个已经为空了,直接返回另一个
if(pHead1 == NULL)
return pHead2;
if(pHead2 == NULL)
return pHead1;
//加一个表头
node* head = new node;
node* cur = head;
//两个链表都要不为空
while(pHead1 && pHead2){
//取较小值的节点
if(pHead1->i <= pHead2->i){
cur->next = pHead1;
//只移动取值的指针
pHead1 = pHead1->next;
}else{
cur->next = pHead2;
//只移动取值的指针
pHead2 = pHead2->next;
}
//指针后移
cur = cur->next;
}
//哪个链表还有剩,直接连在后面
if(pHead1)
cur->next = pHead1;
else
cur->next = pHead2;
//返回值去掉表头
return head->next;
}
node* fangfa_2(node* head) //排序链表
{
//链表为空或者只有一个元素,直接就是有序的
if(head == NULL || head->next == NULL) //预防传入的下一个排序 的两个头节点有错误! 保证有每个链表有一个元素
return head;
node* left = head;
node* mid = head->next;
node* right = head->next->next;
//右边的指针到达末尾时,中间的指针指向该段链表的中间
while(right != NULL && right->next != NULL) //分为两个部分 平分
{
left = left->next;
mid = mid->next;
right = right->next->next;
}
//左边指针指向左段的左右一个节点,从这里断开
left->next = NULL;
//分成两段排序,合并排好序的两段
return merge(fangfa_2(head), fangfa_2(mid));
}
int main()
{
//建立 第一个 单链表
node *a1=new node;
node *a2=new node;
node *a3=new node;
node *a4=new node;
node *a5=new node;
node *a6=new node;
node *a7=new node;
node *a8=new node;
node *a9=new node;
a1->i=2;//链表节点的复制
a2->i=1;
a3->i=4;
a4->i=3;
a5->i=7;
a6->i=8;
a7->i=9;
a8->i=6;
a9->i=5;
a1->next=a2;//链表的连接
a2->next=a3;
a3->next=a4;
a4->next=a5;
a5->next=a6;
a6->next=a7;
a7->next=a8;
a8->next=a9;//a5 是 两个链表的节点
a9->next=NULL;
//node* gg= fangfa_1(a1);
//print(gg);
node* gg1= fangfa_2(a1);
print(gg1);
return 0;
}