【排序汇总】这里记录一切与搜索排序相关的内容~(更新ing)

经典算法

快速排序

经典面试手撕题,刚好明天又要面试百度了,先复习一下~

核心思想

快速排序算法的核心其实就是“分而治之”。

给定一个数组int a = {2, 6, 3, 8, 1, 5, 4},我们首先选择一个基准元素,将问题分割成两个子问题将比基准元素小的值放左边,将比基准元素大的值放右边,那么我们需要排序的整体数组就变成了两个更短的数组,进而我们分别对左右两边的子数组继续干上面相同的事情,直到分割到最后的子数组中只有一个元素,这样的子数组必定是有序的,进而我们的整个数组也就排序好了。实际上我们分析的就是一个递归过程

那么“分”的思路我们想好了,该如何“”呢?

从上面的分析我们可以得出,要写一个快速排序算法,我们需要知道当前处理子数组的左右边界/index,也就是int leftint right

假定我们每一次对子数组进行处理的时候,基准元素都是左边界第一个数,我们将它拿出来key = a[left],将第一个位置看做成一个“坑”来存放之后需要调整到前面来的数。

那么我们首先从后往前去找比key小的数,将它放在a[left]里,这样这个坑就完成了它的使命,但是由于我们将后面的a[right]中的数拿出来,这里又变成了一个坑,那么我们下面就要反过来从前往后去找比key大的数,将它填在a[right]里。依此类推,这就是一个填坑游戏,哈哈~

cpp代码

具体cpp代码如下:

#include <iostream>
#include <vector>
using namespace std;

void quickSort(int left, int right, vector<int>& a){
	// 递归终止条件
	if(left >= right){
		return;
	}
	
	int l = left, r = right;
	int key = a[l];
	while(l < r){
		// 从后往前找比key小的数 
		while(l < r && a[r] >= key){
			r--;
		}
		if(l < r){
			a[l++] = a[r];
		}
		// 从前往后找比key大的数
		while(l < r && a[l] <= key){
			l++;
		} 
		if(l < r){
			a[r--] = a[l];
		} 
	}
	// 放置key的位置 
	a[l] = key;
	// 继续递归处理左右两边子数组
	quickSort(left, l-1, a);
	quickSort(l+1, right, a); 
}

int main() {
    vector<int> a = {10, 7, 2, 6, 5, 11, 8};
    quickSort(0, a.size()-1, a);
    for(int i = 0; i < a.size(); ++i){
    	cout << a[i] << " ";
	}
	cout << endl;
    return 0;
}

二分查找

哈哈哈昨天刚写完快速排序,今天面试面试官反倒让我写了二分查找,这就来更新!

题目🔗

核心思想

对于一个有序数组,其索引范围是[left, right],我们想查询一个元素target是否存在:

首先查询当前中间元素nums[mid]的大小:
- 如果nums[mid] < target,则说明targetnums[mid]右边,进而去(mid, right]中找
- 如果nums[mid] > target,则说明targetnums[mid]左边,进而去[left, mid)中找。

依据上面的分析,实际上就是一个循环的过程,直到我们找到这个target

cpp代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while(left <= right){
        	// 找到中间元素索引
            int mid = (left + right) / 2;
            if(nums[mid] < target){
            	// 当中间元素小于target
                left = mid + 1;
            }
            else if(nums[mid] > target){
            	// 当中间元素大于target
                right = mid - 1;
            }
            // 当中间元素等于target,直接返回索引
            else return mid;
        }
        return -1;
    }
};

具体题目

荷兰旗问题-颜色分类 (leetcode75)

题目🔗

荷兰国旗是由红白蓝三种颜色条纹拼接而成的,如下图所示。
在这里插入图片描述
如果我们给定若干条这样的条纹并且随机拼接(下图情况),请你把他们按照荷兰国旗颜色进行排序。
在这里插入图片描述
具体题目描述如下
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 012 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1
输入nums = [2,0,2,1,1,0]
输出[0,0,1,1,2,2]

