在循环递增一次的数组中插入元素

文章目录

  • 题目
  • 思路
    • 如何建立左右区间?
    • 如何查找最高点?
    • 那我们怎么判断 `num` 到底处于什么样的位置呢?
    • 如何确定插入位置?
    • 插入元素
  • 代码


题目

给一个只循环递增一次的数组 res,res 满足首元素大于等于尾元素,形如:

4,5,6,7,2,4

再给出一个整型数字 num,将其插入到数组中应在的位置。

示例:

输入:

res = 4,5,6,7,2,4
num = 3

输出:

4,5,6,7,2,3,4


思路

用二分查找确定应该插入的位置,难点在于左右区间的建立。

如何建立左右区间?

首先明确两点,对于整个数组而言:

  • 首元素一定 大于等于 尾元素
  • 以数组的最大值为界限,最大值左边的元素一定 大于等于 右边的元素

用图像表示数组是这样的(黑色表下标,紫色表值):
在这里插入图片描述

我们可以看到,要插入的元素无非在最高点的左边或右边(自下文始,最高点用high替代,首元素位置用begin表示,尾元素位置用last表示):

  • num 处于最高点左边时,二分查找的范围应该是 [begin, high]
  • num 处于最高点右边时,二分查找的范围应该是 [high+1,last]

也就是说,划定二分查找范围(左右区间的建立)的重中之重在于最高点的确定。

如何查找最高点?

个人想到两种方法:

  • 遍历查找,时间复杂度O(n)

算法思想没有什么多说的,中规中矩的遍历。

int high = 0;
for (int i = 1; i < res.size(); i++) {if (res[high] > res[i]) {break;}high = i;
}
  • 二分查找,时间复杂度O(log2n)

算法思想是:

  1. 先以数组首元素、尾元素作为二分查找的左右边界,中间元素暂定为high
  2. 左边界小于右边界 作为while的循环条件
  3. 首先判断此时的high是否大于首元素
  4. 大于首元素证明此时的high处于 真正最高点的左边就是真正最高点,此时需要判定high+1中元素和high中元素之间的关系:
  1. 如果high+1中元素大于high中元素,表明 真正最高点应该在high的左边,因此更新左边界—— left = high + 1
  2. 如果high+1中元素小于high中元素,表明 high即为真正最高点 ,因此break出while循环即可
  1. 小于首元素证明此时的high处于 真正最高点的右边 ,此时需要判定high和high-1所指元素之间的关系:
  1. 如果 res[high - 1] < res[high] (如举例中high=6时,4>3),表明最高点仍在 high-1的左边,也就是high-1也处于真正最高点的右边,因此要更新右边界—— right = high - 2
  2. 如果 res[high - 1] > res[high] , 此时表明high-1即为最高点,因此将high–并break出while循环即可
  1. 每次循环更新完左右边界之后需要更新high值—— high = left + (right - left) / 2;
  2. 跳出while循环得到的high即为最高点的位置
int size = res.size() - 1;
int left = 0;
int right = size;
int high = left + (right - left) / 2;
int flag = res[0];
while (left < right) {if (res[high] >= flag) {if (res[high + 1] > res[high]) {left = high + 1;}else {break;}}else {if (res[high - 1] < res[high]) {right = high - 2;}else {high--;break;}}high = left + (right - left) / 2;
}

那我们怎么判断 num 到底处于什么样的位置呢?

这时应该结合之前我们说到的一句话:

首元素一定 大于等于 尾元素

那我们做个归纳,如果:

  1. num > res[0] ,num 处于 high 左边
  2. res[end] < num == res[0] ,num 处于 high 右边,插入到尾元素的位置
  3. res[end] == num == res[0] ,不可能出现这种情况,因为这种情况下num没有位置可以插入。
  4. res[end] == num < res[0] ,num 处于 high 左边,插入到首元素的位置
  5. res[end] > num ,num 处于 high 右边

第二点和第四点是什么意思呢?

关于第二点,我们举这样一个例子:

输入:

res = 5,6,7,2,4
num = 5

输出:

res = 5,6,7,2,4,5

关于第四点,我们举这样一个例子:

输入:

res = 6,7,2,4,5
num = 5

输出:

res = 5,6,7,2,4,5

因此根据上面的归纳我们可以得到代码:

注意:因为第三种情况不可能出现,因此我们在描述第2、4种情况时可以省略大小比较,因为当2、4种描述的等于关系成立时,大小关系必然成立。

if (res[size] > num || num == res[0]) { // num处于high右边left = high + 1;right = size;
}
if(num > res[0] || num == res[size]) { // num处于high左边left = 0;right = high;
} 

