Java数组操作总结

寻找数组中的最大值与最小值

最常用的方法就是遍历两次数组,一次找最大值,一次找最小值,比较2N次.或者遍历一次数组,声明两个变量保存最大值和最小值,每次取出一个数组先与最小值比较,再与最大值比较.,比较次数依旧是2N次

算法优化:
取双元素法:每次比较相邻的两个数,较大者与max比较,较小者与min比较,只需比较1.5N次.
算法注意事项:

  1. min与max初始值均为a[0]
  2. 变量i用来遍历数组a,注意i应该从1开始,由于是比较相邻的两个数,所以i的改变为i=i+2
  3. 如果i指向了最后一个元素,则这个元素分别于min和max比较.
  4. max与min均设置为了静态的
public class 取双元素法找最大最小值 {

	static int max=0,min=0;
	public static void GetMaxAndMin(int[] a) {
		max=a[0];
		min=a[0];
		for(int i=1;i<a.length;i+=2) {
			if (i+1>=a.length) {
				if (a[i]>max) {
					max=a[i];
				}
				if (a[i]<min) {
					min=a[i];
				}
			}else if (a[i]>a[i+1]) {
				if (a[i]>max) {
					max=a[i];
				}
				if (a[i+1]<min) {
					min=a[i+1];
				}
			}
			else if (a[i]<a[i+1]) {
				if (a[i+1]>max) {
					max=a[i+1];
				}
				if (a[i]<min) {
					min=a[i];
				}
			}
		}
	}
	public static void main(String[] args) {
		int[] a= {7,3,19,40,1,4};
		GetMaxAndMin(a);
		System.out.println(max+" "+min);
	}
}

找出数组中第二大的数

通过记录上一次最大数,可以获得第二大的数,因此我们可以声明两个变量,一个记录当前最大值,一个记录上一个最大值.

public class secondMax {
public static void main(String[] args) {
	int a[]= {5,3,2,1,8};
	int max=0;
	int second=0;
	for (int i = 0; i <a.length; i++) {
		if (a[i]>max) {
			second=max;
			max=a[i];
		}
	}
	System.out.println("max:"+max);
	System.out.println("second:"+second);
}
}

求最大子数组的和(还有始末位置)

解法1:
定义两个数ThisSum和MaxSum来记录当前数组的和,以及数组的最大和。用两个for循环来来遍历数组,每一次求出子数组的最大和,每个子数组从0开始,下一次遍历子数组就是从1开始,以此类推。如第一次就【0N-1】的最大和,第二次就是【1N-1】,第三次就是【2~N】。。。。这样,每次用ThisSum来记录当前数组的和,MAXSum来记录当前子数组的最大和,与ThisSum比较,取其最大就能求出最大子数组的和。

public static int start=0;
public static int end=0;
public static int MaxSubArraySum1(int a[],int n) {
	int ThisSum,MaxSum,i,j;
	MaxSum=0;
	for (i = 0;  i<n; i++) {
		ThisSum=0;
		for (j = i; j < n; j++) {
			ThisSum+=a[j];
			if (ThisSum>MaxSum) {
				start=i;
				end=j;
				MaxSum=ThisSum;
			}
		}
	}
	return MaxSum;
}

此方法的时间复杂度O(n²)

解法2:
1、同样我们ThisSum来记录当前子数组的和,MaxSum来记录当前子数组最大和,我们可以从0位置开始便来,开始遍历每次ThisSum 加上 a[ i ],如果当前ThisSum大于MaxSum的话,将ThisSum的值付给MaxSum,如果当前的ThisSum<0的话就把ThisSum的值赋值为0,接着下一步的遍历。

2、这里我必须说一下,求最大子数组的起点到尾端的求法,
a、我们定义start 和end来记录
b、当thisSum > maxSum时记录end 等于此时的下标。
c、当thisSum < 0时,我们可以知道起点必须从新修改,改为下一个下标(i +1)为起点start,但是要小心下一个下标(i+1)可能 元素的值小于0,或者越界,所以我们必须判断一下。如:
if(i <= arr.length - 2 && arr[i+1] > 0){
start = i + 1;
}

