题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。(相对位置:两数之间位置,比如:{1,2,3,4,5},偶数间,2的相邻偶数为4,那么调整数组后,2的相邻偶数也必须为4)。
思路:
用两个索引,一个指向数组的第一个数字,把第二个索引指向数组的最后一个数字,然后第一个索引往后移动,直到碰到第一个偶数,第二个索引往前移动,指导碰到第一个奇数,如果第一个索引小于第二个索引,那么就交换两个索引的值,然后重复上述操作,直到第一个索引大于第二个索引,结束操作,此时,数组中所有的奇数都将位于偶数前面。
例如:有一数组{1,2,3,4,5,6,7,8},现第一个索引指向数组中的第一个数1,第二个索引指向数组的最后一个数8,第一个索引开始往后移动,找到第一个偶数,此时,索引指向的值为2。第二个索引开始移动,直到遇到第一个奇数,此时第二个索引指向的值为7,因为此时第一个索引所指值的下标为1,小于第二个索引所指值得下标6,所以交换2和7得位置,此时数组内容为:{1,7,3,1,5,6,2,8}.
按照上述操作,重复直到第一个索引小于第二个索引,结束循环,数组得调整完成。
以下有三种解决该操作的方法。其中第一种和第二种的时间复杂度与空间复杂度皆小于第三种方法,但是由于前两种方法无法解决数字间的相对位置不变,由此需要使用第三种方法来解决相对位置不变的问题。相对于最基本的思想:“从头遍历数组,每遇到一个偶数,就把后面的数往前移,最后空出的位置插入遇到的偶数。”第三种方法有点空间换时间的概念,直接用两个数组来储存奇偶数,然后遍历完数组之后再把偶数数组的值插入奇数数组中,时间复杂度O(N)。
using System;
namespace 调整数组顺序使奇数位于偶数前面
{
class Program
{
static void Main(string[] args)
{
//Test1
Console.Write("Test1");
int[] arr1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Console.Write("\nAgo:");
PrintArray(arr1);
Console.Write("\nAfter:");
PrintArray(ReOrderArray3(arr1));
//Test2
Console.WriteLine("\nTest2:");
int[] arr2 = { };
PrintArray(ReOrderArray1(arr2));
//Tes3
Console.WriteLine("\nTest3:");
Console.WriteLine("Ago:");
PrintArray(arr1);
Console.WriteLine("After:");
int[] arr3 = { 1, 2, 3, 4, 12, 6, 7, 15, 9, 10 };
PrintArray(ReOrderArray2(arr3));
}
/// <summary>
/// 用于重新排列奇偶数在数组中的顺序,但是该函数无法控制偶数与偶数、奇数与奇数间的相对位置
/// </summary>
/// <param name="array">调整的数组</param>
/// <returns></returns>
public static int[] ReOrderArray1(int[] array)
{
if (array == null || array.Length < 1)
return array;
int pre = 0, last = array.Length - 1;
while (pre < last)
{
//从前往后查找,找到最近的一个偶数
while (pre < last && (array[pre] & 0X1) != 0)
pre++;
//从后往前查找,找到最近的一个奇数
while (last > pre && (array[last] & 0X1) == 0)
last--;
if (pre < last)
{
int temp = array[pre];
array[pre] = array[last];
array[last] = temp;
}
}
return array;
}
//声明一个指向函数的委托,用于在函数ReOrderArray2中调用委托作为形参使用
private delegate bool Event(int n);
/// <summary>
/// 用于潘昕排列奇偶数在数组中的位置,依然无法控制偶数与偶数、奇数与奇数之间的相对位置。但是相对于ReOrder Array1来说,ReOrder Array2减少了数据间的耦合性,可以把IsEvent作为一个标准,如果排列的条件做了改变,只需要重新编写一个标准(重写IsEvent)即可实现新的功能模块,提高了代码的重用性,为功能扩展提供了便利
/// </summary>
/// <param name="array">用于调整的数组</param>
/// <returns></returns>
public static int[] ReOrderArray2(int[] array)
{
int[] arr= ReOrder(array, IsEvent);
Console.WriteLine("\n按奇偶数分组排列:");
PrintArray(arr);
arr = ReOrder(array, IsEvent2);
Console.WriteLine("\n按能否整除3的数分组排列:");
return arr;
}
/// <summary>
/// 此函数是把ReOrderArray1分解成两部分的第一部分,用于判断数字应该在数组前半部分还是后半部分
/// </summary>
/// <param name="array">调整的数组</param>
/// <param name="func">分组的标准,可以重写标准来改变函数的分组</param>
/// <returns></returns>
private static int[] ReOrder(int[] array,Event isEvent)
{
if (array == null || array.Length < 1)
return null;
int pre = 0, last = array.Length - 1;
while (pre<last)
{
//找到最近的一个偶数
while (pre < last && !isEvent(array[pre]))
pre++;
//找到最近的一个奇数
while (pre < last && isEvent(array[last]))
last--;
if (pre < last)
{
int temp = array[pre];
array[pre] = array[last];
array[last] = temp;
}
}
return array;
}
/// <summary>
/// 分组的标准,用于判断传入的数是否为一个偶数,如果是则返回真,否则返回假
/// </summary>
/// <param name="n">传入的需要判断的数</param>
/// <returns></returns>
private static bool IsEvent(int n)
{
return (n & 0x1) == 0;
}
/// <summary>
/// 用于测试的一个新标准,把能整除3的值列为一个新的分组放在数组的后面
/// </summary>
/// <param name="n">传入需要判断的数</param>
/// <returns></returns>
private static bool IsEvent2(int n)
{
return (n % 3) == 0;
}
public static int[] ReOrderArray3(int[] array)
{
int[] arr = new int[array.Length];
int[] ouShu = new int[array.Length];
int ji = 0,ou=0; //奇数的个数,偶数的个数
for(int i = 0; i < array.Length; i++)
{
if ((array[i] & 0X1) != 0)
{
arr[ji++] = array[i];
}
else
ouShu[ou++] = array[i];
}
for(int i = 0; i < ou; i++)
{
arr[ji++] = ouShu[i];
}
return arr;
}
private static void PrintArray(int[] array)
{
if (array == null || array.Length < 1)
return;
for (int i = 0; i < array.Length; i++)
{
if (i % 5 == 0)
Console.WriteLine();
Console.Write(array[i] + "\t");
}
}
}
}