leetcode排序算法总结—时间复杂度o(nlogn)-希尔/堆排/快排/归并小记

简介: leetcode排序算法总结—时间复杂度o(nlogn)-希尔/堆排/快排/归并小记

排序算法总结—时间复杂度O(nlogn)—希尔/堆排序/快排/归并


d34eeaed488d41db86e4ac28dd82c769.jpg希尔排序

有一段间隔的排序,可以逐个子表进行排序,然(例如王道)都给出便于计算机进行连续访问的程序算法,即依次按元素比较不同子表进行子表的调整。


时间复杂度O(n^1.3) 最坏情况下n方

空间复杂度O(1)

不稳定

适用于线性表为顺序存储。


数组排序例题:给你一个整数数组nums,请你将该数组升序排列。

/*** Note: The returned array must be malloced, assume caller calls free().
 */
//希尔
int* sortArray(int* nums, int numsSize, int* returnSize){
    // int *returns = (int *)malloc(sizeof(int )* numsSize);
    int temp,index;//暂存当前比较的数组元素
    for(int dk = numsSize/2;dk > 0;dk /= 2)
    {
        // printf("%d ",dk);
        for(int i = dk;i < numsSize;i++)
        {
            // printf("%d",nums[i-dk]);
            if(nums[i] < nums[i-dk])
            {
                temp = nums[i];
                index = i-dk;
                while(index >= 0 && temp < nums[index])
                {
                    // printf("%d ",index);
                    nums[index+dk] = nums[index];
                    index -= dk;
                }
                nums[index+dk] = temp;
                // printf("%d",nums[index+dk]);
            }
        }
    }
    // for(int k = 0;k < numsSize;k++)
    // {
    //     returns[k] = nums[k];
    // }
    *returnSize = numsSize;
    return nums;
}

相对名次例题

给出N名运动员的成绩,找出他们的相对名次并授予前三名对应的奖牌。前三名运动员将会被分别授予 “金牌”,“银牌” 和“ 铜牌”(“Gold Medal”, “Silver Medal”, “Bronze Medal”)。

(注:分数越高的选手,排名越靠前。)