public static int start=0;
public static int end=0;
public static int MaxSubArraySum2(int a[],int n) {
	int ThisSum=0;
	int MaxSum=0;
	int i;
	start=0;
	end=0;
	for(i=0;i<n;i++) {
		ThisSum+=a[i];
		if (ThisSum>MaxSum) {
			MaxSum=ThisSum;
			end=i;
		}
		if (ThisSum<0) {
			ThisSum=0;
			if (i<=n-2&&a[i+1]>0) {
				start=i+1;
			}
		}
	}
	//如果最大子数组不在数组里的话
	if (start==0 && end==0 && a[0]<=0) {
		start=-1;
		end=-1;
	}
	return MaxSum;
}

时间复杂度O(n).

找出数组中重复元素最多的数

使用Map映射表记录每一个元素出现的次数,然后判断次数大小,进而找出重复次数最多的元素。key表示数组的元素,value表示这个元素在数组中出现的次数。最后对map进行遍历。
代码如下:

public class 找出数组中重复最多的元素 {
public int findMostFrequentInArray(int[] a) {
	Map<Integer, Integer> map=new HashMap<Integer, Integer>();
	for (int i = 0; i < a.length; i++) {
		if (map.containsKey(a[i])) {
			map.put(a[i], map.get(a[i])+1);
		}else {
			map.put(a[i], 1);
		}
	}
	//记录数组中重复元素最多的数
	int index=0;
	int val=0;
	Iterator<Entry<Integer, Integer>> iterator=map.entrySet().iterator();
	while(iterator.hasNext()) {
		Entry<Integer, Integer> next=iterator.next();
		int key=next.getKey();
		int value=next.getValue();
		if (value>val) {
			index=key;
			val=value;
		}
	}
	return index;
}
}

如何把一个数组循环右移K位

这个考验的是关于逆序的操作,比如数组{1,2,3,4,5,6,7,8},右移两位变为{7,8,1,2,3,4,5,6},逆序步骤:

  1. 逆序数组子序列123456,数组序列的形式为65432178

  2. 逆序数组子序列78, 数组序列的形式变为65432187

  3. 全部逆序, 数组序列的形式为78123456

public class 数组右移K位 {

	private void reverse(int[] a,int i,int j) {
		for(;i<j;i++,j--) {
			int temp=a[i];
			a[i]=a[j];
			a[j]=temp;
		}
	}
	//右移K位
	public void shift(int[] a,int k) {
		int n=a.length;
		k=k%n;
		reverse(a, 0, n-k-1);
		reverse(a, n-k, n-1);
		reverse(a, 0, n-1);
	}
}

求数组中第K个最小的数

在海量数据中关于TOP-K问题我写了五种解法,这里因为数据量少的原因,有两种快速解题的思路,
解法1:使用快速排序的思想找到一个元素a的定位,小于a的数放左边,大于a的数放右边,然后有–>如果a大于K,则对a左边的子数组继续使用快排,如果a小于K,则对a右边的子数组继续使用快排,直到a等于K.这种思路叫做局部淘汰法.
解法2:BFPRT算法(中位数的中位数算法),这5个英文字母是五位发现者的名字首字母拼起来的,并没啥含义.这是对解决TOP-K问题最有效的算法.
我们只需要前K大的数,对其余不需要的数进行排序是浪费时间的,这就是这个算法解决的问题.
实现思路:

  1. 选取主元
  2. 将n个元素划分为leng/5+1个组,每组5个元素,若有剩余元素,舍去.
  3. 使用插入排序找到leng/5+1个组中每一组的中位数
  4. 对于3中找到的所有中位数,调用BFPRE算法求出它们的中位数,作为主元
  5. 以4选取的主元位分界点,把小于主元的放在左边,大于主元的放在右边.
  6. 判断主元的位置于K的大小,有选择的对左边或右边递归.
public class 中位数的中位数算法 {

