算法新解前言:最小可用id求解

算法新解前言:最小可用id求解

前言

这是我第一次在csdn上写博客,主要是打算记录一下自己学习算法的一些过程。

遥想大一当年学姐建议我去学习一下算法,时光匆匆,四年转眼即逝,已经研一了,发现这四年确实走了不少弯路。最近发生了几件事,让我萌生了写算法博客的想法。

一是这学期有一门算法课,汤老师虽然讲得很快,不过思路确实讲得好,但是光听课不实践,学习效果就会差很多;
二是有一个学弟学习算法遇到了瓶颈,考PAT总是不顺利,让我想起大二时的自己,那时也是一脸茫然,没有人指引,算法学习之路确实困难;
三是最近打算写了一些算法题,发现自己的算法基础倒退得有点厉害,因此利用写博客的机会理清算法思路。

最重要的一点,就是为了改变自己在本科时期的缺点:做一件事不够坚持,我希望自己能在平常做项目、写文档的同时,依然能坚持学习算法,体会到算法的乐趣。

准备工作

机器:小米笔记本pro15.6,16GB内存+256GB SSD/第八代i7处理器
IDE: Clion
语言:主要为C++11
学习思路:算法思路最重要,同时也不应忽视实现细节
推荐书籍:数据结构与算法分析:C语言描述 + 算法第四版
在这里插入图片描述

在这里插入图片描述
当然算法的经典书籍很多,不过这两本打基础绝对是够了。

题目:最小可用id

介绍:

在这里插入图片描述

1、暴力法

书本中提到的第一个解法,即题目中伪代码描述的一样:
(1)从0开始,检查当前id是否已被使用,是则查询下一个id,不是则返回当前id;
(2)每一次查询当前id的状态,采用的方法是遍历已使用的id列表;
(3)该解法的时间复杂度为o(n^2)

暴力法虽然效率低,但是算法求解首先要解决的问题是正确性问题,其次再考虑效率问题,因此我们应该尽快用最直观的方法来获取初始版本的算法,方便验证之后算法的正确性。

2、用空间换时间法

书本中提到的第二个解法。
一般来说,如果想要提高算法的效率,就需要仔细分析问题,
利用问题的特性进行求解,那么这个问题有哪些特性呢?
(1)id从0开始
(2)寻找的是最小可用id
=> 得出结论
(3)已用id列表为A,列表的大小|A|=n,则
			minid <= n
此结论简单用反证法即可证明,此处不赘述。
因为最小可用id不大于n,因此我们仅需使用一个长度为n+1的列表,
记录不大于n的id已使用时
伪代码如下:

在这里插入图片描述

在没有想出更高效方法的情况下,可以使用空间换时间的方法。

3、先排序后查找

我想到的第一个算法,利用问题寻找最小元素的特性。
看到“最小”,第一反应是一定会运用到排序的思想。
我们观察这样一组数据:
{0,1,2,3,4,6,7,9,10}
不难发现最小可用id(unused_min_id)是5,更关键的是以下性质:
(1)array[i] == i , i < unused_min_id;
(2)array[i] != i , i >= unused_min_id;
  =>
(3)如果整个列表都符合性质(1),则最小可用id是len(array) + 1,
	否则最小可用id就是已用id列表中第一个符合性质(2)的元素下标,

代码如下:

int sort_search(int* array, int array_size){
   
    //先排序

    sort(array,array + array_size);

    //后查找

    for (int i = 0; i < array_size; ++i) {
   
        if(array[i] != i){
   
            return i;
        }
    }

    return array_size;
}

不难得出,时间复杂度为o(nlogn)。

4、分治法求解

我想到第二个解法,是受上一个解法启发的。
先排序后查找,这不是和第K小数的普通求解方法一致吗?

讲到第k小数,就不能不讲到快排过程。
快排的核心思想:
(1)在一次快排交换过程中选出一个标杆值,在此过程中标杆值不变。
(2)通过元素与标杆值比较,找出位置靠前的大数和位置靠后的小数,进行交换。
(3)全部元素扫描完则交换结束,将标杆值放在对应位置,
形成以标杆值下标为界,左区间元素均不大于右区间元素的左小右大局面。
(4)分而治之,继续在左区间、右区间递归地执行排序。
(5)递归终止条件:区间内元素无需排序(没有或只有一个元素)
4.1快排介绍

快排方法很多,简单起见,选择最简单的单向扫描版快排如下:

void quick_sort(int* array,int left,int right){
   
    //递归终止条件:没有元素或者只有一个元素,无需进行排序

    if(left + 1>= right){
   
        return;
    }
    //选出标杆值,交换过程中不移动

    int
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值