[转]C/C++ 数组,链表排序(平均时间复杂度 O(nlogn))归并、快速、堆、希尔之归并排序

归并排序:

给定一个int数组A以及大小n,请返回排序后的数组;

 

时间复杂度分析:

             T(n)            拆分 n/2, 归并 n/2 ,一共是n/2 + n/2 = n

            /    \           以下依此类推:

       T(n/2) T(n/2)      一共是 n/2*2 = n

         /    \  /     \

  T(n/4) ...........   一共是 n/4*4 = n

       一共有logn层,故时间复杂度是 O(nlogn)

空间复杂度:就是那个临时的数组和递归时压入栈的数据占用的空间:n + logn;所以空间复杂度为: O(n)

核心思想:

是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并排序的一般步骤为:
1)将待排序数组(链表)取中点并一分为二;
2)递归地对左半部分进行归并排序;
3)递归地对右半部分进行归并排序;
4)将两个半部分进行合并(merge),得到结果。

采用递归的过程进行归并排序,并使用一个与A大小相同的辅助数组空间B,大家都知道递归是一个进栈和出栈的过程。

1、递归调用归并排序过程将数组折半查找:

1.1、递归调用数组A前一半,和后一半,返回条件是开始索引>=结束索引

递归调用这一步是压栈过程,直到每一半的大小划分为1

1.2、调用将前一半、后一半的数组,归并并且排好序成为一个数组的函数merger()。