	   public static void main(String args []){
           int [] array ={ 1,1,2,3,1,5,-1,7,8,-10};
           int k ;
           for (k =1; k<= array.length; k ++){
                    int k_Num= quickSearch( array,0, array .length-1, k);
                   System. out .println(" 第 "+ k +" 小的元素是: " +k_Num );
          }
}
public static int quickSearch( int []array , int left, int right, int k){
//left,right为下标, k 为第k 小的元素
if( left== right){
    return array [left ];
}
int mid =getArrayMid (array , left, right); //mid 为中位数 id
int mid_new =partition (array , left, right, mid );// 根据中位数划分数组,是中位数在最终的位置上
// 比较中位数与要查找数值的大小
if( mid_new== k-1){
    return array [mid_new ];
} else if (mid_new <k -1){
    left= mid_new+1;
    return quickSearch( array, left, right, k);
} else {
    right= mid_new-1;
    return quickSearch( array, left, right, k);
}
}
public static int getArrayMid( int a [], int l, int r){ //l,r均为数组下标 , 返回中位数的位置
if( l== r){
    return l ;         
}
int i =l ;
for (;i <=r -l -5;i +=5){// 子数组的元素个数为 5 个时
    insertSort( a, i, i+4); //对当前的五个数数进行排序
    swap( a, l+( i- l)/5, i+2); //将所有中位数放在数组 array 的前几位上
}
if( i< r- l){
    insertSort( a, i, r- l);
    swap( a, l+( i- l)/5,( i+ r- l)/2); //将最后一组数的中位数放在数组 array 的前几位上
}
return getArrayMid( a, l, l+( i- l)/5); // 返回中位数的中位数的 id

};
public static int partition( int a [], int l, int r, int mid){
int pivot =a [l ];
while (l <r ){
    while (l <r && pivot<= a[ r]) r--;
    a[ l]= a[ r];
    while (l <r && pivot> a[ l]) l++;
    a[ r]= a[ l];
}
a[ l]= pivot;
return l ;
};
public static void swap(int []a , int i , int j ){
   int temp= a[ i];
   a[ i]= a[ j];
   a[ j]= temp;
}
public static void insertSort( int a [], int l , int r ){ // 对下标为 l到 r之间的元素进行排序
   if( l< r){
         for( int i= l; i< r; i++){
              int j= i+1;
              int temp= a[ j];
              while( j> l&& temp< a[ j-1]){
                    a[ j]= a[ j-1];
                    j--;
             }
              a[ j]= temp;
        }
  }
}
public static void printArray( int arr []){
   for( int k=0; k< arr.length; k++){
        System. out.print( arr[ k]+ "\t");
  }
  System. out.print( "\n");
}

}

找出数组中只出现一次的数字

问题描述:有这样一个整型数组:已知数组里除了一个数组出现一次外,其他数字都出现两次.找出这个只出现1次的数字.要求时间复杂度是O(n),空间复杂度是O(1).

解法1:
根据异或运算的特性,当个相同数字异或为0,因此,我们只要把数组里所有的数字进行异或,最终的结果刚好是这个只出现1次的数字.
代码如下:

//使用异或特性求解
	public static int findNotDouble1(int a[]) {
		int n=a.length;
		int result=a[0];
		int i;
		for(i=1;i<n;i++) {
			result^=a[i];
		}
		return result;
	}

如果把出现2次改为3次呢?那异或运算就无效了,还有哪些解法呢?
解法2:
将数组中的数以2进制显示,比如数组{1,1,1,2,2,2},它们对应的2进制为{01,01,01,10,10,10},我们可以对位数进行1的个数的统计,第0位有3个1,第1位有3个1.假设出现一次的这个数为a,那么去掉a后其他数字对应的二进制数的每个位置出现1的个数为3的倍数.
因此:可以对数组中的所有数字对应的二进制数中各个位置上1的个数对3取余数,就可以得到出现1次的这个数的2进制表示,从而找出这个数.
代码如下:

	//通过统计数组2进制各位数上1的个数找出
	public static int findNotDouble2(int a[],int appearTimes) {
		int n=a.length;
		int[] bitCount=new int[32];
		//计算数组中所有数组对应的2进制数各个位置上出现1的次数
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < 32; j++) {
				bitCount[j]+=((a[i]>>j)&1);
			}
		}
		//若某位上的结果不能被整除,则目标数字在这一位上
		int appearOne=0;
		for(int i=0;i<32;i++) {
			if (bitCount[i]%appearTimes !=0) {
				appearOne+=(1<<i);
				
			}
		}
		return appearOne;
	}

找出数组中唯一重复的数

