排序还是最基础的。
链表的排序总共知道三种,插入、快排、归并。。经过分析之后惊奇的发现链表的快排是稳定的🤣
快排的思想,partition的时候分为三种情况,左 中 右 三段新链表。新数据小于base,接在左边的后面,大于base,接在右边的后边,否则接在中间的后边。
注意选基准值为第一个,第一个作为mid的开头即为稳定排序。
插排的思想,遍历listNode,一个个重新插入即可,插入的时候插在大于自己的位置左边。即为稳定排序,插排适合基本有序或者数据较短的情况。
归并的思想,快慢结点找到中间位置,断开成两段,归并一下即可。
数据的排序就不发了,资料都比较全了我写的也不好。放在最后两个展开的吧,一个是归并不递归,一个是快排不递归,都是python写的。
主要排序应用场景:
冒泡:排行榜
插排:基本有序,小数据
希尔排序:就是间隔从大到小在变的插排。适合当base排序,适合各种情况。速度不行再换别的,算是工具人(排序)了。
归并:各种程度上的稳定排序,空间复杂度O(n)算是比较高了,注意最好不要递归里频繁的开辅助空间,最好事先开好一个O(n)的辅助空间。最好是不递归的更快一点。速度比快排慢。
快排:快,O(1)辅助空间,随机基准位置+三路排序优化,实际中C++的标准sort是快排+插排,所以是不稳定的,但速度比快排的qsort快。想稳定用stable_sort,stable_sort是归并排序。这里产生了疑问,为什么stable_sort用归并排序呢?稳定的快排需要额外O(n)空间和归并应该一样呀。。.(stable_sort是稳定排序的简单证明小程序在最后,C++)
注意快排比归并快一半左右,所以常数差一倍。为啥快一半呢?个人愚见,,可以假设最理想情况下,快排每次切中间。这样快排和归并排序都是一样的切法。
在部分排序中,快排扫描一次,归并扫描两次,归并需要左右扫描到辅助数据里再拷贝原位置所以扫描赋值了两遍,快排是N,那么归并就是2N。
所以二者总复杂度常数差一倍,快排1s归并就需要2s。但是快排最好才是切到中间,所以快排的时间复杂度是归并的一半多一点点。
因为快排的情况是绝大多数情况下接近时间复杂度下界,所以平均复杂度比理想下界慢一点点。(只要随机选择base平均复杂度就比较接近下届了,但很多博客比较排序时间的时候都不写。。。)
但是稳定排序的快排需要辅助数组帮忙,而且是扫描两遍,那么我们就知道了,理想情况下稳定快排和归并时间复杂度一样,但是稳定快排最理想在中间,归并本来就在中间,所以稳定快排复杂度比归并高那么一丁点点。
所以猜测这就是sort用快排打基础,stable_sort用归并原因?
实际时间这里我查阅了不少网上的资料,我真的服了,时间复杂度比较,比个锤子。
首先在很多人的代码里,快排写不对,写个一路快排就当快排也就算了。。然后归并也写不对,比时间?。。。写的时候不用非递归的写法也就算了,最后得出的结论要么是归并排序比快排快,甚至是堆排序比快排快。我整个人都傻了。另一份帖子里归并慢的一批,比堆排序还慢,是快排时间的3倍多?喵喵喵?
我这里找到的一份时间资料,但是他没有给代码。。。个人觉得理论平均时间快排是归并的时间一半朝上都是可以的,最起码代码没写错。
计数排序:用于辅助桶排序,工具人。对数据范围有限但数据很多的情况下非常实用,比如高考成绩全排名。不稳定,但速度是数据个数和数据范围之和的线性关系(O(m)+O(n))。
桶排序:数据切成桶,里面再排序。用于数据非常非常非常多的时候,因为数据特别特别多,需要存到多个电脑里,然后每个电脑当成一个桶或者多个桶,能够获得非常理想的时间复杂度。

