首页 > 其他分享 >不改变相对顺序,负数左边正数右边

不改变相对顺序,负数左边正数右边

时间:2024-03-16 18:23:22浏览次数:24  
标签:右边 nums int 复杂度 mid 负数 正数 left

题目

给定一个只包含正数和负数的数组,不改变正数之间的相对顺序,以及负数之间的相对顺序,重新排列数组,使得负数位于正数之前。
举例:如:[1, 7, -5, 2, -9, 3] 变成 [-5, -9, 1, 7, 2, 3] 使得所有负数位于左边,正数位于右边,且没有改变正数,以及负数在原始数组中的相对位置。

解题思路

这道题是拼多多 1 面问的问题。一开始我想到的是通过冒泡排序的方式,将负数一个个往前冒,时间复杂度是 O(n2)。

后来面试官要求解题方法的时间复杂度是 O(nlogn),空间复杂度是 O(1),这个就把我难倒了,但是从时间复杂度上我猜测到应该是用归并或者快排的思路去做,但是归并的空间复杂度是O(n),所以我就想用快排,但是快排不是稳定O(nlogn),而且快排来做也没有思路。

面试官提示用归并来实现,但是我没有想出来怎么能让 merge 操作不用到额外的空间。后来我线下去看了下解题方案,没有现成的答案,但是我找到了一篇将关于《翻手算法》的文章,利用线性代数的转置思路求解,可以实现时间复杂度O(n),空间复杂度O(1)。

翻手算法的思路是这样的,举例,比如我们要把数组[1,2,3,4,5],转换成[4,5,1,2,3],也就是将 4,5和 1,2,3换一下位置。算法步骤:
步骤 1:将 1,2 转置下,变为 2,1。数组变为:[2,1,3,4,5]
步骤 2:将 3,4,5 转置下,变为5,4,3。数组变为:[2,1,5,4,3]
步骤 3:将整个数组转置下,就变为:[3,4,5,1,2]
转置的意思就是将对应的数前后颠倒下。1,2 → 2,1 3,4 → 4,3 1,2,3 → 3,2,1 1,2,3,4 → 4,3,2,1
更详细的解释可以看下这篇文章:https://blog.csdn.net/KeepThinking_/article/details/8771873,截图了文章中核心部分。

代码实现