问题描述:
数组a[N],1~(N-1)这(N-1)个数存放在a[N]中,其中某个数重复1次.找出被重复的数字.要求每个数组元素只能被访问1次,并且不用辅助存储空间.
思路:
首先我们发现数组元素是连续的,只是有个数重复了一次(比如{1,2,2,3,4}),采用数学求和法,根据累加和原理,对数组的所有项求和,然后减去1~N-1的和,即为所求的重复数.
代码如下:

public static int xor_findDup(int a[]) {
		int n=a.length;
		int temp1=0;
		int temp2=0;
		for(int i=0;i<n-1;i++) {
			temp1+=(i+1);
			temp2+=a[i];
		}
		temp2+=a[n-1];
		int result=temp2-temp1;
		return result;
	}
	public static void main(String[] args) {
		int a[]= {1,2,2,3,4};
		int missingNum=xor_findDup(a);
		System.out.println(missingNum);
	}

用递归方法求一个整数数组的最大元素

思路:
将"数组第一个元素"与"数组中其他元素组成的子数组的最大值"进行比较,求最大值.
代码实现:

public class 递归求数组最大值 {
	private static int max(int a,int b) {
		return a>b?a:b;
	}
	public static int maxnum(int a[],int begin) {
		int length=a.length-begin;
		if (length==1) {
			return a[begin];
		}else {
			return max(a[begin], maxnum(a, begin+1));
		}
	}
	public static void main(String[] args) {
		int[] num= {0,16,2,3,4,5,10,7,8,9};
		System.out.println(maxnum(num, 0));
	}
}

如何求数对之差的最大值

问题描述:
数组中的一个数字减去它右边子数组中的一个数字可以得到一个差值,求所有可能的差值中的最大值,例如,数组{1,4,17,3,2,9}中,最大的差值为17-2=15.
解法1:
遍历数组,找到所有可能的差值,从所有差值中找出最大值.时间复杂度O(n²)
代码实现:

public static int getMax(int[] a) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		if (len<=1) {
			return Integer.MIN_VALUE;
		}
		int max=Integer.MIN_VALUE;
		for(int i=0;i<len-1;i++) {
			for(int j=i+1;j<len;j++) {
				if (a[i]-a[j]>max) {
					max=a[i]-a[j];
				}
			}
		}
		return max;
	}

解法2:
二分法,通过2分法可以减少计算的次数,把数组分为两个子数组,那么最大的差值只能有三种可能:

  1. 最大的差值对应的被减数和减数都在左子数组中,假设最大差值为leftMax.
  2. 被减数和减数都在右子数组中,假设最大差值为rightMax.
  3. 被减数是左子数组的最大值,减数是右子数组的最小值,假设最大差值为mixMax.

max(leftMax,rightMax,mixMax)的值为最大差值.
实现代码如下:

	public static int getMax2(int[] a) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		if (len<=1) {
			return Integer.MIN_VALUE;
		}
		AtomicInteger max=new AtomicInteger(0);
		AtomicInteger min=new AtomicInteger(0);
		return getMaxDiff(a,0,len-1,max,min);
	}
	public static int getMaxDiff(int a[],int begin,int end,AtomicInteger max,AtomicInteger min) {
		if (begin==end) {
			max.set(a[begin]);
			min.set(a[begin]);
			return Integer.MIN_VALUE;
		}
		int middle=begin+(end-begin)/2;
		//数组前半部分的最小值与最大值
		AtomicInteger lMax=new AtomicInteger(0);
		AtomicInteger lMin=new AtomicInteger(0);
		//数组前半部分的最大差值(第一种情况)
		int leftMax=getMaxDiff(a, begin, middle, lMax, lMin);
		//数组后半部分的最小值与最大值
		AtomicInteger rMax=new AtomicInteger(0);
		AtomicInteger rMin=new AtomicInteger(0);
		//数组后半部分的最大差值(第二种情况)
		int rightMax=getMaxDiff(a, middle+1, end, rMax, rMin);
		//对应第三种情况
		int mixMax=lMax.get()-rMin.get();
		//求数组的最大值与最小值
		if (lMax.get()>rMax.get()) {
			max.set(lMax.get());
		}
		else {
			max.set(rMax.get());
		}
		
		if (lMin.get()<rMin.get()) {
			min.set(lMin.get());
		}
		else {
			min.set(rMin.get());
		}
		//求最大的差值
		int allMax=(leftMax>rightMax)?leftMax:rightMax;
		allMax=(allMax>mixMax)?allMax:mixMax;
		return allMax;
	}