C++链表快排、归并、插排
#include <iostream>
#include <stack>
using namespace std;
struct ListNode
{
    int val;
    ListNode *next;
    ListNode(const int&val0):val(val0),next(nullptr){};
    void print()
    {
        ListNode* cur = this;//常指针不危险
        while(cur->next)
        {
            cout<<cur->val<<"->";
            cur = cur->next;
        }
        cout<<cur->val<<endl;
    }
};
void delete_ListNode(ListNode*a)
{
    stack<ListNode*>s;
    while(a)
    {
        s.push(a);
        a=a->next;}
    while(s.empty()==false)
    {
        ListNode* cur = s.top();
        s.pop();
        delete cur;
    }
    return;
}
//链表快排似乎是稳定的
ListNode* qSort_ListNode(ListNode* head)
{
    //C++只能有一个返回值呀,所以把部分排序拆开了
    //三路快排
    if(head==nullptr || head->next==nullptr)return head;
    //前中后头结点
    ListNode *L = new ListNode(-1);
    ListNode *cur_L = L;
    ListNode *M = head;
    ListNode *cur_M = M;
    ListNode *R = new ListNode(-1);
    ListNode *cur_R = R;
    //凡是递归里new出来的都要在递归里删掉
    ListNode *cur = head->next;
    while(cur)
    {
        if(cur->val < head->val)
        {
            cur_L->next = cur;
            cur_L = cur_L->next;
            cur = cur->next;
            continue;
        }
        if(cur->val == head->val)
        {
            cur_M->next = cur;
            cur_M = cur_M->next;
            cur = cur->next;
            continue;
        }
        if(cur->val > head->val)
        {
            cur_R->next = cur;
            cur_R = cur_R->next;
            cur = cur->next;
            continue;
        }
    }
    //添加结尾标志符号
    cur_L->next = nullptr;
    cur_R->next = nullptr;
    //数据被分成了三段,分别是左 中 右
    //中间与右边接起来
    cur_M->next = qSort_ListNode(R->next);
    //析构工具人
    delete(R);
    R=nullptr;
    cur_L = qSort_ListNode(L->next);
    delete L;
    L = cur_L;
    if(L == nullptr)return M;
    //res代表最左边,不空,要遍历到结尾,导致链表的快排比归并慢一点。当然最根本的原因就是链表快排丧失了随机初始化的base选择和原地swap,而且稳定的快排并不比归并好多少,所以链表的快排并不如归并排序
    while(cur_L->next)cur_L = cur_L->next;
    //接起来
    cur_L->next = M;
    return L;
}

//两段链表merge连起来
ListNode* merge_ListNode(ListNode* left,ListNode* right)
{
    ListNode* head = new ListNode(-1);
    ListNode* cur= head;
    while(left && right)
    {
        if(left->val<=right->val)//<=稳定排序
        {
            cur->next = left;
            left = left->next;
            cur = cur->next;
            continue;
        }else
        {
            cur->next = right;
            right = right->next;
            cur = cur->next;
            continue;
        }
    }
    if(left)cur->next = left;
    if(right)cur->next = right;
    cur = head->next;
    delete head;
    return cur;
}
ListNode* mergeSort_ListNode(ListNode* head)
{
    if(head==nullptr || head->next==nullptr)// C++会更倾向于nullptr==head的写法,因为如果不小心==写成=,会报错,nullptr不允许赋值,容易排查出来
    {
        return head;
    }
    ListNode *fast = head,*slow = head;
    while(fast->next && fast->next->next)
    {
        fast = fast->next->next;
        slow = slow->next;//slow停在中间或中间靠左的位置
    }
    //截成两半
    fast = slow->next;
    slow->next = nullptr;
    head = mergeSort_ListNode(head);
    fast = mergeSort_ListNode(fast);
    return merge_ListNode(head,fast);
}
//插入
ListNode* insert(ListNode* head,ListNode* insert_ListNode)
{
    if(head==nullptr)return insert_ListNode;
    if(head->val > insert_ListNode->val)
    {
        insert_ListNode->next = head;
        return insert_ListNode;
    }
    ListNode *cur = head;
    while(cur->next && cur->next->val <= insert_ListNode->val)cur = cur->next;
    insert_ListNode->next = cur->next;
    cur->next = insert_ListNode;
    return head;
}
//插入排序
ListNode *insertSort_ListNode(ListNode *head)
{
    if(head==nullptr)return head;
    ListNode *L = new ListNode(-1);
    ListNode *cur = head;
    while(cur)
    {
        head = head->next;
        cur ->next = nullptr;
        L->next = insert(L->next,cur);
        cur = head;
    }
    cur = L->next;
    delete(L);
    return cur;
}
int main()
{

    //List A;
    //A.print();
    ListNode*B = new ListNode(1);
    B->next =  new ListNode(5);
    B->next->next = new ListNode(2);
    B->next->next->next = new ListNode(4);
    B->next->next->next->next = new ListNode(3);
    B->print();
    //B = qSort_ListNode(B);
    B = mergeSort_ListNode(B);
    //B = insertSort_ListNode(B);
    B->print();
    delete_ListNode(B);
    return 0;
}
python:没写插排
#qsort_listNode
class listNode:
    def __init__(self,val,next=None):
        self.val = val
        self.next = next
    def print(self):
        res = [self.val]
        cur = self.next
        while cur:
            res.append(cur.val)
            cur = cur.next
        print(res)