`

public static void main(String[] args) {
    int[] nums = {1, 7, -5, 2, -9, 3};
    divide(nums, 0, nums.length - 1);
    System.out.println(Arrays.toString(nums));
}

/**
 * 归并算法的分治思想,将 nums 数组拆分成两部分,然后分别对两部分进行归并。
 * divide 方法的空间复杂度为 O(1),时间复杂度为 O(logn)
 */
public static void divide(int[] nums, int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        divide(nums, left, mid);
        divide(nums, mid + 1, right);
        merge(nums, left, mid, right);
    }
}

/**
 * 将归并后的部分,两两比较翻转。
 * merge 方法的空间复杂度为 O(1),时间复杂度为 O(n)
 */
public static void merge(int[] nums, int left, int mid, int right) {
    // 找到第1部分的第一个正数位置
    int leftPoint = -1;
    for (int i = left; i < mid + 1; i++) {
        if (nums[i] > 0) {
            leftPoint = i;
            break;
        }
    }

    // leftPoint == -1 表示第1部分全是负数,那么就不用处理了,天然满足左负右正。
    boolean isAllNegative = leftPoint == -1;
    if (isAllNegative) {
        return;
    }

    // 找到第2部分最后一个负数位置
    int rightPoint = -1;
    for (int i = mid + 1; i <= right; i++) {
        if (nums[i] < 0) {
            rightPoint = i;
        }
    }

    // rightPoint == -1 表示第2部分全是正数,那么就不用处理了,天然满足左负右正。
    boolean isAllPositive = rightPoint == -1;
    if (isAllPositive) {
        return;
    }

    // 翻转第1部分的正数部分
    turnHand(nums, leftPoint, mid);
    // 翻转第2部分的负数部分
    turnHand(nums, mid + 1, rightPoint);
    // 翻转第1部分的正数部分和第2部分的负数部分
    turnHand(nums, leftPoint, rightPoint);
}

/**
 * 翻手,反转数组,调换数组中元素的前后位置
 */
public static void turnHand(int[] nums, int startPoint, int endPoint) {
    while (startPoint < endPoint) {
        int temp = nums[startPoint];
        nums[startPoint] = nums[endPoint];
        nums[endPoint] = temp;
        startPoint++;
        endPoint--;
    }
}

标签:右边,nums,int,复杂度,mid,负数,正数,left
From: https://www.cnblogs.com/loren-Yang/p/18077395

相关文章

  • js-正负数保留小数点特定位数
    functionround(num,iCount){//iCount保留几位小数letchangeNum=numletzs=true//判断是否是负数if(changeNum<0){changeNum=Math.abs(changeNum)z......
  • 关于js数组方法sort()负数排序的陷阱
    今天在刷力扣题的时候遇到数组排序的问题,想着图个方便就使用了arr.sort(),刚开始用正数进行测试用例的时候没有出错,问题:在使用负数的测试用例时,预期目标是 [-10,-2,-1...1,2,3],结果出现了 [-1,-2,-10......1,2,3]这样的结果解析:在网上找了一下发现,sort()这个方法:默认......
  • 【vue】做一个从右边出来又回去的一个抽屉div
    前言:工作需要做一个从右往左出现的一个弹窗,要有抽屉效果,看了网上各种方法好多都不能用,最后试了一种可以用,但是忘记是哪个网址了,现在就是自己写一下这个随便以后用到方便找。做一个从右边出来又回去的一个抽屉divhtml代码<divclass="addBtn"@click="show">点击按钮出......
  • 从右边开始寻找整数的第k位
    从右边开始寻找整数的第k位Implementmatch_k,whichtakesinanintegerkandreturnsafunctionthattakesinavariablexandreturnsTrueifallthedigitsinxthatarekapartarethesame.Forexample,match_k(2)returnsaoneargumentfunctionthattake......
  • elementUI表格滚动条样式修改,隐藏表格右边留白
    修改滚动条样式//设置滚动条的宽度.el-table__body-wrapper::-webkit-scrollbar{width:4px;}//设置滚动条的背景色和圆角.el-table__body-wrapper::-webkit-scrollbar-thumb{background-color:#535353;-webkit-box-shadow:inset005pxrgba(0,0,0,0.2......
  • 41. 缺失的第一个正数
    1.题目介绍给你一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为O(n)并且只使用常数级别额外空间的解决方案。示例1:输入:nums=[1,2,0]输出:3示例2:输入:nums=[3,4,-1,1]输出:2示例3:输入:nums=[7,8,9,11,12]输出:12.题解2.1......
  • vim编辑器实现左边目录右边是文件内容
    转自https://blog.csdn.net/cui_yonghua/article/details/131657518需要使用nerdtree工具。1、安装(1)下载压缩文件wgethttp://www.vim.org/scripts/download_script.php?src_id=17123-Onerdtree.zip(2)解压mkdirnerdtreeunzipnerdtree.zip-dnerdtree/(3)创建plugin,doc......
  • 输入一个整数,将这个整数以字符串的形式逆序输出 程序不考虑负数的情况,若数字含有0,则逆
    描述输入一个整数,将这个整数以字符串的形式逆序输出程序不考虑负数的情况,若数字含有0,则逆序形式也含有0,如输入为100,则输出为001数据范围:0\len\le2^{30}-1\0≤n≤230−1输入描述:输入一个int整数输出描述:将这个整数以字符串的形式逆序输出点击查看代码#include<i......
  • 【力扣】-41. 缺失的第一个正数|刷题打卡-JS
    给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。示例1:输入:nums=[1,2,0]输出:3示例2:输入:nums=[3,4,-1,1]输出:2示例3:输入:nums=[7,8,9,11,12]输出:1提示:1<=nums.length<=5*......
  • std::max、std::min error C2589: “(”:“::”右边的非法标记,error C2059: 语法错误:
    个人采用方案三解决问题。在VC++种同时包含头文件#include<windows.h>和#include<algorithm>后就会出现无法正常使用std标准库中的min和max模板函数,经过查阅发现这是因为在Windows.h种也有min和max的定义,这样就导致了algorithm中的min和max无法正常使用,这里给出两种解决方案,来......