为什么使用AtomicInteger而不是Integer

Integer类对象在方法内部修改值不能影响到方法外的Integer类对象(参照swap()方法使用反射实现的原因),而原子类可以.

解法3:
动态规划法,给定数组a,申请额外的数组diff和max,其中diff [i]是以数组中第i个数字为减数的所有数对之差的最大值(前i+1个数 组成的子数组中最大的差值),max[i]为前i+1个数的最大值.假设已经求得了diff[i],diff[i+1]的值有两种可能性:1、等于diff[i]。2、等于max[i]-a[i].
通过上面的分析,可以得到动态规划方法的计算表达式:diff[i+1]=max(diff[i],max[i-1]-a[i]),max[i+1]=max(max[i],a[i+1]).数组最大的差值为diff[n-1] (n为数组长度)
代码实例如下:

	public static int max(int m,int n) {
		return (m>n)?m:n;
	}
	public static int getMax3(int[] a) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		if (len<=1) {
			return Integer.MIN_VALUE;
		}
		//记录差值的最大值
		int[] diff=new int[len];
		//记录当前的最大值
		int[] max=new int[len];
		diff[0]=0;
		max[0]=a[0];
		for(int i=1;i<len;i++) {
			diff[i]=max(diff[i-1], max[i-1]-a[i]);
			max[i]=max(max[i-1], a[i]);
		}
		return diff[len-1];
	}

算法优化:在求解diff[i+1]时,只用到了diff[i]与max[i],而与数组diff和max中的其他数字无关,因此可以通过两个变量而不是数组来记录diff[i]与max[i]的值,
因此继续优化有:

	public static int max(int m,int n) {
		return (m>n)?m:n;
	}
public static int getMax3_1(int[] a) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		if (len<=1) {
			return Integer.MIN_VALUE;
		}
		//记录差值的最大值
		int diff=0;
		//记录当前的最大值
		int max=a[0];
		for(int i=1;i<len;i++) {
			diff=max(diff, max-a[i]);
			max=max(max, a[i]);
		}
		return diff;
	}

如何求绝对值最小的数

问题描述:有一个升序排序(自然排序)的数组,数组中可能有正数,负数或0,求数组中元素的绝对值最小的数,例如数组{-10,-5,-2,7,15,20},绝对值最小的是-2.
思路:
升序排序后有三种情况:

  1. 如果数组第一个元素为非负数,则第一个元素最小.
  2. 如果数组最后一个元素为负数,则最后一个元素最小.
  3. 如果数组第一个数为负数,最后一个数为正数,则通过比较分界点(0)左右的数的绝对值来确定最小数.

算法优化:二分查找法
取数组中间位置的值a[mid],

  • 当 a[mid]=0时,这个数就是最小数;
  • 当a[mid]>0时,如果a[mid-1]<0,那么就找到了分界点,通过比较a[mid]与a[mid-1]的绝对值就可以找到数组中绝对值最小的数.
  • 当a[mid]<0时,如果a[mid+1]>0,那么就找到了分界点.

代码实现:

public class 二分查找绝对值最小的数 {
	public static int getMinAbsoluteValue(int a[]) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		if (len<1) {
			return Integer.MIN_VALUE;
		}
		//数组中没有负数
		if (a[0]>=0) {
			return a[0];
		}
		//数组中没有正数
		if (a[len-1]<=0) {
			return a[len-1];
		}
		int mid=0;
		int begin=0;
		int end=len-1;
		int absMin=0;
		//数组中既有正数又有负数
		while(true) {
			mid=begin+(end-begin)/2;
			//如果值等于0,那么就是绝对值最小的数
			if (a[mid]==0) {
				return 0;
			}
			//如果值大于0,那么正负数的分界点在左半部分
			else if (a[mid]>0) {
				//继续在数组的左半部分查找
				if (a[mid-1]>0) {
					end=mid-1;
				}//找到正负数的分界点
				else if (a[mid-1]==0) {
					return 0;
				}else {
					break;
				}
			}
			//如果值小于0,在数组右半部分查找
			else {
				//在数组右半部分继续查找
				if (a[mid+1]<0) {
					begin=mid+1;
				}//找到正负数的分界点
				else if (a[mid+1]==0) {
					return 0;
				}else {
					break;
				}
			}
		}
		//获取正负数分界点求绝对值最小的值
		if (a[mid]>0) {
			if (a[mid]<Math.abs(a[mid-1])) {
				absMin=a[mid];
			}else {
				absMin=a[mid-1];
			}
		}else {
			if (Math.abs(a[mid])<a[mid+1]) {
				absMin=a[mid];
			}else {
				absMin=a[mid+1];
			}
		}
		return absMin;
	}
}