`示例 1:

输入: [5, 4, 3, 2, 1]

输出: [“Gold Medal”, “Silver Medal”, “Bronze Medal”, “4”, “5”]

解释: 前三名运动员的成绩为前三高的,因此将会分别被授予 “金牌”,“银牌”和“铜牌” (“Gold Medal”, “Silver Medal” and “Bronze Medal”).

余下的两名运动员,我们只需要通过他们的成绩计算将其相对名次即可。

提示:

1.N 是一个正整数并且不会超过 10000。

2.所有运动员的成绩都不相同。

/*** Note: The returned array must be malloced, assume caller calls free().***/
char ** findRelativeRanks(int* score, int scoreSize, int* returnSize){
    *returnSize = scoreSize;
    int a[scoreSize];
    for(int m = 0;m < scoreSize;m++)
    {
        a[m] = score[m];
    }
    char **returns = (char **)malloc(sizeof(char*)*scoreSize);
    int temp,index;
    for(int dk = scoreSize/2;dk > 0;dk /= 2)
    {
        for(int i = dk;i < scoreSize;i++)
        {
            if(score[i-dk] < score[i])
            {
                temp = score[i];
                index = i-dk;
                while(index >= 0 && temp > score[index])
                {
                    score[index+dk] = score[index];
                    index -= dk;
                }
                score[index+dk] = temp;
            }
        }
    }
    // printf("%c",(char)(5+'0'));
    for(int k = 0;k < scoreSize;k++)
    {
        for(int m = 0;m < scoreSize;m++)
        {
            if(a[k] == score[m])
            {
                if(m == 0)
                    returns[k] = "Gold Medal";
                else if(m == 1)
                    returns[k] = "Silver Medal";
                else if(m == 2)
                    returns[k] = "Bronze Medal";
                else
                {
                    returns[k] = (char*)malloc(sizeof(char)*10);
                    sprintf(returns[k],"%d",m+1) ;
            // char m = (k+'0');
            // returns[k] = m;
                }
                break;
            }
        }
    }
    return returns;
}


堆排序


适合关键字超级多的情况。比如一万个数挑出前100个最大最小值。

image.jpeg

在建立大根堆或小根堆情况下,递归排序,提出根结点,继续建立大根堆或小根堆。


时间复杂度:O(nlogn)

空间复杂度:O(1)

不稳定


最小的k个数


输入整数数组arr,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。**

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
//堆排序
void heapAdjust(int a[],int k,int len)
{
    int temp = a[k];
    for(int j = k*2 ;j <= len;j *= 2)//第2个结点-1 = 下标
    {
        if(j < len && a[j] > a[j+1])
        {
            j++;
        }
        if(a[j] >= temp) 
            break;
        else
        {
            a[k] = a[j];
            k = j;
        }
    }
    a[k] = temp;
}
void buildheap(int a[],int len)
{
    for(int j = len/2;j > 0;j--)
    {
        heapAdjust(a,j,len);
    }
}
void swap(int *a,int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
    // int * a = (int *)malloc(sizeof(int )*(arrSize+1));
    int a[arrSize+1];
    // arr[arrSize] = (int *)malloc(sizeof(int )*1);
    // arr[arrSize] = 1;
    // printf("%d",arr[arrSize]);
    int m = 1;
    for(int j = 0;j < arrSize;j++)
    {
        a[m++] = arr[j];
        // printf("%d",a[m-1]);
    }
    *returnSize = k;
    int *returns = (int *)malloc(sizeof(int) * k);
    buildheap(a,arrSize);
    for(int i = 0,x = arrSize;i < k;i++,x--)
    {
        returns[i] = a[1];
        // printf("%d",a[1]);
        // printf("%d ",a[x]);
        swap(&a[1],&a[x]);
        // printf("%d",a[1]);
        // printf("%d ",a[x]);
        heapAdjust(a,1,x-1);
        // printf("m%dm",returns[i]);
    }
    return returns;
}


快速排序


以某一个元素为枢轴,以轴旋转,比轴小的在左侧,比轴大的在右侧。

每次排序会有一个位置元素回到确定的位置。(理想下,第k轮,排好2的k-1次方个数)

该代码重要的是分区思想的代码,注意边界条件。

1⃣️最简单的思路是:从 left 开始,遇到比基数大的数,就交换到数组最后,并将 right 减一,直到 left 和 right 相遇,此时数组就被分成了左右两个区域。再将基数和中间的数交换,返回中间值的下标即可。

2⃣️双指针:从 left 开始,遇到比基数大的数,记录其下标;再从 right 往前遍历,找到第一个比基数小的数,记录其下标;然后交换这两个数。继续遍历,直到 left 和 right 相遇。然后就和刚才的算法一样了,交换基数和中间值,并返回中间值的下标。

(我猜我们会考双轴快排)


时间复杂度:O(nlogn)

空间复杂度:最好情况O(logn) 有递归存在,栈的深度为logn

不稳定

还是用排序数组的题

这个敲的很顺手!

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* sortArray(int* nums, int numsSize, int* returnSize){
    if(numsSize == 0) return nums;
    *returnSize = numsSize;
    quicksort(nums,0,numsSize-1);
    return nums;
}
void quicksort(int a[],int start,int end)
{
    if(start >= end) return;
    int mid = partition(a,start,end);
    quicksort(a,start,mid-1);
    quicksort(a,mid+1,end);
}
int partition(int a[],int start,int end)
{
    int temp = a[start];
    int left = start + 1;
    int right = end;
    while(left < right)
    {
        while(left < right && a[left] <= temp) left++;
        // while(left < right && a[right] >= temp) right--;
        if(left != right)
        {
            swap(a,left,right);
            // left++;
            right--;
        }
    }
    if(left == right && a[right] > temp) right--;
    if(right != start) swap(a,start,right);
    return right;
}
void swap(int a[],int x,int y)
{
    int t = a[x];
    a[x] = a[y];
    a[y] = t;
}


归并排序


两个有序子表合并,整个归并排序需要进行logn向上取整趟。

先拆开,再两两合并。

1.jpg

时间复杂度:O(nlogn)

空间复杂度:O(n)需要一个辅助数组的空间

稳定!!终于稳定啦!撒花🎉

逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

这道题太好了!每次看每次都看明白,下一次都又,啥啊,这是什么啊?

!!划重点,敲黑板🤏

对于分成1个1个子表开始合并时,若后边的元素要放入数组中,就证明该元素比前边元素都小,形成了前面剩下元素个逆序对,依次记录📝,可得总和。

int reversePairs(int* nums, int numsSize){
    int count = 0;
    int *com = (int *)malloc(sizeof(int)*numsSize);
    // for(int i = 0; i < numsSize;i++)
    // {
    //     com[i] = nums[i];
    // }
    count = mergesort(nums,com,0,numsSize-1,count);
    return count;
}
int mergesort(int a[],int com[],int low,int high,int count)
{
    // int count = 0;
    if(low < high)
    {
        int mid = (low+high)/2;
        count = mergesort(a,com,low,mid,count);
        count = mergesort(a,com,mid+1,high,count);
        // printf("a%d ",count);
        count = merge(a,com,low,mid,high,count);
        // printf(" %da",count);
    }
    return count;
}
int merge(int a[],int com[],int low,int mid,int high,int count)
{
    // int count = 0;
    // int *com = (int *)malloc(sizeof(int)*);
    // printf("b%d",count);
    for(int i = low; i <= high;i++)
    {
        com[i] = a[i];
    }
    int i,j,k;
    for(i = low,j = mid+1,k = low;i <= mid && j <= high;k++)
    {
        if(com[i] <= com[j])
        {
            a[k] = com[i];
            i++;
        }
        else
        {
            a[k] = com[j];
            j++;
            count += mid-i+1; //每一次后边的小放到前面就形成了前一个数组所剩元素数量的逆序对
        }
        // printf("%db",count);
    }
    while(i <= mid) a[k++] = com[i++];
    while(j <= high) a[k++] = com[j++];
    return count;
}

好的!我会了,我一定会这四种了!冲冲冲👌

相关文章
|
1月前
|
机器学习/深度学习 缓存 算法
Python算法设计中的时间复杂度与空间复杂度,你真的理解对了吗?
【10月更文挑战第4天】在Python编程中,算法的设计与优化至关重要,尤其在数据处理、科学计算及机器学习领域。本文探讨了评估算法性能的核心指标——时间复杂度和空间复杂度。通过详细解释两者的概念,并提供快速排序和字符串反转的示例代码,帮助读者深入理解这些概念。同时,文章还讨论了如何在实际应用中平衡时间和空间复杂度,以实现最优性能。
62 6
|
3月前
|
机器学习/深度学习 算法 程序员
读《趣学算法》:重开算法之门,时间复杂度与空间复杂度
本文是作者阅读《趣学算法》后的笔记,介绍了算法复杂度的基本概念,包括时间复杂度和空间复杂度的不同阶表示,并通过具体例子展示了如何计算和理解算法的效率。
65 2
读《趣学算法》:重开算法之门,时间复杂度与空间复杂度
|
1月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
38 0
|
30天前
|
搜索推荐 算法
插入排序算法的平均时间复杂度解析
【10月更文挑战第12天】 插入排序是一种简单直观的排序算法,通过不断将未排序元素插入到已排序部分的合适位置来完成排序。其平均时间复杂度为$O(n^2)$,适用于小规模或部分有序的数据。尽管效率不高,但在特定场景下仍具优势。
|
9天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
1月前
|
机器学习/深度学习 存储 缓存
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
文章主要介绍了排序算法的分类、时间复杂度的概念和计算方法,以及常见的时间复杂度级别,并简单提及了空间复杂度。
24 1
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
|
1月前
|
存储 算法
算法的时间复杂度和空间复杂度
本文详细讨论了算法的时间复杂度和空间复杂度,包括它们的概念、计算方法和常见复杂度的对比,并通过多个实例解释了如何计算算法的时间和空间复杂度。
64 0
算法的时间复杂度和空间复杂度
|
2月前
|
算法 Python
震惊!Python 算法设计背后,时间复杂度与空间复杂度的惊天秘密大起底!
在 Python 算法设计中,理解并巧妙运用时间复杂度和空间复杂度的知识,是实现高效、优雅代码的必经之路。通过不断地实践和优化,我们能够在这两个因素之间找到最佳的平衡点,创造出性能卓越的程序。
39 4
|
1月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
13 0
|
1月前
|
算法 C语言
深入理解算法效率:时间复杂度与空间复杂度
深入理解算法效率:时间复杂度与空间复杂度