(这一步就是出栈,并且从划分为1的划分慢慢变大,但是划分的数组因为调用merger()函数已经成为有序数组)(这个是重点理解

2、merger()函数归并思想体现在这(将前一半a1数组和后一半a2数组,合并并排序,其实a1和a2经过前面的递归已经有序):

2.1、将a1和a2从头开始比较,谁小,就把谁存到B数组里面(重点

2.2、将a1或a2中还没比较的,直接复制到数组B中

2.3、将排序好的B数组复制给A数组

A[i]和A[j]进行比较谁小,就把它放到B中,然后小的下标向后移动,另一个不动,循环比较;

直到有一个先结束(A[j]先结束),另外一个(A[i]的6,7值)还没结束,直接将没结束的后面每比较的直接复制到B中。

代码:

 
  1.  
    class MergeSort {
  2.  
    public:
  3.  
    //第一步:分配B数组
  4.  
    int* mergeSort(int* A, int n) {
  5.  
     
  6.  
    int *B = (int*)malloc(sizeof(int) * n);
  7.  
     
  8.  
    QuickSort(A, B, 0, n-1);
  9.  
     
  10.  
    free(B);
  11.  
     
  12.  
    return A;
  13.  
     
  14.  
    }
  15.  
    //第三步:merger()函数
  16.  
    void merger(int *A, int *B, int start, int mid, int end){
  17.  
    int i = start;
  18.  
    int j = mid+1;
  19.  
    int k = start;
  20.  
    //将a1和a2从头开始比较,谁小,就把谁存到B数组里面
  21.  
    while(i <=mid && j <=end){
  22.  
    if(A[i] < A[j]){
  23.  
    B[k++] = A[i++];
  24.  
    }
  25.  
    else{
  26.  
    B[k++] = A[j++];
  27.  
    }
  28.  
    }
  29.  
    //将a1或a2中还没比较的,直接复制到数组B中
  30.  
    while(i != mid+1){
  31.  
    B[k++] = A[i++];
  32.  
    }
  33.  
     
  34.  
    while(j != end+1){
  35.  
    B[k++] = A[j++];
  36.  
    }
  37.  
    //将排序好的B数组复制给A数组
  38.  
    k=start;
  39.  
    while(k <= end){
  40.  
    A[k++] = B[k];
  41.  
    //k++; = 为从右到左的运算 不能写成 A[k] = B[k++],因为这样k不是从0开始,k++已经让k+1过了A[k]中的k是1;
  42.  
    }
  43.  
    }
  44.  
     
  45.  
    //第二步:递归调用归并排序
  46.  
    void QuickSort(int *A, int *B, int start, int end){
  47.  
    if(start >= end)
  48.  
    return;
  49.  
     
  50.  
    int mid = (start + end) /2;
  51.  
     
  52.  
    QuickSort(A, B, start, mid);
  53.  
    QuickSort(A, B, mid+1, end);
  54.  
     
  55.  
    merger(A, B, start, mid, end);
  56.  
     
  57.  
    }
  58.  
     
  59.  
    };
 

main调用:

 
  1.  
    #include <iostream>
  2.  
    using namespace std;
 
 
  1.  
    int main(){
  2.  
    int AAA[]={1,3,6,9,2,5,4,8,7};
  3.  
    MergeSort M;
  4.  
    int *b=M.mergeSort(AAA,9);
  5.  
    for(int i=0;i<9;i++){
  6.  
        cout<<b[i]<<endl;
  7.  
    }
  8.  
     
  9.  
    return 0;
  10.  
    }
 

链表:

采用快慢指针找中点:快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针恰好在链表中点

时间复杂度为:O(nlogn),原因一样

空间复杂度为:O(1),因为没有借助其他空间。

代码:

 
  1.  
    class Solution {
  2.  
    public:
  3.  
    ListNode* findMiddle(ListNode* head){
  4.  
    ListNode* chaser = head;
  5.  
    ListNode* runner = head->next;//一定要用next
  6.  
    while(runner != NULL && runner->next != NULL){
  7.  
    chaser = chaser->next;
  8.  
    runner = runner->next->next;
  9.  
    }
  10.  
    return chaser;
  11.  
    }
  12.  
     
  13.  
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
  14.  
    if(l1 == NULL){
  15.  
    return l2;
  16.  
    }
  17.  
    if(l2 == NULL){
  18.  
    return l1;
  19.  
    }
  20.  
    ListNode* dummy = new ListNode();//空结构
  21.  
    ListNode* head = dummy;
  22.  
    while(l1 != NULL && l2 != NULL){
  23.  
    if(l1->val > l2->val){
  24.  
    head->next = l2;
  25.  
    l2 = l2->next;
  26.  
    }
  27.  
    else{
  28.  
    head->next = l1;
  29.  
    l1 = l1->next;
  30.  
    }
  31.  
    head = head->next;
  32.  
    }
  33.  
    if(l1 == NULL){
  34.  
    head ->next = l2;
  35.  
    }
  36.  
    if(l2 == NULL){
  37.  
    head->next = l1;
  38.  
    }
  39.  
    return dummy->next;
  40.  
    }
  41.  
     
  42.  
    ListNode* sortList(ListNode* head) {
  43.  
    if(head == NULL || head ->next == NULL){
  44.  
    return head;
  45.  
    }
  46.  
    ListNode* middle = findMiddle(head);
  47.  
    ListNode* right = sortList(middle->next);//这就是上面为啥要用next的原因
  48.  
    middle -> next = NULL;//截断
  49.  
    ListNode* left = sortList(head);
  50.  
    return mergeTwoLists(left, right);
  51.  
    }
  52.  
    };
 

main()调用

 
  1.  
    #include <iostream>
  2.  
    using namespace std;
  3.  
    struct ListNode{
  4.  
    int val;
  5.  
    struct ListNode* next;
  6.  
     
  7.  
    };
 
 
  1.  
    int main(){
  2.  
    ListNode *list=(ListNode *)malloc(sizeof(ListNode));
  3.  
    list->val=2;
  4.  
    list->next=NULL;
  5.  
    //可以用循环
  6.  
    ListNode *l=list;
  7.  
    ListNode *p=(ListNode *)malloc(sizeof(ListNode));
  8.  
    p->val=4;
  9.  
    list->next=p;
  10.  
    list=list->next;
  11.  
     
  12.  
    ListNode *lo=(ListNode *)malloc(sizeof(ListNode));
  13.  
    lo->val=5;
  14.  
    list->next=lo;
  15.  
    list=list->next;
  16.  
     
  17.  
    ListNode *q=(ListNode *)malloc(sizeof(ListNode));
  18.  
    q->val=3;
  19.  
    list->next=q;
  20.  
    list=list->next;
  21.  
     
  22.  
    list->next=NULL;
  23.  
     
  24.  
     
  25.  
    Solution s;
  26.  
    ListNode* ss =s.sortList(l);
  27.  
    while(ss!=NULL){
  28.  
    cout<<ss->val<<endl;
  29.  
    ss=ss->next;
  30.  
    }
  31.  
     
  32.  
    return 0;
  33.  
    }
 

 

 

 


---------------------
作者:冰凌其
来源:CSDN
原文:https://blog.csdn.net/xiao1_1bing/article/details/79436710
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By: CSDN,CNBLOG博客文章一键转载插件
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值