求数组中两个元素的最小距离

问题描述:给定一个数组,数组中含有重复元素,给出两个数n1和n2,求这两个数字在数组中所出现位置的最小距离,例如,数组{4,5,6,4,7,4,6,4,7,8,5,6,4,3,10,8}中,4和8的最小距离为2.
思路:遍历数组后,

  1. 当遇到n1时,记录下n1值对应的数组下标的位置n1_index,通过求n1_index与上次遍历到n2的下标值n2_index的差,可以求出最近一次遍历到的n1和n2的距离.
  2. 当遇到n2时,同样记录下它在数组中下标的位置n2_index,然后通过求n2_index与上次遍历到n1的下标值n1_index的差,求出最近一次遍历到的n1与n2的距离.

定义一个变量min_dist记录n1与n2的最小距离,在以上两种情况下,每次求出n1与n2的距离后与min_dist相比,求最小值.

实现代码如下:

public class 数组中两元素的最小距离 {
	private static int min(int a,int b) {
		return a>b?b:a;
	}
	public static int minDistance(int a[],int n1,int n2) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		int n1_index=-1;
		int n2_index=-1;
		int min_dist=Integer.MIN_VALUE+1;
		for (int i = 0; i < len; i++) {
			if (a[i]==n1) {
				n1_index=i;
				if (n2_index>=0) {
					min_dist=min(Math.abs(min_dist), Math.abs(n1_index-n2_index));
				}
			}
			if (a[i]==n2) {
				n2_index=i;
				if (n1_index>=0) {
					min_dist=min(Math.abs(min_dist), Math.abs(n2_index-n1_index));
				}
			}
		}
		return min_dist;
	}
	public static void main(String[] args) {
		int a[]= {4,5,6,4,7,4,6,4,7,8,5,6,4,3,10,8};
		System.out.println(minDistance(a, 4, 8));
	}
}

如何求指定数字在数组中第一次出现的位置

问题描述:
给定数组a={3,4,5,6,5,6,7,8,9,8},这个数组中相邻元素之差都为1,给定数字9,求它在数组中第一次出现的位置的下标.

跳跃搜索法:
因为数组中相邻元素之差都为1,因此9在数组中出现的最早的位置必定为第1+(9-a[0])=7个位置;有两种情况:

  1. 如果是递增数组且相邻差为1,则第7个位置必为9.(比如{3,4,5,6,7,8,9}).
  2. 如果不是递增的且相邻差依然为1,那9出现的位置一定在第7个位置之后.这就是跳跃搜索的原理.

代码实现如下:

public static int findIndex(int a[],int t) {
		if (a==null) {
			return Integer.MIN_VALUE;
		}
		int len=a.length;
		int i=0;
		while(i<len) {
			if (a[i]==t) {
				return i;
			}else {
				i+=Math.abs(t-a[i]);
			}
		}
		return -1;
	}

如何对数组的两个子有序段进行合并(归并排序的合并优化)

问题描述:数组a[0,mid-1]和a[mid,n-1]是各自有序的,对数组a[0,n-1]的两个子有序段进行合并,得到a[0,n-1]整体有序.要求空间复杂度O(1).比如数组a={1,5,6,7,9,2,4,8,10,13,14},mid=5,a[0] ~ a[4]是有序的,a[5] ~ a[10]是有序的.
思路:
首先遍历数组中下标为0 ~ mid-1的元素,将遍历到的元素的值与a[mid]进行比较,当遍历到a[i]>a[mid]时,交换a[i]与a[mid]的值,接着找到交换后的a[mid]在a[mid~ num-1]中的具体位置
实现方法:
遍历a[mid~ num-2],如果a[mid+1]<a[mid],那么交换a[mid]与a[mid+1]的位置.

