本篇博文给大家介绍前N个正整数的排列求解的三种方式。第一种是暴力求解法;第二种则另外声明了一个长度为N的数组,并且将已经排列过的数字保存其中;第三种方式则采用了另外一种思路,即首先获取N个整数的升序排列,然后对其位置进行随机交换以达到前N个整数的随机排列的目的。首先我们来看看第一种方式,具体代码如下:

import java.util.Random;

import org.junit.Test;

public class App {
private int n = 100000;
@Test
public void testFirst() {
int[] arr = new int[n];
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
arr[i] = readInt(1, n + 1);
while (isExist(arr, i)) {
arr[i] = readInt(1, n + 1);
}
}
long end = System.currentTimeMillis();
System.out.println("方法一消耗时长为:" + (end - start));
} private boolean isExist(int[] arr, int index) {
for (int i = 0; i < index; i++) {
if (arr[i] == arr[index]) {
return true;
}
}
return false;
} private int readInt(int i, int j) {
Random random = new Random();
int result = random.nextInt(j);
while (result < i) {
result = random.nextInt(j);
}
return result;
}
}

这里输出结果如下

方法一消耗时长为:38580

即当n为100000的时候,计算结果需要38.580s。该算法通过readInt(int i, int j)方法获取一个大于等于i并且小于等于j的整数,通过isExist(int[] arr, int index)方法判断数组中在索引为index之前的序列里是否已经存在了索引为index对应的值。该算法求解的思路即为从0开始对数组arr赋值,并且在每次获取一个随机数之后判断数组之前的部分中是否已经存在了该值,若存在了该值则继续获取,直至数组之前的部分中不存在该值。这种算法将产生大量的重复计算,主要来源在于判断数组之前的部分中是否存在该值和对于新的数组元素的获取两个部分。为了解决第一个问题,即判断数组之前部分中是否存在该值,我们采用了第二种算法,具体代码如下:

import java.util.Random;

import org.junit.Test;

public class App {
private int n = 100000; @Test
public void testSecond() {
int[] arr = new int[n];
long start = System.currentTimeMillis();
boolean[] used = new boolean[n];
for (int i = 0; i < n; i++) {
arr[i] = readInt(1, n + 1);
while (used[arr[i] - 1]) {
arr[i] = readInt(1, n + 1);
}
used[arr[i] - 1] = true;
}
long end = System.currentTimeMillis();
System.out.println("方法二消耗时长为:" + (end - start));
} private int readInt(int i, int j) {
Random random = new Random();
int result = random.nextInt(j);
while (result < i) {
result = random.nextInt(j);
}
return result;
}
}

计算结果如下:

方法二消耗时长为:148

若将N改为1000000,计算结果为:1.648s,若将N改为10000000,计算结果为33.679s,可以看出,该算法相对于第一种算法有所改进,但是对于千万级数据量的处理消耗时长较长。第二种算法另外声明了一个数组used,该数组长度为N,这里将要求解的数据(长度为N的正整数的排列)与used数组的下标形成一种一一对应的关系,当要判断某个获取到的正整数是否已经存在时,只需要判断数组中对应下标的数据值是否为true,为true则表示已经存在。但是这里经过我们的分析也会发现,对于采用readInt(int i, int j)方法获取新数据时,若该数据非常靠后,那么获取的难度(循环的次数)将会大大增加,这里也会影响该算法的运行效率。为了避免这个问题,我们则可以采用第三种方法,该方法则使用了一个已知的事实,即结果数组中的数值我们是完全确定的,变化的只是该数值的顺序,因而,我们如果采用随机算法改变各个数值的位置,即可达到排列各个数值的目的。具体的算法如下:

import java.util.Random;

import org.junit.Test;

public class App {
private int n = 10000000; @Test
public void testThird() {
int[] arr = new int[n];
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
for (int i = 1; i < n; i++) {
swapReferences(arr, i, readInt(0, i));
}
long end = System.currentTimeMillis();
System.out.println("方法三消耗时长为:" + (end - start));
} private void swapReferences(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
} private int readInt(int i, int j) {
Random random = new Random();
int result = random.nextInt(j);
while (result < i) {
result = random.nextInt(j);
}
return result;
}
}

运行结果如下:

方法三消耗时长为:2105

该方法通过readInt(int i, int j)方法每次获取数组中索引为0到索引为i的随机索引,将并且将数组中索引为i的数据与获取到的索引对应的数据位置替换,这样就可以达到全排列的目的。