示例 2
输入nums = [2,0,1]
输出[0,1,2]

提示
n == nums.length
1 <= n <= 300
nums[i]012

思路

这题是不是和快排很像?我们如果把小于基准元素的值放在左边,把等于基准元素的值放在中间,把大于基准元素的值放在右边,就完美解决了。

但是如何实现“把等于基准元素的值放在中间”呢?

因为这题只是进行一趟排序,所以没有递归的过程,那么我们可以用一个cur指针来遍历数组中的元素,同样,我们也有leftright,分别记录中间和左边、中间和右边两个区间的边界。

那么当我们的cur碰到比key大的元素,就把它和right前一个数进行交换,并且right--。同理,当cur碰到比key小的元素,就把它和left后一个数进行交换,并且left++, cur++

重点是,当cur碰到和key相等的元素时,就跳过,将它留在中间区域,这样就实现了“把等于基准元素的值放在中间”。

可能有细心的小伙伴就发现了,欸这里的right--怎么没有cur++呢?这是因为我们在交换的时候可能会把后面的另一个大数给交换回来,如果我们进行cur++,就跳过了这次比较,有漏网之鱼了哦。

cpp代码

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int left = -1, right = nums.size();
        int key = 1;
        int cur = 0;
        while(cur < right){
            if(nums[cur] < key){
                swap(nums[cur++], nums[++left]);
            }
            else if(nums[cur] > key){
                swap(nums[cur], nums[--right]);
            }
            else{
                cur++;
            }
        }
    }
};

数组中的第K个最大元素 (leetcode215)

题目🔗

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104

思路:快速选择

其实回看每一轮快速排序,就已经确定基准元素的位置,那么我们就可以把快排简化成,去确定第k-1个位置的数值(这里我们是从大到小进行排列)。

这里我们要考虑基准元素的选取问题,为了避免当选择边界作为基准,对于已经有序的区间而出现时间复杂度退化的情况,我们选取中间元素作为基准,也即int mid = left + (right - left) / 2;,同时将它和边界最右侧的元素进行互换swap(nums[mid], nums[right]),保证它在边界处不会被覆盖,这样我们的基准元素就变为了nums[right]

我们用int lint r分别从左右两侧进行判断,l从左侧找比nums[right]小的数,然后r从右侧找比nums[right]大的数,最后将两数位置交换,变成有序的状态。

那么最后的基准元素nums[right]应该放哪呢
我们结束循环的条件是l == r,且最后一定是以l的while循环结果结束,那么这个时候r一定是指向比nums[right]小的数,因为在[r, right)区间中,所有数都是满足比nums[right]小的。现在l == r,说明lr都指向比nums[right]小的数,那我们直接把nums[right]num[l]交换就可以了。

然后我们通过判断这一轮确定下来的基准元素位置和k的大小,再去选择性的进行下一次排序确定,最后当某一轮确定的基准元素位置和k相等时,我们就可以返回结果了

cpp代码

class Solution {
private:
    int quickSortKthElement(vector<int>& nums, int k, int left, int right){
        // 选择中间元素作为基准元素,避免有序情况下的退化
        int mid = left + (right - left) / 2;
        swap(nums[mid], nums[right]);   // 将基准元素放在最右边,防止被覆盖
        int l = left, r = right;
        while(l < r){
            while(l < r && nums[l] >= nums[right]) l++; // 循环直到找到左边部分比基准元素小的
            while(l < r && nums[r] <= nums[right]) r--; // 循环直到找到右边部分比基准元素大的
            if(l < r){
                swap(nums[l], nums[r]); // 交换大小元素
            }
        }
        swap(nums[l], nums[right]); // 最后将基准元素放在l的位置
        if(l == k - 1) return nums[l];
        else if(l > k -1) return quickSortKthElement(nums, k, left, l - 1);
        else return quickSortKthElement(nums, k, l + 1, right);
    };
public:
    int findKthLargest(vector<int>& nums, int k) {
        return quickSortKthElement(nums, k, 0, nums.size() - 1);
    }
};
  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值