代码实现如下:

public class 对数组的两个有序段进行合并 {
	//将右子序列最小值放在a[mid]位置
	public static void findRightPlaceForMid(int a[],int mid) {
		int len=a.length;
		int temp;
		//相邻交换排序,交换排序后依旧有序的原因在于子序列段本来就是有序的
		for(int i=mid;i<len-1;i++) {
			if (a[i+1]<a[i]) {
				temp=a[i];
				a[i]=a[i+1];
				a[i+1]=temp;
			}
		}
	}
	public static void sort(int a[],int mid) {
		int temp;
		for(int i=0;i<=mid-1;i++) {
			if (a[mid]<a[i]) {
				temp=a[i];
				a[i]=a[mid];
				a[mid]=temp;
				findRightPlaceForMid(a, mid);
			}
		}
	}
}

如何判断一个数组中数值是否连续相邻

问题描述:
一个数组序列,元素取值可能是0~65535中的任意一个且相同数值不会重复出现.当从数组序列中随意选取5个数,判断这5个数是否连续相邻,规定:

  1. 5个数允许乱序(要求5,6,7,8,9,数组中为{8,7,5,6,9}也符合要求)
  2. 0可以通配任意数值.
  3. 0可以多次出现
  4. 全0或只有一个非0算连续.

思路:如果没有0的存在,要组成连续的数列,最大值和最小值的差距必须是4,存在0的情况下,只要最大值和最小值的差距小于4就可以所以应找出数列中非0的最大值和非0的最小值,时间复杂度O(n)如果 非0最大-非0最小+1<=5,那么这5个数值连续相邻.

代码实现:

public class 数组中数值是否连续相邻 {
	public static Boolean IsContinuous(int a[]) {
		int n=a.length;
		int min=-1;
		int max=-1;
		for(int i=0;i<n;i++) {
			if (a[i]!=0) {
				if (min>a[i]||-1==min) {
					min=a[i];
				}
				if (max<a[i]||-1==max) {
					max=a[i];
				}
			}
		}
		if (max-min>n-1) {
			return false;
		}else {
			return true;
		}
	}
	public static void main(String[] args) {
		int a[]= {8,7,5,0,6};
		if (IsContinuous(a)) {
			System.out.println("连续相邻");
		}else {
			System.out.println("不连续相邻");
		}
	}
}

求解数组中反序对(逆序对)的个数

问题描述:
给定一个数组a,如果a[i]>a[j] (i<j),那么a[i]与a[j]被称为一个反序,例如,给定数组{1,5,4,2,6},共有(5,3),(5,2),(3,2)三个反序对.

分治归并法:在归并排序的基础上额外使用一个计数器来记录逆序对的个数.比如数组{5,8,3,6}.归并过程如图:
在这里插入图片描述
时间复杂度O(nlogn)
代码实现如下:

public static int reverseCount=0;
	public static void merge(int a[],int begin,int mid,int end) {
		int i,j,k,n1,n2;
		n1=mid-begin+1;
		n2=end-mid;
		int L[]=new int[n1];
		int R[]=new int[n2];
		for (i = 0,k=begin; i < n1; i++,k++) {
			L[i]=a[k];
		}
		for(i=0,k=mid+1;i<n2;i++,k++)
			R[i]=a[k];
		
		for(k=begin,i=0,j=0;i<n1&&j<n2;k++) {
			if (L[i]<R[j]) {
				a[k]=L[i++];
			}else {
				reverseCount+=mid-i+1;
				a[k]=R[j++];
			}
		}
		//将找出的逆序变为顺序避免重复计算,即序列必定递增
		if (i<n1) {
			for(j=i;j<n1;j++,k++)
				a[k]=L[j];
		}
		
		if (j<n2) {
			for(i=j;i<n2;i++,k++)
				a[k]=R[i];
		}
	}
	public static void merge_sort(int a[],int begin,int end) {
		if (begin<end) {
			int mid=(end+begin)/2;
			merge_sort(a, begin, mid);
			merge_sort(a, mid+1, end);
			merge(a, begin, mid, end);
		}
	}
	public static void main(String[] args) {
		int a[]= {8,5,3,6};
		merge_sort(a, 0, a.length-1);
		System.out.println(reverseCount);
	}

求解最小三元组距离

