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

9 篇文章 1 订阅

归并排序:

给定一个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中。

代码:

class MergeSort {  
public:  
      //第一步:分配B数组
      int* mergeSort(int* A, int n) {  

        int *B = (int*)malloc(sizeof(int) * n);  
          
        QuickSort(A, B, 0, n-1);  
          
        free(B);  
          
        return A;  
         
     }  
    //第三步:merger()函数
     void merger(int *A, int *B, int start, int mid, int end){  
        int i = start;  
        int j = mid+1;  
        int k = start;  
        //将a1和a2从头开始比较,谁小,就把谁存到B数组里面
        while(i <=mid && j <=end){  
            if(A[i] < A[j]){  
                B[k++] = A[i++];  
            }  
            else{  
                B[k++] = A[j++];  
            }  
        }  
        //将a1或a2中还没比较的,直接复制到数组B中
        while(i != mid+1){  
            B[k++] = A[i++];  
        }  
          
        while(j != end+1){  
            B[k++] = A[j++];  
        }  
        //将排序好的B数组复制给A数组
        k=start;  
        while(k <= end){  
            A[k++] = B[k];  
            //k++;  = 为从右到左的运算  不能写成 A[k] = B[k++],因为这样k不是从0开始,k++已经让k+1过了A[k]中的k是1;  
        }  
    }  
  
    //第二步:递归调用归并排序
    void QuickSort(int *A, int *B, int start, int end){  
        if(start >= end)  
            return;  
          
        int mid = (start + end) /2;  
          
        QuickSort(A, B, start, mid);  
        QuickSort(A, B, mid+1, end);  
          
        merger(A, B, start, mid, end);  
   
    }  

};  

main调用:

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

	return 0;
}

链表:

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

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

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

代码:

class Solution {
public:
    ListNode* findMiddle(ListNode* head){
        ListNode* chaser = head;
        ListNode* runner = head->next;//一定要用next
        while(runner != NULL && runner->next != NULL){
            chaser = chaser->next;
            runner = runner->next->next;
        }
        return chaser;
    }
     
 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL){
            return l2;
        }
        if(l2 == NULL){
            return l1;
        }
        ListNode* dummy = new ListNode();//空结构
        ListNode* head = dummy;
        while(l1 != NULL && l2 != NULL){
            if(l1->val > l2->val){
                head->next = l2;
                l2 = l2->next;
            }
            else{
                head->next = l1;
                l1 = l1->next;
            }
            head = head->next;
        }
        if(l1 == NULL){
            head ->next = l2;
        }
        if(l2 == NULL){
            head->next = l1;
        }
        return dummy->next;
    }
     
    ListNode* sortList(ListNode* head) {
        if(head == NULL || head ->next == NULL){
            return head;
        }
        ListNode* middle = findMiddle(head);
        ListNode* right = sortList(middle->next);//这就是上面为啥要用next的原因
        middle -> next = NULL;//截断
        ListNode* left = sortList(head);
        return mergeTwoLists(left, right);
    }
};

main()调用

#include <iostream>
using namespace std;
struct ListNode{
	int val;
	struct ListNode* next;

};
int main(){
	ListNode *list=(ListNode *)malloc(sizeof(ListNode));
	list->val=2;
	list->next=NULL;
	//可以用循环
	ListNode *l=list;
	ListNode *p=(ListNode *)malloc(sizeof(ListNode));
	p->val=4;
	list->next=p;
	list=list->next;

	ListNode *lo=(ListNode *)malloc(sizeof(ListNode));
	lo->val=5;
	list->next=lo;
	list=list->next;

	ListNode *q=(ListNode *)malloc(sizeof(ListNode));
	q->val=3;
	list->next=q;
	list=list->next;

	list->next=NULL;


	Solution s;
	ListNode* ss =s.sortList(l);
	while(ss!=NULL){
		cout<<ss->val<<endl;
		ss=ss->next;
	}

	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用指针来实现数组链表之间的转换。具体来说,可以定义一个指向链表节点的指针,然后遍历数组中的元素,将每个元素插入到链表中。以下是一个示例代码: ```c #include <stdio.h> #include <stdlib.h> struct ListNode { int val; struct ListNode *next; }; struct ListNode* arrayToList(int* nums, int size) { struct ListNode* head = NULL; struct ListNode* tail = NULL; for (int i = 0; i < size; i++) { struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); node->val = nums[i]; node->next = NULL; if (head == NULL) { head = node; tail = node; } else { tail->next = node; tail = node; } } return head; } void printList(struct ListNode* head) { while (head != NULL) { printf("%d ", head->val); head = head->next; } printf("\n"); } int main() { int nums[] = {1, 2, 3, 4, 5}; int size = sizeof(nums) / sizeof(nums[0]); struct ListNode* head = arrayToList(nums, size); printList(head); return 0; } ``` 在上面的代码中,我们定义了一个结构体 `ListNode`,表示链表节点。然后定义了一个函数 `arrayToList`,用于将数组转换为链表。在函数中,我们遍历数组中的每个元素,创建一个新的节点,并将其插入到链表中。最后返回链表的头节点。我们还定义了一个函数 `printList`,用于打印链表中的所有元素。在 `main` 函数中,我们调用 `arrayToList` 函数将数组转换为链表,并调用 `printList` 函数打印链表中的所有元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值