[C++&Rust]LeetCode 81. 搜索旋转排序数组 II(每日一题)

本文介绍了LeetCode第81题的解决方案,该题要求在旋转排序数组中查找目标值。通过分析,我们可以使用两次二分查找来解决此问题。首先找到数组的转折点,然后在有序部分进行二分查找目标值。文章还提到了特殊情况下的O(n)线性搜索方法,并提及Rust语言的快速排序算法。
摘要由CSDN通过智能技术生成

大家好!今天给大家带来的是力扣第81题81. 搜索旋转排序数组 II。
原贴地址:http://leanote.com/blog/post/606d9d79ab644172650003ed

题目

已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。

给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路分析

这道题一看特别有规律,因为是一个有序数组被打乱,而且是从一个节点打乱。我们只需找出这个节点就可以恢复之前的有序数组,那么我们只需要使用一次二分查找就可以找出来这个数字是否存在了。
但是我们思考一个问题,我们寻找这个节点用的是什么方法呢?必须是一个O(nlgn)的方法吧,要不然我们为何不直接使用O(n)的遍历去寻找呢?所以我们确定了我们的思路,就是先使用二分查找找到原数组被打乱的位置,还原出原来的数组,再对原数组进行一次二分查找定位我们的target。
思路很美好,可是我们没有写过这种奇奇怪怪的二分耶
那怎么办呢,我们就得思考一下这个二分如何操作了。
我们先做出一个公认的定义:转折点,就是该数组被打断的节点,也就是原数组的末尾
我们举一个例子:
假设该数组是[7,8,2,3,4,5,6]
他的转折点位于8的位置
按照二分的操作,我们先取出一个中间值,对比一下,有三种情况

n[left]==n[mid]这种情况我们没法区分转折点在哪里,所以我们只能使用O(n)的方法进行寻找,即让left节点向右单步移动


n[left] < n[mid]这种情况来看,mid以左的部分是有序的,转折点可能在mid及mid的右方。所以我们得分两种情况讨论:

1.nums[start] <= target < nums[mid]这就说明target有可能出现的地方只能是mid的左边,我们对左边进行二分(right = mid - 1)
2.否则我们就对右边进行二分(left = mid + 1)


n[left] > n[mid]这种情况来看,mid以右的部分是有序的,转折点可能在mid及mid的左方。所以我们还是得分两种情况讨论:

1.nums[mid] <= target < nums[right]这就说明target有可能出现的地方只能是mid的右边,我们对右边进行二分(left = mid + 1)
2.否则我们就对左边进行二分(right = mid - 1)

思路讲完了,接下来我们看一下代码。

class Solution {
public:
    bool search(vector<int> & nums, int target) {
        if (nums.size() == 0) return false;
        int left = 0, right = nums.size() - 1, mid;
        while (left <= right) {
            mid = left + (right - left) / 2;
            if (nums[mid] == target) return true;
            if (nums[left] == nums[mid]) {
                left++;
                continue;
            }
            if (nums[left] < nums[mid]) 
                if (nums[mid] > target && nums[left] <= target) right = mid - 1;
                else left = mid + 1;
            else 
                if (nums[mid] < target && nums[right] >= target) left = mid + 1;
                else right = mid - 1;
        }
        return false;
    }
};

其实有心的小伙伴已经发现了,我们这个程序的整体复杂度已经被特殊情况提升到O(n)了,所以其实也可以简单遍历哦!(是不是很想打人)

class Solution {
public:
    bool search(vector<int> & nums, int target) {
        for (auto & num : nums) if (num == target) return true;
        return false;
    }
};

接下来还有个知识点想跟大家分享一下,就是我们一般使用的排序都是O(nlgn)的,但是rust作为一个新兴语言,使用的排序算法自然也很先进,平均速度竟然达到了惊人的O(n)级别,实在是太快啦!

impl Solution {
    pub fn search(nums: Vec<i32>, target: i32) -> bool {
        let mut nums = nums;
        nums.sort_unstable();
        if let Ok(_) = nums.binary_search(&target) {
            return true;
        }
        return false;
    }
}

注意:要使用排序的时候请注意,rust提供了sort和sort_unstable两个函数,第一个是稳定排序,第二个是不稳定排序,就是值相同的元素在稳定排序之后的相对位置不会改变,但是在不稳定排序之后就是完全不确定的相对位置,但我们只是排序几个简单的小数字,这种东西当然不会考虑啦!直接上unstalbe,又把C++甩开好几个身位了呢!(根据统计,rust在刷题时相比C++有3-5倍的速度提升,内存占用就更小了呢!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Rust 是一种现代的编程语言,特别适合处理内存安全和线程安全的代码。在 LeetCode 中,链表是经常出现的题目练习类型,Rust 语言也是一种非常适合处理链表的语言。接下来,本文将从 Rust 语言的特点、链表的定义和操作,以及 RustLeetCode 中链表题目的练习等几个方面进行介绍和讲解。 Rust 语言的特点: Rust 是一种现代化的高性能、系统级、功能强大的编程语言,旨在提高软件的可靠性和安全性。Rust 语言具有如下几个特点: 1. 内存安全性:Rust 语言支持内存安全性和原语级的并发,可以有效地预防内存泄漏,空悬指针以及数据竞争等问题,保证程序的稳定性和可靠性。 2. 高性能:Rust 语言采用了“零成本抽象化”的设计思想,具有 C/C++ 等传统高性能语言的速度和效率。 3. 静态类型检查:Rust 语言支持静态类型检查,可以在编译时检查类型错误,避免一些运行时错误。 链表的定义和操作: 链表是一种数据结构,由一个个节点组成,每个节点保存着数据,并指向下一个节点。链表的定义和操作如下: 1. 定义:链表是由节点组成的数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。 2. 操作:链表的常用操作包括插入、删除、查找等,其中,插入操作主要包括在链表首尾插入节点和在指定位置插入节点等,删除操作主要包括删除链表首尾节点和删除指定位置节点等,查找操作主要包括根据数据元素查找节点和根据指针查找节点等。 RustLeetCode 中链表题目的练习: 在 LeetCode 中,链表是常见的题目类型,而 Rust 语言也是一个非常适合练习链表题目的语言。在 Rust 中,我们可以定义结构体表示链表的节点,使用指针表示节点的指向关系,然后实现各种操作函数来处理链表操作。 例如,针对 LeetCode 中的链表题目,我们可以用 Rust 语言来编写解法,例如,反转链表,合并两个有序链表,删除链表中的重复元素等等,这样可以更好地熟悉 Rust 语言的使用和链表的操作,提高算法和编程能力。 总之,在 Rust 中处理链表是非常方便和高效的,而 LeetCode 中的练习也是一个非常好的机会,让我们更好地掌握 Rust 语言和链表数据结构的知识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值