问题描述:
已知3个升序整数数组a[i],b[m]和c[n].请在3个数组中各找一个元素,使得组成的三元组距离最小.三元组的距离定义是:假设a[i],b[j]和c[k]是一个三元组,那么距离为Distance=max(|a[i]-b[j]|,|a[i]-c[k]|,|b[j]-c[k]|),请设计一个求最小三元组距离的最优算法.

解法1:最容易想到的方法就是分别遍历3个数组中的元素,分别求出它们的距离,然后从这些值里面查找最小值.算法时间复杂度为O(l * m* n).

public class 三元数组最小距离 {
	public static int max(int a,int b,int c) {
		int max=a<b?b:a;
		max=max<c?c:max;
		return max;
	}
	public static int Solvingviolence(int a[],int b[],int c[]) {
		int aLen=a.length;
		int bLen=b.length;
		int cLen=c.length;
		int minDist=max(Math.abs(a[0]-b[0]), Math.abs(a[0]-c[0]), Math.abs(b[0]-c[0]));
		int dist=0;
		for(int i=0;i<aLen;i++) {
			for (int j = 0; j < bLen; j++) {
				for (int k = 0; k < cLen; k++) {
					//求距离
					dist=max(Math.abs(a[i]-b[j]), Math.abs(a[i]-c[k]), Math.abs(b[j]-c[k]));
					//找最小距离
					if (minDist>dist) {
						minDist=dist;
					}
				}
			}
		}
		return minDist;
	}
	public static void main(String[] args) {
		int a[]= {3,4,5,7};
		int b[]= {10,12,14,16,17};
		int c[]= {20,21,23,24,37,30};
		System.out.println(Solvingviolence(a, b, c));
	}
}

解法2:最小距离法。假设当前遍历到这3个数组中的元素分别为ai,bj,ck,并且满足ai <= bj <= ck,此时他们的距离肯定为Di=ck - ai
分析如下三种情况:

  1. 如果接下来求ai,bj,ck+1的距离,那么由于ck+1 >=ck,此时它们的距离必定为Di+1=ck+1-ai,我们会发现Di+1>Di,因此没必要让c的下标k继续增加。因为找不出最小距离
  2. 如果接下来求ai,bj+1,ck的距离,由于bj+1>=bj,如果bj+1<=ck此时它们的距离仍然为Di+1=ck-ai;如果bj+1>ck,那么此时它们的距离为Di+1=bi+1-ai,显然Di+1>=Di,得出结论b的下标j也没有必要增加。因为找不出最小距离
  3. 如果接下来求ai+1,bj,ck的距离,如果ai+1<|ck-ai|+ck,此时他们的距离Di+1=ck-ai+1,显然Di+1小于Di,因此Di+1有可能是最小值

综上所述,在求最小距离时只要考虑第三种情况。
具体实现思路:
从3个数组的第一个元素开始,先求最小距离minDist,接着找出这三个数中最小数对应的数组,只对这个数组的下标往后移一个位置,接着求3个数组中当前元素的距离,循环这个过程(找最小值对应数组,下标+1,比较)直到遍历完其中一个数组,最差的时间复杂度为O(l+m+n).

public static int max(int a,int b,int c) {
		int max=a<b?b:a;
		max=max<c?c:max;
		return max;
	}
	public static int min(int a,int b,int c) {
		int min=a<b?a:b;
		min=min<c?min:c;
		return min;
	}
	public static int minDistance(int a[],int b[],int c[]) {
		int aLen=a.length;
		int bLen=b.length;
		int cLen=c.length;
		int curDist=0;
		int min=0;
		int minDist=Integer.MIN_VALUE;
		int i=0,j=0,k=0;
		while(true) {
			curDist=max(Math.abs(a[i]-b[j]), Math.abs(a[i]-c[k]), Math.abs(b[j]-c[k]));
			if (curDist<minDist) {
				minDist=curDist;
			}
			//找出当前遍历到3个数组中的最小值
			min=min(a[i],b[j],c[k]);
			if (min==a[i]) {
				if (++i>=aLen) {
					break;
				}
			}
			else if (min==b[j]) {
				if (++j>=bLen) {
					break;
				}
			}else {
				if (++k>=cLen) {
					break;
				}
			}
		}
		return minDist;
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值