剑指offer:调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变

思路:

(1)时间复杂度:o(n平方),从头开始遍历数组,没遇到一个偶数,把它拿出来,并将它后面的数依次往前移动,并将这个偶数插入到最后的空位中。但是时间复杂度太高。

题目要求了在调整顺序之后需要保证奇数和奇数,偶数和偶数之间的相对位置不变,因此,必须开辟新的空间。

另外两种开辟空间方法:

(1)建立两个新的数组temp1,temp2,遍历原数组array,如果遇到奇数,则将这个数存入temp1中,遇到偶数,则将这个数存入到temp2中。遍历结束后将这两个数组中的数依次存入原数组。(只遍历了一遍数组,时间复杂度为o(n),但是需要开辟两个新空间,空间复杂度较高)

(2)建立一个新的数组temp,对原数组array做两次遍历,第一次遍历之后,将所有的奇数取出来,存入到temp中,第二次遍历后,将所有的偶数取出来存入到temp奇数的后面。(这次只建立了一个数组,开辟了一块内存空间,空间复杂度较(1)小,但是需要遍历两次原数组,时间复杂度增大2倍)。

实现1:

/*因为需要保持原数组中奇数和奇数,偶数和偶数的相对位置不变,因此需要建立2个新数组temp1,temp2,
	 * 分别将奇数和偶数存入到temp1和temp2中,同时记录奇偶数组的长度。然后再将这两个数组存入原数组中*/
	public void reOrderArray(int [] array) {
		int [] temp1=new int[array.length];
		int [] temp2=new int[array.length];
		int j=0;
		int k=0;
		for (int i = 0; i < array.length; i++) {
			if((array[i]&1)==1){
				temp1[j]=array[i];//如果是奇数,存入temp1
				j++;
			}else{
				temp2[k]=array[i];
				k++;
			}			
		}
		System.arraycopy(temp1, 0, array, 0, j);
		System.arraycopy(temp2, 0, array, j, k);
//		System.out.println(Arrays.toString(array)); 
	    }

实现2:

/*建立一个新数组temp,遍历第一遍原数组,将所有的奇数存入temp中,再遍历第二遍原数组,将所有的偶数存入到temp中。
	 * 最后返回新数组temp*/
	public void reOrderArray2(int [] array) {
		int len=array.length;
		int [] temp=new int[len];
		int j=0;
		//遍历第一遍,将所有的奇数存入temp
		for (int i = 0; i < len; i++) {
			if((array[i]&1)==1){
				temp[j++]=array[i];
			}
			
		}
		//遍历第二遍,将所有的偶数存入temp
		for (int i = 0; i < len; i++) {
			if((array[i]&1)==0){
				temp[j++]=array[i];
			}
			
		}
//		array=temp;//直接这样赋值,在自己电脑上运行时没错的,但是粘贴到牛客上报错。
//		//原因是:在Java里面,可以用复制语句”A=B”给基本类型的数据传递值,但是如果A,B是两个同类型的数组,
//		//复制就相当于将一个数组变量的引用传递给另一个数组;如果一个数组发生改变,那么引用同一数组的变量也要发生改变.
		
		//因此采样arraycopy的方式
		System.arraycopy(temp,0,array,0,len);   
		System.out.println(Arrays.toString(array));
		
	}

 

如果要求不能开辟新空间,且时间复杂度不能太高,则需要使用到两个指针来操作。

(1)begin从左向右遍历,找到第一个偶数; 
(2)from从begin+1开始向后找,直到找到第一个奇数; 
(3)将[begin,…,from-1]的元素整体后移一位; 
(4)将找到的奇数放入begin位置,然后begin++。

实现3:

//如果既要求要求奇数和奇数,偶数和偶数之间保持相对位置不变,还不能开辟新空间。
	public void reOrderArray4(int[] array){
		if(array.length==0){
			return;
		}
		int begin=0,from;
		while(begin<array.length){
			while(begin<array.length&&(array[begin]&1)==1){
				begin++;//begin是奇数,则进行自增,直到找到偶数
			}
			from=begin+1;
			while(from<array.length&&(array[from]&1)==0){
				from++;//如果from指向的是偶数,则进行自增,直到找到奇数
			}
			//此时begin指向偶数,from指向奇数,将from指向的元素换到begin的位置,其他后移一位。
			if(from<array.length){
				int temp=array[from];
				for (int i = from-1; i>=begin; i--) {
					array[i+1]=array[i];
				}
				array[begin++]=temp;
			}else{
				break;
			}
		}
		System.out.println(Arrays.toString(array));
	}

上面是要求保持奇数和奇数,偶数和偶数之间相对位置不变。如果没有这个要求,只需要把所有的奇数排在偶数的前面。则不用开辟新的数组,可以在原数组上直接操作。

初步解法:  

从数组头部开始遍历,遇到偶数就将其后面的所有数字向前挪动一位,然后将该偶数插入到数组末尾,然后继续遍历,同理。当遍历完毕后所有的偶数就在后半部分,奇数就在前半部分了。这种解法的时间复杂度为O(n^2),复杂度太高。

探索:

维护两个指针,分别指向数组的首尾,然后一个向后一个向前,在两个指针相遇之前,如果第一个指针指向偶数,而第二个指针指向奇数,那么就交换这两个数。(无需开辟新空间)

实现4:

/*如果本题没有要求保持奇数与奇数,偶数和偶数之间的相对位置不变,则可以通过使用两个指针来实现
	 * 指针p1,p2分别指向数组array的首尾,然后p1向后遍历,p2向前遍历,在p1,p2相遇之前,
	 * 如果p1指向偶数,p2指向奇数,则互换p1,p2指向的元素,使得奇数在偶数前面(本操作在原数组上进行操作,不需要开辟新空间)*/
	public void reOrderArray3(int [] array){
		int left=0;
		int right=array.length-1;
		while(left<right){
			while((array[left]&1)==1){    //如果left指向的是奇数,则left进行自增,直到left指向偶数
				left++;
			}
			while((array[right]&1)==0){//如果right指向的是偶数,则right进行自减,直到right指向奇数
				right--;
			}
			if(left<right){
				int temp=array[left];
				array[left]=array[right];
				array[right]=temp;
			}
			
		}
		System.out.println(Arrays.toString(array));
	}

参考:

https://www.cnblogs.com/AndyJee/p/4643404.html

https://blog.csdn.net/luoshasha1216/article/details/78972281

https://blog.csdn.net/lks1139230294/article/details/52761479

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值