对“求数组中所有和为某固定数的所有数对”的算法的简单思考
2010-01-20 21:21
405 查看
一、题目描述
有一个数组a[1000],里面存放了1000个整数,请找出该数组中所有和为M的数对。例如数组为-1,2,4,6,5,3,4,2,9,0,8,3,那么和为8的数对有(-1,9),(2,6),(4,4),(5,3),(5,3),(0,8)。
二、最普通的算法
在不可率复杂度的情况下,对于这个问题的最简单的算法如下:
利用两层循环查找所有和为固定数sumTotal的所有数对,将找到的数对存入List中。但是这个算法复杂度有点高,实际上在遍历的时候做了一些重复的工作:
1) 后续循环的时候没有必要对前面已经配对的数列进行处理。
2)查找与某个数和为sumTotal的数时,应该可以有一些可以相互利用的过程。
基于这两点,可以引用一些01背包或动态规划的思想(或许引用得不恰当,我对这两种思想和理解很肤浅)来对这个算法进行改进,利用递归来操作。
二、采用递归算法
采用递归算法的代码如下:
每递归一次后,将所有没有检查过匹配的数字再组成一个新的数组以便在下一次递归中来处理,但这么做浪费了巨大的空间,特别是数组很大的情况下会消耗很多内存。为了不每次递归创建新的数组,可以在原来的数组上进行处理,将已经匹配的数字从数组中剔出,将后续数字前移替补。
三、数组动态改变算法
这种算法的代码如下:
这种算法会破坏原有数组的数据的。
四、三种算法时间的比较
虽然后两种算法的初衷是为了减少时间复杂度,但在具体测试中并没有比第一种算法优越多少,特别是递归的算法比第一种算法所用的时间还明显加长。或许我的想法从基础上就有问题,也或许是这个例子过于简单使得移动数据占用的时间明显凸现出来了。
一直未对算法有过多研究,希望高手们能指点一二,我相信有肯定有更完美的解决方案。
五、所有码
有一个数组a[1000],里面存放了1000个整数,请找出该数组中所有和为M的数对。例如数组为-1,2,4,6,5,3,4,2,9,0,8,3,那么和为8的数对有(-1,9),(2,6),(4,4),(5,3),(5,3),(0,8)。
二、最普通的算法
在不可率复杂度的情况下,对于这个问题的最简单的算法如下:
private static ListUseNormalWay(int[] arr, int sumTotal) { List result = new List (); for (int i = 0; i < arr.Length; i++) { int expectNum = sumTotal - arr[i]; for (int j = i + 1; j < arr.Length; j++) { if (arr[j] == expectNum) { result.Add(new int[]{arr[i], expectNum}); } } } return result; }
利用两层循环查找所有和为固定数sumTotal的所有数对,将找到的数对存入List中。但是这个算法复杂度有点高,实际上在遍历的时候做了一些重复的工作:
1) 后续循环的时候没有必要对前面已经配对的数列进行处理。
2)查找与某个数和为sumTotal的数时,应该可以有一些可以相互利用的过程。
基于这两点,可以引用一些01背包或动态规划的思想(或许引用得不恰当,我对这两种思想和理解很肤浅)来对这个算法进行改进,利用递归来操作。
二、采用递归算法
采用递归算法的代码如下:
private static ListUseRecursion(int[] arr, int length, int sumTotal) { List result = new List (); int[] nextArr = new int[length]; int nextLength = 0; int expectNum = sumTotal - arr[0]; int findStartNumCount = 0; int findEndNumCount = 0; for (int i = 1; i < length; i++) { if (arr[i] == expectNum) { int circleCount = arr[0] == expectNum ? findEndNumCount : findStartNumCount; circleCount += 1; for (int j = 0; j < circleCount; j++) { result.Add(new int[] { arr[0], expectNum }); } findEndNumCount++; } else if (arr[i] == arr[0]) { for (int j = 0; j < findEndNumCount; j++) { result.Add(new int[] { expectNum, arr[0] }); } findStartNumCount++; } else { nextArr[nextLength++] = arr[i]; } } if (nextLength > 0) { List nextResult = UseRecursion(nextArr, nextLength, sumTotal); foreach (int[] item in nextResult) { result.Add(item); } } return result; }
每递归一次后,将所有没有检查过匹配的数字再组成一个新的数组以便在下一次递归中来处理,但这么做浪费了巨大的空间,特别是数组很大的情况下会消耗很多内存。为了不每次递归创建新的数组,可以在原来的数组上进行处理,将已经匹配的数字从数组中剔出,将后续数字前移替补。
三、数组动态改变算法
这种算法的代码如下:
private static ListUseAdvancedWay(int[] arr, int sumTotal) { List result = new List (); int startIndex = 0, endIndex = arr.Length - 1; while (startIndex < endIndex) { int expectNum = sumTotal - arr[startIndex]; int findStartNumCount = 0; int findEndNumCount = 0; for (int i = startIndex + 1; i <= endIndex; i++) { if (findStartNumCount > 0 || findEndNumCount > 0) { arr[i] = arr[i + findStartNumCount + findEndNumCount]; } if (arr[i] == expectNum) { int circleCount = arr[startIndex] == expectNum ? findEndNumCount : findStartNumCount; circleCount += 1; for (int iStart = 0; iStart < circleCount; iStart++) { result.Add(new int[] { arr[startIndex], arr[i] }); } findEndNumCount++; endIndex--; i--; } else if (arr[i] == arr[startIndex] && arr[startIndex] != expectNum) { for (int iEnd = 0; iEnd < findEndNumCount; iEnd++) { result.Add(new int[] { expectNum, arr[i] }); } findStartNumCount++; endIndex--; i--; } } startIndex++; } return result; }
这种算法会破坏原有数组的数据的。
四、三种算法时间的比较
虽然后两种算法的初衷是为了减少时间复杂度,但在具体测试中并没有比第一种算法优越多少,特别是递归的算法比第一种算法所用的时间还明显加长。或许我的想法从基础上就有问题,也或许是这个例子过于简单使得移动数据占用的时间明显凸现出来了。
一直未对算法有过多研究,希望高手们能指点一二,我相信有肯定有更完美的解决方案。
五、所有码
static void Main(string[] args) { const int NUM_COUNT = 8000; const int MIN_NUM = -4; const int MAX_NUM = 12; const int TOTAL = 8; int[] arr = GenerateArray(NUM_COUNT, MIN_NUM, MAX_NUM); //Console.WriteLine("The numbers generated are:/n-------------------------------------"); //PrintNumArray(arr); // Normal way TimeSpan normalTimeStart = Process.GetCurrentProcess().TotalProcessorTime; Stopwatch normalStw = new Stopwatch(); normalStw.Start(); ListresultUseNormalWay = UseNormalWay(arr, TOTAL); double normalCupTime = Process.GetCurrentProcess().TotalProcessorTime.Subtract(normalTimeStart).TotalMilliseconds; normalStw.Stop(); double normalRealTime = normalStw.Elapsed.TotalMilliseconds; // Print Normal Result //Console.WriteLine("The result number pairs using normal way are:/n----------------------------------"); //PrintNumPairs(resultUseNormalWay); // Recursion way TimeSpan recuTimeStart = Process.GetCurrentProcess().TotalProcessorTime; Stopwatch recuStw = new Stopwatch(); recuStw.Start(); List resultUseRecursionWay = UseRecursion(arr, arr.Length, TOTAL); double recuCupTime = Process.GetCurrentProcess().TotalProcessorTime.Subtract(recuTimeStart).TotalMilliseconds; recuStw.Stop(); double recuRealTime = recuStw.Elapsed.TotalMilliseconds; // Print Recursion Result //Console.WriteLine("The result number pairs using recusion way are:/n----------------------------------"); //PrintNumPairs(resultUseRecursionWay); // Advanced way TimeSpan advTimeStart = Process.GetCurrentProcess().TotalProcessorTime; Stopwatch advStw = new Stopwatch(); advStw.Start(); List resultUseAdvancedWay = UseAdvancedWay(arr, TOTAL); double advCupTime = Process.GetCurrentProcess().TotalProcessorTime.Subtract(advTimeStart).TotalMilliseconds; advStw.Stop(); double advRealTime = advStw.Elapsed.TotalMilliseconds; // Print Advanced Result //Console.WriteLine("The result number pairs using advanced way are:/n----------------------------------"); //PrintNumPairs(resultUseAdvancedWay); Console.WriteLine("/n================================/nThe time used:/n-----------------------------------"); Console.WriteLine(String.Format("Normal : count - {0} Cpu Time - {1} Real Time - {2}", resultUseNormalWay.Count, normalCupTime, normalRealTime)); Console.WriteLine(String.Format("Recursion: count - {0} Cpu Time - {1} Real Time - {2}", resultUseRecursionWay.Count, recuCupTime, recuRealTime)); Console.WriteLine(String.Format("Advanced : count - {0} Cpu Time - {1} Real Time - {2}", resultUseAdvancedWay.Count, advCupTime, advRealTime)); Console.Read(); } private static int[] GenerateArray(int numCount, int minValue, int maxValue) { int[] arr = new int[numCount]; Random rdm = new Random((int)DateTime.Now.Ticks); for (int i = 0; i < arr.Length; i++) { arr[i] = rdm.Next(minValue, maxValue); } return arr; } private static void PrintNumArray(int[] arr) { for (int i = 0; i < arr.Length; i++) { if (i > 0 && i % 20 == 0) { Console.Write("/n"); } Console.Write(String.Format("{0,2} ", arr[i])); } Console.Write("/n"); } private static void PrintNumPairs(List numList) { for (int i = 0; i < numList.Count; i++) { if (i > 0 && i % 10 == 0) { Console.Write("/n"); } Console.Write(string.Format("({0,2},{1,2}) ", numList[i][0], numList[i][1])); } Console.Write("/n"); } private static List UseNormalWay(int[] arr, int sumTotal) { List result = new List (); for (int i = 0; i < arr.Length; i++) { int expectNum = sumTotal - arr[i]; for (int j = i + 1; j < arr.Length; j++) { if (arr[j] == expectNum) { result.Add(new int[]{arr[i], expectNum}); } } } return result; } private static List UseRecursion(int[] arr, int length, int sumTotal) { List result = new List (); int[] nextArr = new int[length]; int nextLength = 0; int expectNum = sumTotal - arr[0]; int findStartNumCount = 0; int findEndNumCount = 0; for (int i = 1; i < length; i++) { if (arr[i] == expectNum) { int circleCount = arr[0] == expectNum ? findEndNumCount : findStartNumCount; circleCount += 1; for (int j = 0; j < circleCount; j++) { result.Add(new int[] { arr[0], expectNum }); } findEndNumCount++; } else if (arr[i] == arr[0]) { for (int j = 0; j < findEndNumCount; j++) { result.Add(new int[] { expectNum, arr[0] }); } findStartNumCount++; } else { nextArr[nextLength++] = arr[i]; } } if (nextLength > 0) { List nextResult = UseRecursion(nextArr, nextLength, sumTotal); foreach (int[] item in nextResult) { result.Add(item); } } return result; } private static List UseAdvancedWay(int[] arr, int sumTotal) { List result = new List (); int startIndex = 0, endIndex = arr.Length - 1; while (startIndex < endIndex) { int expectNum = sumTotal - arr[startIndex]; int findStartNumCount = 0; int findEndNumCount = 0; for (int i = startIndex + 1; i <= endIndex; i++) { if (findStartNumCount > 0 || findEndNumCount > 0) { arr[i] = arr[i + findStartNumCount + findEndNumCount]; } if (arr[i] == expectNum) { int circleCount = arr[startIndex] == expectNum ? findEndNumCount : findStartNumCount; circleCount += 1; for (int iStart = 0; iStart < circleCount; iStart++) { result.Add(new int[] { arr[startIndex], arr[i] }); } findEndNumCount++; endIndex--; i--; } else if (arr[i] == arr[startIndex] && arr[startIndex] != expectNum) { for (int iEnd = 0; iEnd < findEndNumCount; iEnd++) { result.Add(new int[] { expectNum, arr[i] }); } findStartNumCount++; endIndex--; i--; } } startIndex++; } return result; }
相关文章推荐
- 对“求数组中所有和为某固定数的所有数对”的算法的简单思考
- 算法导论 4.1-5 最大子数组问题 线性时间算法 C++简单实现
- 数组的简单算法
- C#中定义数组和一个简单排序算法实现。
- 【简单算法】30.将有序数组转换为二叉搜索树
- 一个简单的算法---实现找出数组中一个数字出现次数最多的数字
- 简单归并算法-有序数组合并算法实现
- C++中一些简单的数组算法 第一篇
- 也说一道面试题 求数组中所有和为某固定数的所有数对
- 数组数据去重简单算法
- 算法训练 数组排列去重(set简单用法)
- 【大家来一起讨论吧】百度面试题,假设一整型数组存在若干正数和负数,现在通过某种算法使得该数组的所有负数在正数的左边,且保证负数件和正数间元素相对位置不变。时空复杂度要求:o(n),o(1)
- 设计一个算法找到数组中两个元素相加等于指定数的所有组合
- 这个简单算法也许可以让人工智能真正像人一样思考
- 简单的算法题之合并数组
- 【算法题】找到数组中和为固定值的两个元素
- 面试常考算法题 局部最小 求二叉树结点 求两个数组中所有数的上中位数 两个数组的所有数中第K小的数
- 算法题: 求一个整数数组中,通过元素加减运算得到指定结果的所有运算过程. 例如【5,4,6,7,1】= 9 ?
- js简单算法——寻找文档中所有元素节点
- 【简单算法】31.合并两个有序数组