a = listNode(1)
a.next,a.next.next,a.next.next.next,a.next.next.next.next,a.next.next.next.next.next,a.next.next.next.next.next.next=listNode(6),listNode(2),listNode(5),listNode(3),listNode(4),listNode(0)
a.print()
def partition_listNode(st):
    if not st or st.next is None:
        return (None,st,None)
    base = st
    #三路切分
    left = listNode(-1)
    mid = listNode(-1)
    right = listNode(-1)
    head_l,head_m,head_r=left,mid,right
    cur = st.next
    while cur:
        if cur.val<base.val:
            left.next = cur
            left = left.next
            cur = cur.next
            continue
        if cur.val>base.val:
            right.next = cur
            right = right.next
            cur = cur.next
            continue
        mid.next = cur
        mid = mid.next
        cur = cur.next
        continue
    #这一步使得排序变得不稳定了
    mid.next = base
    mid = mid.next
    #修改方法很简单,把下面注释的加上
    #mid.next = None
    #base.next = head_m.next
    #head_m.next = base
    ##
    left.next =None
    mid.next = None
    right.next =None
    #头结点的下一个才是真正的开头
    return (head_l.next,head_m.next,head_r.next)
#快排,可以稳定
def qsort_List(st):
    if not st or st.next is None:
        return st
    res = partition_listNode(st)
    left,mid,right = res[0],res[1],res[2]
    #右边不空
    if right:
        mid.next = qsort_List(right)
    res = mid
    if not left:
        return res
    #左边不空
    res = qsort_List(left)
    cur = res
    #sort的返回值是头结点,如果可以返回头结点和尾节点,这里就不需要走到最后了
    #这一步复杂度增加了n,总体复杂度是2nlog(n),还是nlog(n)复杂度
    while cur.next:
        cur = cur.next
    cur.next = mid
    return res
qsort_List(a).print()
#归并排序,稳定
#merge_sort_listNode
def merge_listNode(left,right):
    new =  listNode(-1)
    cur = new
    while left and right:
        if left.val<=right.val:
            cur.next = left
            cur = cur.next
            left = left.next
        else:
            cur.next = right
            cur = cur.next
            right = right.next
    if left:
        cur.next = left
    else:
        cur.next = right
    return new.next
def merge_sort_listNode(st):
    if not st or not st.next:
        return st
    fast,slow = st.next,st
    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next
    #slow是中间结点
    left_end,right = slow,slow.next
    left_end.next = None
    left,right = merge_sort_listNode(st),merge_sort_listNode(right)
    return merge_listNode(left,right)
a = listNode(1)
a.next,a.next.next,a.next.next.next,a.next.next.next.next,a.next.next.next.next.next,a.next.next.next.next.next.next=listNode(6),listNode(2),listNode(5),listNode(3),listNode(4),listNode(0)
a.print()
merge_sort_listNode(a).print()   

C++:stable稳定肉眼判定
因为数据少了,sort用的是插排,所以我看的一些网上的资料说sort和stable_sort排序结果没区别,我真服了,就几个数据,都掉用插入排序,肯定没区别呀
感兴趣的可以把
for(int j=0;j<500;j++)
改小一点,5-15吧,发现两个排序结果一样了,原因就是sort函数调用的是插入排序而不是快排了,插入排序是稳定滴
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include <iostream>
#include <cstdlib>
#include <ctime>
#define random(x) rand()%(x)
const int SIZE_CHAR = 6; //生成32 + 1位C Style字符串
using namespace std;
bool less_len(const string &str1, const string &str2)
{
        return str1.length() < str2.length();
}
void print(vector<string>&data)
{
    for(c:data)cout<<c<<"->";
    cout<<endl;
}
int main()
{
    string CCH = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
    srand(time(0));
    vector<string>s1,s2;
    for(int j=0;j<500;j++)
    {
        string tmp;
        tmp="";
        int size_s = rand()%SIZE_CHAR+1;
        for (int i = 0; i < size_s; ++i)
        {
            int x = rand()/(RAND_MAX /(sizeof(CCH)-1));
            tmp.push_back(CCH[x]);
        }
        s1.push_back(tmp);
        s2.push_back(tmp);
    }
    print(s1);
    cout<<endl;
    cout<<endl;
    cout<<endl;
    cout<<endl;
    cout<<endl;
    sort(s1.begin(),s1.end(),less_len);
    cout<<endl;
    cout<<endl;
    cout<<endl;
    cout<<endl;
    cout<<endl;
    stable_sort(s2.begin(),s2.end(),less_len);
    print(s1);
    print(s2);
}