如何确定插入位置?

建立好左右边界后就可以根据二分查找来确定插入位置了。

  1. 当左边界小于右边界时执行二分查找。
  2. 中间点(mid)对应的元素小于 num 时,左边界更改为 mid+1
  3. mid 对应的元素大于 num 时,右边界更改为 mid-1
int mid = left + (right - left) / 2;
while (left <= right)
{if (res[mid] < num) {left = mid + 1; }else {right = mid - 1;}mid = left + (right - left) / 2;
} 

插入元素

最终使用insert函数进行插入:

res.insert(res.begin() + mid, num);

insert会将给定值插入到给定位置之前。


代码

class Solution {
public:vector<int> fun(vector<int> res, int num) {int size = res.size() - 1;int left = 0;int right = size;/*int high = 0;for (int i = 1; i < res.size(); i++) {if (res[high] > res[i]) {break;}high = i;}*/// 与上面注释部分一样都是查最高点int high = left + (right - left) / 2;int flag = res[0];while (left < right) {if (res[high] > flag) {if (res[high + 1] > res[high]) {left = high + 1;}else {break;}}else {if (res[high - 1] < res[high]) {right = high - 2;}else {high--;break;}}high = left + (right - left) / 2;}// 确定左右边界if (res[size] > num || num == res[0]) { // num处于high右边left = high + 1;right = size;}if(num > res[0] || num == res[size]) { // num处于high左边left = 0;right = high;} // 确定插入位置int mid = left + (right - left) / 2;while (left <= right){if (res[mid] < num) {left = mid + 1; }else {right = mid - 1;}mid = left + (right - left) / 2;} res.insert(res.begin() + mid, num);return res;}
};

用例测试:

int main() {Solution s;vector<int> iv = { 4,5,6,7,1,2,4 };int num = 3;iv = s.fun(iv, num);for (auto i = iv.begin(); i != iv.end(); i++) {cout << *i << " ";}cout << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443795.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

表示数值的字符串(有限状态自动机与搜索)

文章目录题目思路一代码一思路二代码二题目 思路一 考察有限状态自动机&#xff08;参考jyd&#xff09;&#xff1a; 字符类型&#xff1a; 空格 「 」、数字「 0—9 」 、正负号 「 」 、小数点 「 . 」 、幂符号 「 eE 」 。 状态定义&#xff1a; 按照字符串从左到右的…

树的子结构

文章目录题目深搜深搜代码广搜广搜代码题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 给定的树 B&#xff1a; 返回 true&#xff0c;因为…

写题过程中碰见的小问题

文章目录和--vector二维vector的初始化数组中最大的数max_element()数组所有元素之和accumulate()vector数组去重对pair类型的vector排序对元素都为正整数的vector利用sort默认的升序排列进行降序排列一维数组转二维数组size_t和int如何不用临时变量交换两个数?将类函数的形参…

LeetCode——二叉树序列化与反序列化

文章目录题目思路问题一问题二代码实现题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 设计一个算法来实现二叉树的序列化与反序列化。不限定序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序…

jsp中生成的验证码和存在session里面的验证码不一致的处理

今天在调试项目的时候发现&#xff0c;在提交表单的时候的验证码有问题&#xff0c;问题是这样的&#xff1a;就是通过debug模式查看得知&#xff1a;jsp页面生成的验证码和表单输入的页面输入的一样&#xff0c;但是到后台执行的时候&#xff0c;你会发现他们是不一样的&#…

求1~n这n个整数十进制表示中1出现的次数

文章目录题目思路代码复杂度分析题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;那么1&#xff5e;12这些整数中包含1 的数字有1、10、11和12。可得1一共出现了5次。 思路 将个位、十位……每位…

求数字序列中的第n位对应的数字

文章目录题目思路代码复杂度分析致谢题目 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中&#xff0c;第5位&#xff08;从下标0开始计数&#xff09;是5&#xff0c;第13位是1&#xff0c;第19位是4&#xff0c;等等。 请写一个函数&#xff0c…

一学就废的归并排序

文章目录其他与排序有关的文章原理代码实现复杂度分析其他与排序有关的文章 一学就废的三种简单排序【冒泡、插入、选择】 原理 归并排序&#xff08;Merge sort&#xff09;&#xff1a; 归并排序对元素 递归地 进行 逐层折半分组&#xff0c;然后从最小分组开始&#xff0c…

树状数组的相关知识 及 求逆序对的运用

文章目录树状数组概念前缀和和区间和树状数组原理区间和——单点更新前缀和——区间查询完整代码离散化sort函数unique函数去重erase函数仅保留不重复元素通过树状数组求逆序对树状数组概念 树状数组又名二叉索引树&#xff0c;其查询与插入的复杂度都为 O(logN)&#xff0c;其…

二叉搜索树相关知识及应用操作

文章目录概念查找二叉搜索树的第k大节点概念 二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又名&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;——它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若它的…

二叉树相关知识及求深度的代码实现

文章目录树二叉树满二叉树和完全二叉树二叉树的性质代码实现求二叉树的深度树 树是一种非线性的数据结构&#xff0c;它是由n个有限结点组成一个具有层次关系的集合。 树的相关名词&#xff1a; 根节点&#xff1a;没有前驱结点的结点。父节点&#xff0c;子节点&#xff1a…

大端小端存储模式详解及判断方法

文章目录大小端模式的概念两种模式出现原因两种模式的优劣大小端的应用情景判断机器的字节序大小端模式的概念 当我们查看数据在内存中的存储情况时&#xff0c;我们经常会发现一个很奇怪的现象&#xff0c;什么现象呢&#xff1f; int main() {int i 12;return 0; }数据在内…

Linux 内存管理 | 物理内存、内存碎片、伙伴系统、SLAB分配器

文章目录物理内存物理内存分配外部碎片内部碎片伙伴系统(buddy system)slab分配器物理内存 在Linux中&#xff0c;内核将物理内存划分为三个区域。 在解释DMA内存区域之前解释一下什么是DMA&#xff1a; DMA&#xff08;直接存储器访问&#xff09; 使用物理地址访问内存&am…

Linux 内存管理 | 虚拟内存管理:虚拟内存空间、虚拟内存分配

文章目录虚拟地址空间用户空间内核空间用户空间内存分配malloc内核空间内存分配kmallocvmalloc虚拟地址空间 在早期的计算机中&#xff0c;程序是直接运行在物理内存上的&#xff0c;而直接使用物理内存&#xff0c;通常都会面临以下几种问题&#xff1a; 内存缺乏访问控制&a…

Linux | 编译原理、gcc的命令参数、自动化构建工具 make/Makefile

文章目录编译原理预处理编译汇编链接gcc的常用命令参数make 和 Makefile 的概念make的运行通配符自动化变量伪目标.PHONE:【命令】编译原理 在解释 makefile 前&#xff0c;首先解释一下 .c 文件变成 .exe 文件要经过的四个步骤——预处理、编译、汇编和链接&#xff08;参考来…

Linux | 进程概念、进程状态(僵尸进程、孤儿进程、守护进程)、进程地址空间

文章目录进程和程序操作系统如何控制和调度程序进程控制块–PCB子进程进程状态僵尸进程孤儿进程守护进程&#xff08;精灵进程&#xff09;进程地址空间引言页表进程和程序 程序&#xff1a; 一系列有序的指令集合&#xff08;就是我们写的代码&#xff09;。进程&#xff1a;…

Linux 进程控制 :进程创建,进程终止,进程等待,程序替换

文章目录进程创建进程等待程序替换进程终止进程创建 fork函数&#xff1a; 操作系统提供的创建新进程的方法&#xff0c;父进程通过调用 fork函数 创建一个子进程&#xff0c;父子进程代码共享&#xff0c;数据独有。 当调用 fork函数 时&#xff0c;通过 写时拷贝技术 来拷贝…

Linux 内存管理 | 连续分配方式 和 离散分配方式

文章目录前言连续分配单一连续分配分区式分配固定分区分配动态分区分配可重定位分区分配离散分配分段分页多级页表快表(TLB)段页式Linux前言 Linux 内存管理 | 虚拟内存管理&#xff1a;虚拟内存空间、虚拟内存分配 Linux 内存管理 | 物理内存、内存碎片、伙伴系统、SLAB分配器…

操作系统 | 用户态和内核态的切换(中断、系统调用与过程(库函数)调用)

文章目录中断过程调用系统调用过程调用和系统调用的区别中断 用户态、内核态之间的切换是怎么实现的? 用户态→内核态 是通过中断实现的。并且 中断是唯一途径 。核心态→用户态 的切换是通过执行一个特权指令&#xff0c;将程序状态字 (PSW) 的标志位设置为 用户态 。 中断…

管道实现父子进程的信息传递(二)【标准流和其文件描述符、fwrite函数、perror函数】

文章目录代码实现标准流 和 标准流文件描述符代码中用到的函数fwrite()perror()在复习进程间的通信方式时又写了一遍&#xff0c;和 管道实现父子进程的信息传递&#xff08;一&#xff09;【fork函数、pipe函数、write/read操作、wait函数】 的区别不是特别大&#xff0c;只是…