`
javasalatu
  • 浏览: 756795 次
  • 性别: Icon_minigender_2
  • 来自: 北京
博客专栏
96df99eb-e89d-3228-9c8e-967fc745ec52
程序员的自我经营之道
浏览量:7821
文章分类
社区版块
存档分类
最新评论

腾讯面试题(除掉N个整数中重复数)解题(线性时间,原地置换排序算法)(已修正)

 
阅读更多

题目:一个大小为N的数组,里面是N个整数,怎样去除重复,
要求时间复杂度为O(n),空间复杂度为O(1).

//下面的思路没问题,但算法有问题,修正后的算法见后面.

/// <summary>
/// 需要除掉重复的整数的数组,注意这里我没有处理负数情况,
/// 其实负数情况只要先用0快排分一下组,然后各自用以下算法进行处理即可。
/// 另外因为是整数,这里没考虑32位符号位,只考虑31位。
/// 题目分析:从要求来看,如果一个数组是排好序的,除掉重复就很简单,因此就转换成了
/// 排序算法寻找,这种算法需要满足:线性时间,常量内存,原地置换。但纵观这么多算法,比较排序肯定不行,
/// 那么就只有基数排序,桶排序和计数排序,但基数排序依赖于位排序,而且要求位排序是稳定的,
/// 且不能过多使用辅助空间,计数排序排除,因为计数排序无法原地置换,桶排序也需要辅助空间,所以最后考虑用
/// 基数排序。但问题是如何选择位排序,因为位上只有0和1,因此有其特殊性,使用快排的分组就可以达到线性,
/// 但问题是这种算法虽然是线性,原地置换,但不稳定。所以要利用一种机制来确保快排是稳定的。经过一段时间思考,
/// 发现,如果从高位开始排序,假设前K位是排好的,对K+1进行排序时,只针对前K位的相同的进行,前K位不相同,也不可
/// 能相等,第K+1位也不影响结果,而前K位相同的排序,就不怕快排的不稳定了,因为这个不稳定不会影响到最终结果。
/// 下面就是算法:
/// </summary>
/// <param name="A"></param>
private void BitSortAndDelRepeatorsA(int[] A)
{
//获取数组长度
int theN = A.Length;
//从高位到低位开始排序,这里从31位开始,32位是符号位不考虑,或者单独考虑。
for (int i = 31; i >= 1; i--)
{
//当前排序之前的值,只有该值相同才进行快排分组,如果不相同,则重新开始另外一次快排
//这很关键,否则快排的不稳定就会影响最后结果.
int thePrvCB = A[0] >> (i) ;
//快排开始位置,会变化
int theS = 0;
//快排插入点
int theI = theS;
//整数基元,就选择快排开始位置的数.
int theAxNum = A[theI];
//2进制基数,用于测试某一位是否为0
int theBase = 1 << (i-1);
//位基元,
int theAxBit = (theAxNum >> (i-1)) & 1;//(A[theI] & (theBase)) > 0 ? 1 : 0;
//分段快排,但总体上时间复杂度与快排分组一样.
for (int j = 1; j < theN; j++)
{
//获取当前数组值的前面已拍过序的位数值。
int theTmpPrvCB = A[j] >> (i);
//如果前面已排过的位不相同,则重新开始一次快排.
if (theTmpPrvCB != thePrvCB)
{
A[theS] = A[theI];
A[theI] = theAxNum;
theS = j;
theI = theS;
theAxNum = A[theI];
theAxBit = A[theI] & theBase;
thePrvCB = theTmpPrvCB;
continue;
}
//如果相同,则按快排处理
int theAJ = (A[j] >> (i - 1)) & 1;//(A[j] & (theBase)) > 0 ? 1 : 0;
if (theAJ <= theAxBit)
{
theI++;
int theTmp = A[j];
A[j] = A[theI];
A[theI] = theTmp;
}
}
//注意最后一次交换。
A[theS] = A[theI];
A[theI] = theAxNum;
}
}

除掉重复数:只要对上述排序结果进行一次遍历处理即可.

private int[] DeleteRepeatedInt(int[] A)
{
int N = A.Length;
//从低位到高位进行计数排序,因为是整数,这里假设都是正数.
for (int i = 1; i <= 32; i++)
{
CountSort2(A, i);
}
//除掉重复的数
int thePreNum = int.MinValue;
List<int> theRet = new List<int>();
for (int i = 0; i < N; i++)
{
if (A[i] != thePreNum)
{
theRet.Add(A[i]);
thePreNum = A[i];
}
}
return theRet.ToArray();
}

===================================================

排序算法修正部分

/// <summary>
/// 需要除掉重复的整数的数组,注意这里我没有处理负数情况,
/// 其实负数情况只要先用0快排分一下组,然后各自用以下算法进行处理即可。
/// 另外因为是整数,这里没考虑32位符号位,只考虑31位。
/// 题目分析:从要求来看,如果一个数组是排好序的,除掉重复就很简单,因此就转换成了
/// 排序算法寻找,这种算法需要满足:线性时间,常量内存,原地置换。但纵观这么多算法,比较排序肯定不行,
/// 那么就只有基数排序,桶排序和计数排序,但基数排序依赖于位排序,而且要求位排序是稳定的,
/// 且不能过多使用辅助空间,计数排序排除,因为计数排序无法原地置换,桶排序也需要辅助空间,所以最后考虑用
/// 基数排序。但问题是如何选择位排序,因为位上只有0和1,因此有其特殊性,使用快排的分组就可以达到线性,
/// 但问题是这种算法虽然是线性,原地置换,但不稳定。所以要利用一种机制来确保快排是稳定的。经过一段时间思考,
/// 发现,如果从高位开始排序,假设前K位是排好的,对K+1进行排序时,只针对前K位的相同的进行,前K位不相同,也不可
/// 能相等,第K+1位也不影响结果,而前K位相同的排序,就不怕快排的不稳定了,因为这个不稳定不会影响到最终结果。
/// 下面就是算法:
/// </summary>
/// <param name="A"></param>
private void BitSortAndDelRepeatorsA(int[] A)
{
//获取数组长度
int theN = A.Length;
//从高位到低位开始排序,这里从31位开始,32位是符号位不考虑,或者单独考虑。
for (int i = 31; i >= 1; i--)
{
//当前排序之前的值,只有该值相同才进行快排分组,如果不相同,则重新开始另外一次快排
//这很关键,否则快排的不稳定就会影响最后结果.
int thePrvCB = A[0] >> (i) ;
//快排开始位置,会变化
int theS = 0;
//快排插入点
int theI = theS-1;
//2进制基数,用于测试某一位是否为0
int theBase = 1 << (i-1);
//位基元始终为0,
int theAxBit = 0;

//分段快排,但总体上时间复杂度与快排分组一样.
for (int j = 0; j < theN; j++)
{

//获取当前数组值的前面已拍过序的位数值。
int theTmpPrvCB = A[j] >> (i);
//如果前面已排过的位不相同,则重新开始一次快排.
if (theTmpPrvCB != thePrvCB)
{
theS = j;
theI = theS - 1;
theAxBit = 0;
thePrvCB = theTmpPrvCB;
j--;//重新开始排,回朔一位.
continue;
}
//如果前面的数相同,则寻找第1个1,thI指向其
//如果相同,则按快排处理
int theAJ = (A[j] & (theBase)) > 0 ? 1 : 0; ;//(A[j] & (theBase)) > 0 ? 1 : 0;(A[j] >> (i - 1)) & 1
//如果是重新开始排,则寻找第1个1,并人theI指向其.这可以减少交换,加快速度.
if (theI < theS)
{
if (theAJ == 0)
{
continue;
}
theI = j;//Continue保证J从theI+1开始.
continue;
}
//交换.
if (theAJ <= theAxBit)
{
int theTmp = A[j];
A[j] = A[theI];
A[theI] = theTmp;
theI++;
}
}
}
}

经过测试,算法复杂度<32*n。

后记:其实这个面试题的实用价值还是非常大的,这里是整数,如果是字符串排序也可以采用类似算法,而且空间要求比较小。

声明:该算法是本人的原创算法,转载请注明出处,谢谢!

注,本算法也不是稳定的.

另外:面试题集锦,大家可以去July的博客看看,会很有收获:http://blog.csdn.net/v_JULY_v

分享到:
评论

相关推荐

    依次去掉n中的某一位数字,得到m个整数,并将这m个整数按从小到大的次序排列后输出.docx

    函数`depart`中,数组`a`用来接收从整数`n`中去除某一位后的结果,`sort`和`output`函数则分别对这个数组进行排序和打印。 2. **冒泡排序**:冒泡排序是一种简单的排序算法,它重复地遍历待排序的数列,一次比较两...

    JAVA经典算法40面试题

    JAVA经典算法40面试题 本资源摘要信息涵盖了JAVA经典算法40面试题,包含基本的算法面试代码题。以下是对标题、描述、标签和部分内容的详细解释: 一、标题:“JAVA经典算法40面试题” 该标题表明该资源是关于JAVA...

    JAVA经典算法面试39题及答案

    JAVA经典算法面试39题及答案 本资源总结了39道经典的 JAVA 算法面试题目,每个题目都附带答案,涵盖了常见的算法问题,旨在帮助读者更好地掌握 JAVA 编程语言和算法设计。 算法概述 算法是计算机科学中最重要的...

    前端面试题:常用算法.pdf

    ### 前端面试题:常用算法 #### 时间复杂度 在评估算法性能时,我们通常关注的是**时间复杂度**。它用来描述算法运行时间与输入数据规模之间的关系。通常,我们会关注最糟糕情况下的时间复杂度,因为它提供了一个...

    java,输入十个整数,去掉其中重复的数后输出

    用java编的一个小程序,输入10个整数,去掉其中重复的数后输出

    大厂算法面试题库中高频出现的30道典型题

    - **题目描述**:给定 n 个非负整数,每个数代表坐标中的一个点 (i, ai),在坐标内画 n 条垂直线,找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 - **示例**:输入 `[1,8,6,2,5,4,8,3,7]`,...

    设计算法把一个十进制整数转换为二至九进制之间的任意进制数输出

    在计算机科学中,将一个十进制整数转换为其他任意进制(如二进制、八进制、十六进制或介于二至九之间的任何其他进制)是常见的编程任务。这种转换通常用于数据表示、计算以及理解计算机内部工作原理。下面我们将详细...

    纯数数组去重复算法1千万3秒

    标题中的“纯数数组去重复算法1千万3秒”指的是一个高效的算法,它能在3秒钟内处理含有1000万个唯一数值的数组并去除其中的重复元素。这个算法的性能特点是不受数据重复数量的影响,无论是1千万个还是1亿个重复元素...

    用算法去除数组中重复的数字

    用算法去除数组中重复的数字(不使用查重函数)通过嵌套for循环和if条件判断实现,用算法去除数组中重复的数字(不使用查重函数)通过嵌套for循环和if条件判断实现

    信息学奥赛一本通-教程PPT课件(第五版)算法部分 第二章 数据排序.pdf

    4. 第k小整数问题:这个算法问题属于选择排序的范畴,关键在于找出一组数中第k小的数。在信息学奥赛中,常用的方法是快速选择算法(Quick Select),这是快速排序的变种,它可以在平均线性时间内找到第k小的元素。 ...

    去除重复数据,去除重复数据算法

    根据给定文件的信息,本文将围绕“去除重复数据”这一主题进行深入探讨,重点解析一个简单易懂且适用性广的去重算法,并通过具体的代码示例来展示其实现过程。 ### 去除重复数据的基本概念 在计算机科学中,“去除...

    用筛选法删除输入的10个数中的重复的数

    - **哈希表优化**:可以使用哈希表(如C语言中的字典结构)来记录已出现的数字,这样可以在O(1)时间内检查一个数字是否重复,整体时间复杂度降为O(n)。 - **排序后去重**:先对数组进行排序,然后线性扫描数组去重,...

    LMS算法自适应线性预测_LMS算法自适应线性预测_lms算法预测_lms_回归_预测_

    LMS(Least Mean Squares)算法,全称为最小均方误差算法,是自适应滤波理论中的一个核心算法,主要用于在线估计线性系统的参数。它由斯坦福大学的John W. Widrow和Marcian E. Hoff在1960年提出,因其简单高效而在...

    【源代码】C++算法(五)一维数组去重(复杂度为n且不新开辟空间)

    在本篇【源代码】C++算法中,我们主要探讨的是如何在一维数组中去除重复元素,同时确保算法的时间复杂度为O(n),并且在整个处理过程中不额外开辟新的内存空间。这种问题在实际编程中非常常见,特别是在处理大量数据...

    Java面试经典算法

    Java 面试经典算法是指在 Java 面试中经常会被问到的算法题目,这些题目涵盖了数据结构、算法设计、编程语言基础知识等方面的知识。本文总结了 17 道 Java 面试经典算法题目,并对每道题目进行了详细的分析和解释。 ...

    java程序员面试题算法

    【程序 15】:排序问题,可以使用冒泡排序、选择排序等简单排序算法,这里是最简单的三数取小法,每次比较并交换相邻的两个数,确保每次之后的第一个数都是当前未排序部分的最小值。 【程序 16】:9乘法口诀表,...

    JAVA算法编程题汇总(50题及答案)

    Java 算法编程题汇总是本文的主题,本文将对 50 道 Java 算法编程题进行汇总,涵盖了 Fibonacci 序列、素数、水仙花数、质因数分解等多个方面的知识点。 Fibonacci 序列 Fibonacci 序列是一个经典的算法问题,...

    算法面试题总结.docx

    ### 算法面试题总结 #### 一、两数之和 **题目描述:** ...以上是针对“两数之和”和“回文数”这两个经典的算法面试题的解析及其不同解法的实现。这些方法不仅有助于理解题目,还能够提升解决实际问题的能力。

    java算法练习试题

    判断素数的常用算法是埃拉托斯特尼筛法,但在这个问题中,我们可以简单地遍历该范围内的每个数,用2到该数平方根的每个数尝试去除,如果能被整除则不是素数,否则是素数。 【程序 3】"水仙花数"是三位数中的一种...

Global site tag (gtag.js) - Google Analytics