快排非递归,两路快排:
def q_sort_bottom(data,st=None,en=None):
    if not data:
        return
    if st is None:
        st = 0
    if en is None:
        en = len(data)-1
    if st>=en:
        return
    import queue
    q = queue.Queue()
    q.put((st,en))
    while(q.empty()==False): 
        cur = q.get()
        st,en = cur[0],cur[1]
        import random
        index = random.randint(st,en)
        data[index],data[st]=data[st],data[index]
        l,r = st+1,en
        base = data[st]
        index = st
        while l<=r:
            #左端点比叫base,小值则交换base和当前左端点,大值则当前左端点和尾端交换
            if data[l]<base:
                data[index],data[l],index,l=data[l],data[index],index+1,l+1
                continue
            if data[l]>base:
                data[l],data[r],r=data[r],data[l],r-1
                continue
            l+=1
        if st<index-1:
            q.put((st,index-1))
        if r+1<en:
            q.put((r+1,en))
    return data
print(q_sort(old_data[:]))
print(q_sort_bottom(old_data[:]))
归并非递归:
写的很丑。。。
#非递归,每次相邻合并一下即可,步长从1自增
def merge(data,l1,r1,l2,r2,data_help):
    cur_index = 0
    t_l1,t_l2=l1,l2
    while(l1<=r1 and l2<=r2):
        if data[l1]<=data[l2]:
            data_help[cur_index],cur_index,l1,=data[l1],cur_index+1,l1+1
        else:
            data_help[cur_index],cur_index,l2=data[l2],cur_index+1,l2+1
    while(l1<=r1):
        data_help[cur_index],cur_index,l1,=data[l1],cur_index+1,l1+1
    while(l2<=r2):
        data_help[cur_index],cur_index,l2=data[l2],cur_index+1,l2+1
    l1,l2=t_l1,t_l2
    for index in range(r1-l1+1):
        data[l1+index]=data_help[index]
    for index in range(r2-l2+1):
        data[l2+index]=data_help[index+r1-l1+1]
def merge_bottom(data,st = None,en = None):
    if not data:
        return None
    if st is None:
        st = 0
    if en is None:
        en = len(data)-1
    step = 1
    data_help = [0]*(en-st+1)
    while step<=en-st+1:
        for left in range(st,en-step+1,2*step):
            l1,r1,l2,r2 = left,left+step-1,left+step,left+2*step-1
            if r2>en:
                r2=en
            merge(data,l1,r1,l2,r2,data_help)
        step*=2
    print(data)
merge_sort(old_data[:])
merge_bottom(old_data[:])
非常粗糙简单的桶排序:
#工具人排序
def nums_sort(data):
    if not data:
        return []
    min_data = min(data)
    max_data = max(data)
    nums = [0]*(max_data-min_data+1)
    for value in data:
        nums[value-min_data]+=1
    cur = min_data
    for index in range (len(data)):
        while cur<= max_data and nums[cur-min_data]<=0:
            cur+=1
        nums[cur-min_data]-=1
        data[index] = cur
    return data
print(nums_sort(old_data[:]))
def bucket_sort(data,K = 10):
    if not data:
        return
    #K桶的大小 桶的大小 = data的范围时,就一个桶,可以退化为计数排序
    if K<=0:
        K = 10
    min_data = min(data)
    max_data = max(data)
    #//K向下取整,需要多补一个,计数排序返回空[]才可以用extend函数
    buckets = [[] for _ in range(1+(max_data-min_data)//K)]
    for value in data:
        buckets[(value-min_data)//K].append(value)
    res = []
    for i in buckets:
        res.extend(nums_sort(i))
    return res
print(bucket_sort(old_data[:],K=2))