【算法】排序

       ~~~~~~       排序算法作为一项需求,它足够简单,是学习基础算法思想(例如:分治算法、减治思想、递归写法)的很好的学习材料。

参考:leetcode一位很棒的题解大佬
链接: https://leetcode.cn/problems/sort-an-array/solution/fu-xi-ji-chu-pai-xu-suan-fa-java-by-liweiwei1419/

1、选择排序(了解)

思路:每一轮选取未排定的部分中最小的部分交换到未排定部分的最开头。

// 1 选择排序 : 每次找出"i~n"最小的元素,放到最前面 
void select_sort(int arr[],int len)
{
	for(int i=0;i<len;i++)
	{
		int min_th = i; 
		for(int j=i+1;j<len;j++)
		{
			if(arr[j]<arr[min_th])
				min_th = j;
		}
		int temp = arr[i];
		arr[i] = arr[min_th];
		arr[min_th] = temp;
	}
} 

复杂度分析:
时间复杂度:O( N 2 N^2 N2),这里 N 是数组的长度;
空间复杂度:O(1),使用到常数个临时变量。

2、插入排序(熟悉)

思路:每次将一个数字插入一个有序的数组里,成为一个长度更长的有序数组,有限次操作以后,数组整体有序(打扑克理牌的原理)。
       ~~~~~~       由于「插入排序」在几乎有序的数组上表现良好,特别地,在短数组上的表现也很好。因为短数组的特点是:每个元素离它最终排定的位置都不会太远。为此,在小区间内执行排序任务的时候,可以转向使用「插入排序」

// 3 插入排序 : 打扑克理牌
void insert_sort(int arr[],int len)
{
	for(int i=1;i<len;i++)
	{
		int value = arr[i];   //一定要设置变量储存arr[i]的初始值 
		int preIndex = i-1;  //用于寻找value的插入位置
		while(preIndex>=0 && arr[preIndex]>value)
		{
			arr[preIndex+1] = arr[preIndex];
			preIndex--;
		}
		arr[preIndex+1] = value;
	}
}

插入排序有两个关键变量:
     ~~~~     ①value值为当前摸到的扑克牌
     ~~~~     ②preIndex值为已排序的最后一个扑克牌的位置索引,即 i-1.

复杂度分析:
时间复杂度:O( N 2 N^2 N2),这里 N 是数组的长度;
空间复杂度:O(1),使用到常数个临时变量。

3、冒泡排序(了解)

// 2 冒泡排序 : 每次检查相邻两个元素,每次找出最大的数放在最后 
void bubble_sort(int arr[],int len)
{
	for(int i=0;i<len-1;i++)
	{
		for(int j=0;j<len-1-i;j++)
		{
			if(arr[j]>arr[j+1])
			{
				int temp = arr[j+1];
				arr[j+1] = arr[j];
				arr[j] = temp;
			}	
		}	
	}	
} 

4、归并排序(重点)

思路:分治,借助额外空间,合并两个有序数组,得到更长的有序数组。(理解递归过程的非常好的学习材料,重点掌握)
在这里插入图片描述归并排序是建立了额外的数组空间tmp,用来储存合并两个有序数组的结果。那么我们如何得到左右两个有序数组呢?是用递归的思想,递归的结束条件是左边界大于等于右边界,即分解到只有1个元素时结束。

class Solution {
    vector<int> tmp;
    void mergeSort(vector<int>& nums, int l, int r) {
        if (l >= r) return;
        int mid = (l + r) >> 1;
        mergeSort(nums, l, mid);
        mergeSort(nums, mid + 1, r);
        int i = l, j = mid + 1;
        int cnt = 0;
        // 合并两个有序数组
        while (i <= mid && j <= r) {
            if (nums[i] <= nums[j]) {
                tmp[cnt++] = nums[i++];
            }
            else {
                tmp[cnt++] = nums[j++];
            }
        }
        while (i <= mid) {
            tmp[cnt++] = nums[i++];
        }
        while (j <= r) {
            tmp[cnt++] = nums[j++];
        }
        // 将额外空间中的结果返回给nums
        for (int i = 0; i < r - l + 1; ++i) {
            nums[i + l] = tmp[i];
        }
    }
public:
    vector<int> sortArray(vector<int>& nums) {
        tmp.resize((int)nums.size(), 0);
        mergeSort(nums, 0, (int)nums.size() - 1);
        return nums;
    }
};

复杂度分析:
时间复杂度:O(Nlog⁡N),这里 N是数组的长度;
空间复杂度:O(N),辅助数组与输入数组规模相当。

5、快速排序(重点)

思路:快速排序每一次都排定一个元素——基准数(这个元素呆在了它最终应该呆的位置),然后递归地去排它左边的部分和右边的部分,依次进行下去,直到数组有序。

具体实现过程(下图借用链接作者的图):参考 快速排序过程详解
原文作者的图

// 6 快速排序
void quick_sort(int s[], int l, int r)
{
		if(l>=r)
			return ;
    //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 :以中间数作为基准数
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;  
            while(i < j && s[i] <= x) // 从左向右找第一个大于x的数
                i++;  
			if(i < j)
			{
				int temp = s[i];
				s[i] = s[j];
				s[j] = temp;
			}
        }

		int temp = s[l];  //这里一定是s[l]而不是x
		s[l] = s[i];
		s[i] = temp;
        quick_sort(s, l, j - 1); // 递归调用 
        quick_sort(s, j + 1, r);
} 

注意:第二个交换中,交换的变量是s[l]而不是x。

复杂度分析:
时间复杂度:O(Nlog⁡N),这里 N 是数组的长度;
空间复杂度:O(log⁡N),这里占用的空间主要来自递归函数的栈空间。

注:(针对特殊测试用例:顺序数组或者逆序数组)一定要随机化选择切分元素,否则在输入数组是有序数组或者是逆序数组的时候,快速排序会变得非常慢(等同于冒泡排序or选择排序)

6、堆排序(根据个人情况掌握)

后续再补充吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值