使用三种方法求解前N个正整数的排列的更多相关文章

  1. 三种方法求解最大子区间和:DP、前缀和、分治

    题目 洛谷:P1115 最大子段和 LeetCode:最大子序和 给出一个长度为 \(n\) 的序列 \(a\),选出其中连续且非空的一段使得这段和最大. 挺经典的一道题目,下面分别介绍 \(O(n) ...

  2. 数组k平移三种方法(java)

    上代码,本文用了三种方法实现,时间复杂度不一样,空间复杂度都是o(1): public class ArrayKMove { /** * 问题:数组的向左k平移,k小于数组长度 * @param ar ...

  3. 【SQL】Oracle分页查询的三种方法

    [SQL]Oracle分页查询的三种方法 采用伪列 rownum 查询前10条记录 ? 1 2 3 4 5 6 7 8 9 10 11 [sql] select * from t_user t whe ...

  4. Java中获取键盘输入值的三种方法

    Java中获取键盘输入值的三种方法     Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值 ...

  5. 【Cocos2d-x游戏开发】解决Cocos2d-x中文乱码的三种方法

    众所周知,Cocos2d-x是一款不错的开源引擎,但是在Cocos2d-x中直接使用中文是无法正确显示的.比如下面的情况: 解决这个问题常用的有三种方法:1.通过转换为UTF-8编码来显示.2.使用i ...

  6. Eclipse插件安装的三种方法

    转自:http://www.blogjava.net/tangzurui/archive/2008/06/30/211669.html  整理了一下格式. (前两种安装方式以多国语言包的安装为例) 1 ...

  7. Centos内核升级的三种方法

    本文出自 “存储之厨” 博客,请务必保留此出处http://xiamachao.blog.51cto.com/10580956/1755354 在基于CentOS平台的工作过程中,难免有时需要升级或者 ...

  8. Struts2获取request三种方法

    Struts2获取request三种方法   struts2里面有三种方法可以获取request,最好使用ServletRequestAware接口通过IOC机制注入Request对象. 在Actio ...

  9. MyEclipse6.5安装SVN插件的三种方法

    MyEclipse6.5安装SVN插件的三种方法 方法一.如果可以上网可在线安装 1. 打开Myeclipse,在菜单栏中选择Help→Software Updates→Find and Instal ...

随机推荐

  1. springmvc返回json字符串中文乱码问题

    问题: 后台代码如下: @RequestMapping("menuTreeAjax") @ResponseBody /** * 根据parentMenuId获取菜单的树结构 * @ ...

  2. Jquery 改变样式

    Jquery简单的操作 Jquery 是一个非常好用JS库,有很多的特殊的操作,为了方便,我们都可以引入Jquery. <script src="dist/js/vendor/jque ...

  3. python爬虫小结1

    先看正则化,正则化就是描述命令和字符切分.查找.筛选等功能的方便方式. http://www.cnblogs.com/fnng/archive/2013/05/20/3089816.html 一个游戏 ...

  4. 使用parcel替代webpack制作omi-tap插件

    Parcel 是一个零配置的网络应用打包工具, 适用于经验不同的开发者, 它利用多核处理提供了极快的速度.对比 webpack 最大的两个亮点: 零配置 极快速 omi 相关的插件都是基于 webpa ...

  5. Python 项目实践二(下载数据)第三篇

    接着上节继续学习,在本章中,你将从网上下载数据,并对这些数据进行可视化.网上的数据多得难以置信,且大多未经过仔细检查.如果能够对这些数据进行分析,你就能发现别人没有发现的规律和关联.我们将访问并可视化 ...

  6. ASP.NET MVC下自定义错误页和展示错误页的几种方式

    在网站运行中,错误是不可避免的,错误页的产生也是不可缺少的. 这几天看了博友的很多文章,自己想总结下我从中学到的和实际中配置的. 首先,需要知道产生错误页的来源,一种是我们的.NET平台抛出的,一种是 ...

  7. 树链剖分X2

    1.ZJOI树的统计 板子题 因为初始化没打改了几个小时 改到双腿软着出的机房(身体素质感人 #include<iostream> #include<cstdio> #incl ...

  8. Docker安装入门 -- 中间件镜像

    Docker安装入门 -- 中间件镜像 php-fpm 1.docker build -t csphere/php-fpm:5.4 .   //在当前目录下创建镜像 2.docker run -d - ...

  9. date 命令详解

    date - print or set the system date and time Display the current time in the given FORMAT, or set th ...

  10. 根据图片的路径(绝对路径/相对路径都可以),生成base64的

    根据图片的路径(绝对路径/相对路径都可以),生成base64的 <!DOCTYPE html> <html> <head> <meta charset=&qu ...