数据结构 --- 数组
来源:互联网 发布:ps大小尺寸怎么调整mac 编辑:程序博客网 时间:2024/04/29 22:14
1. 求数组中第二大的数
1. //定义两个变量2. const int MINNUMBER = -32767 ; 3. int find_sec_max( int data[], int count) 4. { 5. int maxnumber = data[0] ; 6. int sec_max = MINNUMBER ; 7. for ( int i =1 ; i < count ; i++) 8. { 9. if ( data[i] > maxnumber ) 10. { 11. sec_max = maxnumber ; 12. maxnumber = data[i] ; 13. } 14. else 15. { 16. if ( data[i] > sec_max ) 17. sec_max = data[i] ; 18. } 19. } 20. return sec_max ; 21. }
2. 求数组的子数组之和的最大值 — 动态规划
设sum[i]为以第i个元素结尾且和最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即sum[i] = max(sum[i-1] + a[i], a[i])。可以通过判断sum[i-1] + a[i]是否大于a[i]来做选择,而这实际上等价于判断sum[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小。
1. int MaxSubSum1(int *A,int n) 2. { 3. int start, all; 4. all = start = A[n-1]; 5. for(int i = n-2; i >= 0; i--) 6. { 7. start = Max(A[i], start + A[i]); //包含当前元素的最大值8. all = Max(all, start); 9. } 10. return all; 11. } 12. int MaxSubSum2(int *A, int n)13. {14. int sum = 0, cur = 0;15. int max = -(1 << 31);16. while(cur < n)17. {18. sum += A[cur++];19. if(max < sum)20. max = sum;21. else if(sum < 0) //A中有正数和负数22. sum = 0;23. }24. return max;25. }26. 27. int MaxSubSum3(int *A, int n)28. {29. int max = A[0];30. int sum = 0;31. for(int i = 0; i < n; i++)32. {33. if(sum >= 0)34. sum += A[i];35. else36. sum = A[i];37. if(sum > max)38. max = sum;39. }40. return max;41. }
3. 数对之差的最大值 – 动态规划
在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,数对之差的最大值是11,是16减去5的结果。
思路:这又是一道动态规划的题目,时间复杂度为O(n)。假设f[i]表示数组中前i+1个数的解,前i+1个数的最大值为m[i]。则有下列式子。
f[i] = max(f[i-1], m[i-1] - a[i]), m[i] = max(m[i-1],a[i])。问题的解为f[n-1]。
1. int MaxDiff_Solution2(int *pArray, int nLen) 2. { 3. if(pArray == NULL || nLen <= 1) 4. return 0; 5. int maxDiff = 0; 6. int maxElem = pArray[0]; 7. for(int i = 1; i < nLen; i++) 8. { 9. maxDiff = max(maxDiff, maxElem - pArray[i]); 10. maxElem = max(maxElem, pArray[i]); 11. } 12. return maxDiff; 13. }
4. 数组中只出现1次的两个数字
设题目中这两个只出现1次的数字分别为A和B,如果能将A,B分开到二个数组中,那显然符合“异或”解法的关键点了。因此这个题目的关键点就是将A,B分开到二个数组中。由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的。根据这一位是0还是1可以将A,B分开到A组和B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行“异或”解法就可以得到A,B了。而要判断A,B在哪一位上不相同,只要根据A异或B的结果就可以知道了,这个结果在二进制上为1的位都说明A,B在这一位上是不相同的。
比如int a[] = {1, 1, 3, 5, 2, 2}
整个数组异或的结果为3^5即 0x0011 ^ 0x0101 = 0x0110
对0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。
a[0] =1 0x0001 第一组
a[1] =1 0x0001 第一组
a[2] =3 0x0011 第二组
a[3] =5 0x0101 第一组
a[4] =2 0x0010 第二组
a[5] =2 0x0010 第二组
第一组有{1, 1, 5},第二组有{3, 2, 3},明显对这二组分别执行“异或”解法就可以得到5和3了。
1. void FindTwoNotRepeatNumberInArray(int *a, int n, int *pN1, int *pN2) 2. { 3. int i, j, temp; 4. 5. //计算这两个数的异或结果 6. temp = 0; 7. for (i = 0; i < n; i++) 8. temp ^= a[i]; 9. 10. // 找第一个为1的位 11. for (j = 0; j < sizeof(int) * 8; j++) 12. if (((temp >> j) & 1) == 1) 13. break; 14. 15. // 第j位为1,说明这两个数字在第j位上是不相同的 16. // 由此将整个数组分组即可 17. *pN1 = 0, *pN2 = 0; 18. for (i = 0; i < n; i++) 19. if (((a[i] >> j) & 1) == 0) 20. *pN1 ^= a[i]; 21. else 22. *pN2 ^= a[i]; 23. }
5. 数组中只出现1次的三个数字
假设x y z为只出现一次的数,其他出现偶数次。lowbit为某个数从右往左扫描第一次出现1的位置,则x^y、 x^z、 y^z 这三个值的lowbit有一个规律,其中肯定两个是一样的,另外一个是不一样的。令flips为上述三个值的异或,即flips=lowbit(a^b)^lowbit(a^c)^lowbit(b^c)。因此,可以利用此条件获得某个x(或者y,或者z),循环判断的条件是a[i]^xors的lowbit==flips(其中xors为所有数的异或值)
解释:a[i]^xors即可划分为两组,一组是lowbit与flips不同,一组是lowbit与flips相同。这样就能找到某个x,y,z,找出后,将其与数组最后一个值交换,在利用上题思路,在前面n-1个数中找出剩余两个。
1. // lowbit为某个数从右往左扫描第一次出现1的位置2. int lowbit(int x)3. {4. return x & ~(x - 1);5. }6. //三个数两两的异或后lowbit有两个相同,一个不同,可以分为两组7. void Find3(int seq[], int n, int& a, int& b, int& c)8. {9. int i, xors = 0;10. for(i = 0; i < n; i++)11. xors ^= seq[i];12. 13. int flips = 0;14. for(i = 0; i < n; i++) //因为出现偶数次的seq[i]和xors的异或,异或结果不改变15. flips ^= lowbit(xors ^ seq[i]); //表示的是:flips = lowbit(a^b) ^ lowbit(a^c) ^ lowbit(b^c)16. 17. //三个数两两异或后lowbit有两个相同,一个不同,可以分为两组18. //所以flips的值为:lowbit(a^b) 或 lowbit(a^c) 或 lowbit(b^c)19. 20. //得到三个数中的一个21. a = 0;22. for(i = 0; i < n; i++)23. {24. if(lowbit(seq[i] ^ xors) == flips) //找出三个数两两异或后的lowbit与另外两个lowbit不同的那个数25. a ^= seq[i];26. }27. 28. //找出后,与数组中最后一个值交换,利用Find2,找出剩余的两个29. for(i = 0; i < n; i++)30. {31. if(a == seq[i])32. {33. int temp = seq[i];34. seq[i] = seq[n - 1];35. seq[n - 1] = temp;36. }37. }38. 39. //利用Find2,找出剩余的两个40. Find2(seq, n - 1, b, c);41. }42. //假设数组中只有2(010)、3(011)、5(101)三个数,2与3异或后为001,2与5异或后为111,3与5异或后为110,43. //则flips的值为lowbit(001)^lowbit(111)^lowbit(110)= 2 ,当异或结果xors与第一个数2异或的时候,得到的就是3与5异或的结果110,其lowbit值等于flips,所以最先找出来的是三个数中的第一个数:244.
6. 数组中出现一次的数,其他数字出现3次
如果数组中没有x,那么数组中所有的数字都出现了3次,在二进制上,每位上1的个数肯定也能被3整除。而再对该数组添加任何一个数,如果这个数在二进制的某位上为1都将导致该位上1的个数不能被3整除。因此通过统计二进制上每位1的个数就可以推断出x在该位置上是0还是1了,这样就能计算出x了。推广一下,所有其他数字出现N(N>=2)次,而一个数字出现1次都可以用这种解法来推导出这个出现1次的数字。
1. int FindNumber(int a[], int n) 2. { 3. int bits[32]; //32=sizeof(int)*84. int i, j; 5. // 累加数组中所有数字的二进制位 6. memset(bits, 0, 32 * sizeof(int)); 7. for (i = 0; i < n; i++) 8. for (j = 0; j < 32; j++) 9. bits[j] += ((a[i] >> j) & 1); 10. // 如果某位上的结果不能被整除,则肯定目标数字在这一位上为1 11. int result = 0; 12. for (j = 0; j < 32; j++) 13. if (bits[j] % 3 != 0) 14. result += (1 << j); //1<<j即第j位为1 15. return result; 16. }
7. 把数组排成最小的数
输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。例如输入数组{32, 321},则输出这两个能排成的最小数字32132。请给出解决问题的算法,并证明该算法。
思路:先将整数数组转为字符串数组,然后字符串数组进行排序,最后依次输出字符串数组即可。这里注意的是字符串的比较函数需要重新定义,不是比较a和b,而是比较ab与 ba。如果ab < ba,则a < b;如果ab > ba,则a > b;如果ab = ba,则a = b。比较函数的定义是本解决方案的关键。
1. //重新定义比较函数对象 2. struct compare 3. { 4. bool operator() (const string &src1, const string &src2) 5. { 6. string s1 = src1 + src2; 7. string s2 = src2 + src1; 8. return s1 < s2; //升序排列,如果改为s1 > s2则为逆序排列 9. } 10. }; 11. //函数功能 : 把数组排成最小的数 12. //函数参数 : pArray为数组,num为数组元素个数 13. //返回值 : 无 14. void ComArrayMin(int *pArray, int num) 15. { 16. int i; 17. string *pStrArray = new string[num]; 18. 19. for(i = 0; i < num; i++) //将数字转换为字符串 20. { 21. stringstream stream; 22. stream<<pArray[i]; 23. stream>>pStrArray[i]; 24. } 25. 26. sort(pStrArray, pStrArray + num, compare()); //字符串数组排序 27. 28. for(i = 0; i < num; i++) //打印字符串数组 29. cout<<pStrArray[i]; 30. cout<<endl; 31. 32. delete [] pStrArray; 33. }
8. 将二个有序数列合并
1. //将有序数组a[]和b[]合并到c[]中 2. void MemeryArray(int a[], int n, int b[], int m, int c[]) 3. { 4. int i, j, k; 5. 6. i = j = k = 0; 7. while (i < n && j < m) 8. { 9. if (a[i] < b[j]) 10. c[k++] = a[i++]; 11. else 12. c[k++] = b[j++]; 13. } 14. 15. while (i < n) 16. c[k++] = a[i++]; 17. 18. while (j < m) 19. c[k++] = b[j++]; 20. }
9. 找数组中的特定元素
问题描述:一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。能否只用一个额外数组和少量其它空间实现。
思路:如果能用两个辅助数组,那么相对来说简单一点,可定义数组Min和数组Max,其中Min[i]表示自a[i]之后的最小值(包括a[i]),Max[i]表示自a[i]之前元素的最大值。有了这两个辅助数组后,对于a[i],如果它大于Max[i-1]并且小于Min[i+1],那么就符合要求。
但是题目要求是只用一个额外数组,其实Max数组可以省去,完全可以边判断边计算,这是因为Max[i]是自左往右计算的,而判断时也是自左往右,两个过程正好可以合起来。只需用一个变量Max保存一下当前的最大值即可。下面给出两种方法的代码实现。
1. //函数功能 : 找元素 2. //函数参数 : pArray指向数组,len为数组的元素个数 3. //返回值 : 无 4. void FindElements_Solution1(int *pArray, int len) 5. { 6. if(pArray == NULL || len <= 0 ) 7. return ; 8. 9. int *pMin = new int[len]; 10. int *pMax = new int[len]; 11. int i; 12. 13. pMax[0] = pArray[0]; 14. for(i = 1; i < len; i++) //计算自i往前最大值的辅助数组 15. pMax[i] = (pMax[i-1] >= pArray[i])? pMax[i-1]: pArray[i]; 16. pMin[len-1] = pArray[len-1]; //必须从后往前遍历17. for(i = len - 2; i >= 0; i--) //计算自i开始最小值的辅助数组 18. pMin[i] = (pMin[i+1] <= pArray[i])? pMin[i+1]: pArray[i]; 19. 20. if(pArray[0] <= pMin[0]) //检查第1个元素是否满足条件 21. cout<<pArray[0]<<' '; 22. for(i = 1; i < len - 1; i++) 23. { 24. if(pArray[i] >= pMax[i-1] && pArray[i] <=pMin[i+1]) //满足这个关系式的元素符合要求 25. cout<<pArray[i]<<' '; 26. } 27. if(pArray[len-1] >= pMax[len-1]) //检查第len个元素是否满足条件 28. cout<<pArray[i]; 29. cout<<endl; 30. 31. delete [] pMin; 32. delete [] pMax; 33. pMin = pMax = NULL; 34. } 1. void FindElements_Solution2(int *pArray, int len) 2. { 3. if(pArray == NULL || len <= 0 ) 4. return ; 5. 6. int *pMin = new int[len]; 7. int Max; 8. int i; 9. 10. Max = pArray[0]; 11. pMin[len-1] = pArray[len-1]; 12. for(i = len - 2; i >= 0; i--) //计算自i开始最小值的辅助数组 13. pMin[i] = (pMin[i+1] <= pArray[i])? pMin[i+1]: pArray[i]; 14. 15. if(pArray[0] <= pMin[0]) //检查第1个元素是否满足条件 16. cout<<pArray[0]<<' '; 17. 18. for(i = 1; i < len - 1; i++) 19. { 20. if(pArray[i] >= Max && pArray[i] <=pMin[i+1]) //满足这个关系式的元素符合要求 21. cout<<pArray[i]<<' '; 22. Max = (Max < pArray[i])? pArray[i]: Max; //更新当前最大值 23. } 24. if(pArray[len-1] >= Max) //检查第len个元素是否满足条件 25. cout<<pArray[i]; 26. cout<<endl; 27. 28. delete [] pMin; 29. pMin = NULL; 30. }
10. 从数列1,2…n中随意取几个数,使其和等于m
输入两个整数n和m,从数列1,2…….n中随意取几个数,使其和等于m,要求将其中所有的可能组合列出来。
思路:这个问题其实背包问题的变形,本文给出两种解法。
解法一:用递归,效率可能低了点。假设问题的解为F(n, m),可分解为两个子问题 F(n-1, m-n)和F(n-1, m)。对这两个问题递归求解,求解过程中,如果找到了符合条件的数字组合,则打印出来。
解法二:用循环,其实就是枚举所有组合。对于n ,组合数应该为2^n。我们可以用一个数字 i 来表示组合。如果i = 5,其二进制形式为101,相应的组合为{1, 3}。也就是说,二进制的每一位都代表一个数字,bit0代表数字1,bit1代表数字2,依次类推。当某位为1,表示选中了该位所表示的数字。
1. //函数功能 : 从数列1,2...n中随意取几个数,使其和等于m 2. //函数参数 : n为当前最大值,m为剩余值,flag标记选中与否,len为flag的容量 3. //返回值 : 无 4. void BagProblem_Solution1(int n, int m, int *flag, int len) 5. { 6. if(n < 1 || m < 1) 7. return; 8. 9. if(n < m) 10. { 11. flag[n-1] = 1; 12. BagProblem_Solution1(n-1, m-n, flag, len); //选了n 13. flag[n-1] = 0; 14. BagProblem_Solution1(n-1, m, flag, len); //不选n 15. } 16. else 17. { 18. flag[m-1] = 1; //n>=m,选中m即可 19. for(int i = 0; i < len; i++) 20. { 21. if(flag[i] == 1) 22. cout<<i+1<<' '; 23. } 24. cout<<endl; 25. flag[m-1] = 0; //不选m,继续递归。比如n = 10,m = 8,求出{1, 7}后,仍需继续,{1,3,4} {1,2,5}都是解 26. BagProblem_Solution1(m-1, m, flag, len); 27. } 28. } 1. //函数功能 : 从数列1,2...n中随意取几个数,使其和等于m 2. //函数参数 : n为当前最大值,m为剩余值 3. //返回值 : 无 4. void BagProblem_Solution2(int n, int m) 5. { 6. if(n < 1|| m < 1) 7. return; 8. if(n > m) 9. n = m; 10. 11. int num = 1<<n; //枚举次数 12. for(int i = 1; i < num; i++) //枚举所有情况 13. { 14. int sum = 0; 15. int j, k; 16. for(j = i, k = 1; j != 0; j>>=1, k++) //针对每种情况求和,判断是否满足条件 k记录bit位为1的位置17. { 18. if(j&1) 19. sum += k; 20. } 21. if(sum == m) //如果满足,打印结果 22. { 23. for(j = i, k = 1; j != 0; j>>=1, k++) 24. { 25. if(j&1) 26. cout<<k<<' '; 27. } 28. cout<<endl; 29. } 30. } 31. }
11. 旋转数组中的最小元素
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
思路:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(n)。但这个思路没有利用输入数组的特性。既然有时间复杂度更小的算法,我们容易想到二分查找,因为它的时间复杂度为O(logn)。这个问题是否可以运用二分查找呢?答案是肯定的。观察一下数组的特性,首先递增(称为递增a),然后突然下降到最小值,然后再递增(称为递增b)。当然还有一种特殊情况,就是数组递增,中间没有下降,即旋转元素个数为0。
对于一般的情况,假设A为输入数组,left 和 right 为数组左右边界的坐标,考察中间位置的值A[mid] ,如果A[mid] <= A[right],表明处于递增b,调整右边界 right = mid;如果A[mid] >= A[left],表明处于递增a,因此调整左边界left = mid。当左右边界相邻时,较小的一个就是数组的最小值。其实,对于一般情况,右边界所指的元素为最小值。
对于特殊情况,即旋转个数为0。按照上述算法,右边界会不断减少,直到与左边界相邻。这时左边界所指的元素为最小值。下面给出几组测试案例:
1. //{1,2,3,4,5,6,7,8,9,10} 1
2. //{4,5,6,7,8,9,10,1,2,3} 1
3. //{1,1,1,1,1,1,1,1,1,1} 1
4. //{1,9,10,1,1,1,1,1,1,1} 1
5. //{9,9,9,9,9,9,9,10,1,9} 9 错误
第五组的结果是错误的。其实,上述算法适用于严格递增的数组,对于非严格递增,用二分法无法保证正确解。有兴趣的读者,可以试试,对于非严格递增的序列,是否可以用二分法得到正确解。
1. //函数功能 : 旋转数组的最小元素 2. //函数参数 : pArray指向数组,len为数组长度 3. //返回值 : 最小元素 4. int FindMin(int *pArray, int len) 5. { 6. if(pArray == NULL || len <= 0) 7. return 0; 8. int left = 0, right = len - 1, mid; 9. while(right - left != 1) 10. { 11. mid = left + ((right - left)>>1); 12. if(pArray[right] >= pArray[mid]) 13. right = mid; 14. else if(pArray[left] <= pArray[mid]) 15. left = mid; 16. } 17. return pArray[right] > pArray[left] ? pArray[left]: pArray[right]; 18. }
12. 求最长单调递减子序列
问题描述:求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}。
思路:这是很经典的一个问题,用动态规划解决。假设源数组为A,定义一个辅助数组为B,B[i]表示以A[i]结尾的最长递减序列的长度。举个简单的例子,如果A[i]大于之前的所有元素,那么B[i] = 1。
有了这个辅助数组后,可以推出下面这个递推式子。B[i] = max{B[k] + 1, A[k]>A[i]&&0=
1. //函数功能 : 打印最长递减子序列 2. //函数参数 : pArray指向源数组,pB指向辅助数组,k表示最长子序列的末尾元素 3. //返回值 : 无 4. void Print(int *pArray, int *pB, int k) 5. { 6. for (int i = k - 1; i >= 0; i--) 7. { 8. if(pB[k] == pB[i] + 1 && pArray[i] > pArray[k]) //再现动态规划求解的过程,只不过是逆向 9. { 10. Print(pArray, pB, i); 11. break; 12. } 13. } 14. cout<<pArray[k]<<' '; 15. } 16. 17. //函数功能 : 一个数组的最长递减子序列 18. //函数参数 : pArray指向源数组,len表示数组长度 19. //返回值 : 无 20. void FindMDS(int *pArray, int len) 21. { 22. int i, j, maxi = 0; //maxi用来记录最长递减序列的末尾元素 23. int *pB = new int [len]; //辅助空间,pB[i]表示以pAray[i]结尾的最长递减序列长度 24. for(i = 0 ; i < len; i++) //初始化 25. pB[i] = 0; 26. 27. for(i = 0; i < len; i++) //计算以pAray[i]结尾的最长递减序列 28. { 29. pB[i] = 1; 30. for(j = 0; j < i; j++) 31. { 32. if(pArray[j] > pArray[i] && pB[j] + 1 > pB[i]) //这个判断式是关键 33. { 34. pB[i] = pB[j] + 1; 35. if(pB[i] > pB[maxi]) //更新当前找到的最长递减序列 36. maxi = i; 37. } 38. } 39. } 40. Print(pArray, pB, maxi); //打印目标序列 41. delete [] pB; 42. }
13. 查找最小的k个元素
题目是:输入n 个整数,输出其中最小的k 个。例如输入1,2,3,4,5,6,7 和8 这8 个数字,则最小的4 个数字为1,2,3 和4。
(1)看到题目的时候我第一反应,这题很简单,使用任何方式的排序将数列按顺序存储,之后遍历需要的k个元素即可,于是自己动手很容易就完成了。
(2)咱们想到了用选择或交换排序,即遍历n个数,先把最先遍历到的k个数存入大小为k的数组之中,对这k个数,利用选择或交换排序,找到k个数中的最大数kmax(kmax设为k个元素的数组中最大元素),用时O(k)(你应该知道,插入或选择排序查找操作需要O(k)的时间),后再继续遍历后n-k个数,x与kmax比较:如果x
1. public static void FindKMin(int[] sort, int k)2. {3. int[] heap = sort;4. int rootIndex = k / 2 - 1;5. while (rootIndex >= 0)6. {7. reheap(heap, rootIndex, k - 1);8. rootIndex--;9. }10. 11. for (int i = k, len=heap.Length; i < len; i++)12. {13. if (heap[i]<heap[0])14. {15. heap[0] = heap[i];16. reheap(heap, 0, k - 1);17. }18. }19. 20. Console.WriteLine("The {0} min element =",k);21. for (int i = 0; i < k; i++)22. {23. Console.Write(heap[i] + " ");24. }25. }26. 27. private static void reheap(int[] heap, int rootIndex, int lastInddex)28. {29. int orphan = heap[rootIndex];30. bool done = false;31. int leftIndex = rootIndex * 2 + 1;32. while (!done && leftIndex <= lastInddex)33. {34. int largerIndex = leftIndex;35. if (leftIndex+1 <= lastInddex)36. {37. int rightIndex = leftIndex + 1;38. if (heap[rightIndex] > heap[leftIndex])39. {40. largerIndex = rightIndex;41. }42. }43. 44. if (orphan < heap[largerIndex])45. {46. heap[rootIndex] = heap[largerIndex];47. rootIndex = largerIndex;48. leftIndex = rootIndex * 2 + 1;49. }50. else51. {52. done = true;53. }54. }55. 56. heap[rootIndex] = orphan;57. }
14. 顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
例如:如果输入如下矩阵:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
则依次打印出数字1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10。
解题思路(借鉴 迷宫问题):1)在矩阵周围 添加一堵墙 (-1);
2)分为四个方向运动,向右,向下,向左,向右。
3)在碰到墙 或者 已经走过的点, 则改变方向,方向的改变依赖上条的四个方向,依次循环找下一个方向。(而具体方向的执行,表现在 下标的改动)。
4)用printCount计数打印了的点数,也就是 走过的路径长度,而总路径长度是size*size的,如果大于它了,则不需要在往下执行
1. // ClockWisePrintMatrix_顺时针打印矩阵.cpp : Defines the entry point for the console application. 2. // 3. #include "stdafx.h" 4. #define MAX_SIZE 100 5. int Matrix[MAX_SIZE][MAX_SIZE]; 6. int size; 7. #define START_X 1 8. #define START_Y 0 9. int END_X,END_Y; 10. typedef enum Direction{ 11. RIGHT=0, 12. DOWN=1, 13. LEFT=2, 14. UP=3, 15. }; 16. //wall is -1, if has walked also change value to -1 17. void BuildOutsideWall() 18. { 19. for(int i=0;i<size+2;i++) 20. { 21. Matrix[0][i]=-1; 22. Matrix[size+1][i]=-1; 23. } 24. for(int i=1;i<size+2;i++) 25. { 26. Matrix[i][0]=-1; 27. Matrix[i][size+1]=-1; 28. } 29. 30. } 31. Direction ChangeDirection(int dir) 32. { 33. switch (dir) 34. { 35. case RIGHT: 36. return DOWN; 37. case DOWN: 38. return LEFT; 39. case LEFT: 40. return UP; 41. case UP: 42. return RIGHT; 43. } 44. } 45. void PrintClockWiseMatrix() 46. { 47. int i=START_X,j=START_Y; 48. Direction dir=RIGHT; 49. int printCount=0; 50. while(printCount<=size*size) 51. { 52. switch(dir) 53. { 54. case RIGHT: 55. if(Matrix[i][j+1]!=-1) 56. { 57. j++; 58. printf("%d ",Matrix[i][j]); 59. Matrix[i][j]=-1; //已输出的元素置为-160. printCount++; //控制循环的次数n*n61. } 62. else 63. { 64. dir=ChangeDirection(dir); 65. } 66. break; 67. case DOWN: 68. if(Matrix[i+1][j]!=-1) 69. { 70. i++; 71. printf("%d ",Matrix[i][j]); 72. Matrix[i][j]=-1; 73. printCount++; 74. } 75. else 76. { 77. dir=ChangeDirection(dir); 78. } 79. 80. break; 81. case LEFT: 82. if(Matrix[i][j-1]!=-1) 83. { 84. j--; 85. printf("%d ",Matrix[i][j]); 86. Matrix[i][j]=-1; 87. printCount++; 88. } 89. else 90. { 91. dir=ChangeDirection(dir); 92. } 93. break; 94. case UP: 95. if(Matrix[i-1][j]!=-1) 96. { 97. i--; 98. printf("%d ",Matrix[i][j]); 99. Matrix[i][j]=-1; 100. printCount++; 101. } 102. else 103. { 104. dir=ChangeDirection(dir); 105. } 106. break; 107. } 108. } 109. 110. } 111. int _tmain(int argc, _TCHAR* argv[]) 112. { 113. scanf("%d",&size); 114. END_X=END_Y=size; 115. BuildOutsideWall(); 116. for(int i=1;i<=size;i++) 117. for(int j=1;j<=size;j++) 118. { 119. scanf("%d",&Matrix[i][j]); 120. } 121. PrintClockWiseMatrix(); 122. return 0; 123. }
15. 寻找和为给定值的两个数
输入一个数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数
字。要求时间复杂度是 O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组 1、2、4、7、11、15 和数字 15。由于 4+11=15,因此输出 4 和 11。
不论原序列是有序还是无序,解决这类题有以下三种办法:
1)、二分(若无序,先排序后二分),时间复杂度总为 O(n*logn),空间复杂度为 O(1);
对每个a[i],查找sum-a[i]是否也在原始序列中。
2)、扫描一遍,X-S[i]映射到一个数组或构造hash表(大小由和决定,在没有负数的情况下),时间复杂度为O(n),空间复杂度为O(n);
3)、两个指针两端扫描(若无序,先排序后扫描),时间复杂度最后为:有序O(n),无序O(n*logn+n)=O(n*logn),空间复杂度都为O(1)。然后用两个指针 i,j,各自指向数组的首尾两端,令i=0,j=n-1,然后i++,j–,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a[j]>sum,则要想办法让sum的值减小,所以此刻i不动,j–,如果某一刻a[i]+a[j]
1. bool findNum(int data[], int len, int sum, int &firstNum, int &secondNum)2. {3. if(len < 1)4. return false;5. 6. int begin = 0;7. int end = len – 1;8. 9. while(begin < end)10. {11. int curSum = data[begin] + data[end];12. if(curSum == sum)13. {14. firstNum = data[begin];15. secondNum = data[end];16. return true;17. }18. else if(curSum > sum)19. end--;20. else21. begin++;22. }23. return false;24. }
16. 进制之间的转换
(1)十进制二进制
整数部分:除2取余,商继续除2,直到商为0,从最后一个余数读起直到第一个余数
小数部分:乘2取整,剩下的小数部分继续乘2,直到小数部分为0;如果永远不能为0,就利用0舍1入。读数时从前面得到的整数读到后面的整数。
(2)二进制十进制
不分整数和小数部分,按权相加法,即将二进制每位上的数乘以权,然后相加之和即是结果。
(3)二进制八进制
取三合一法,即从二进制的小数点为分界点,向左(向右)每三位取成一位,接着将这三位二进制按权相加,得到的数就是一位八位二进制数,然后,按顺序进行排列,小数点的位置不变。大家在做添0和去0的时候要注意,是在小数点最左边或者小数点的最右边(即整数的最高位和小数的最低位)才能添0或者去0,否则将产生错误 。1101.1 –> 15.4
(1) 八进制二进制
取一分三法,即将一位八进制数分解成三位二进制,用三位二进制按权相加去凑这位八进制数,小数点位置照旧。67.54转换为二进制数为110111.101100,即110111.1011
(5)二进制十六进制
取四合一法,即从二进制的小数点为分界点,向左(向右)每四位取成一位,接着将这四位二进制按权相加,得到的数就是一位十六位二进制数,然后,按顺序进行排列,小数点的位置不变,得到的数字就是我们所求的十六进制数。如果向左(向右)取四位后,取到最高(最低)位时候,如果无法凑足四位,可以在小数点最左边(最右边),即整数的最高位(最低位)添0,凑足四位。 将二进制11101001.1011转换为十六进制
(6)十进制转换为八进制
1)间接法:先将十进制转换成二进制,然后将二进制又转换成八进制
2)直接法:前面我们讲过,八进制是由二进制衍生而来的,因此我们可以采用与十进制转换为二进制相类似的方法,还是整数部分的转换和小数部分的转换,下面来具体讲解一下:
①整数部分
方法:除8取余法,即每次将整数部分除以8,余数为该位权上的数,而商继续除以8,余数又为上一个位权上的数,这个步骤一直持续下去,直到商为0为止,最后读数时候,从最后一个余数起,一直到最前面的一个余数。
②小数部分
方法:乘8取整法,即将小数部分乘以8,然后取整数部分,剩下的小数部分继续乘以8,然后取整数部分,剩下的小数部分又乘以8,一直取到小数部分为零为止。如果永远不能为零,就同十进制数的四舍五入一样,暂取个名字叫3舍4入。
例:将十进制数796.703125转换为八进制数
解:先将这个数字分为整数部分796和小数部分0.703125
- 数据结构---数组
- 数据结构数组
- 数据结构---->数组
- 数据结构-------数组
- 数据结构-数组
- 数据结构--数组
- 数据结构 - 数组
- 数据结构--数组
- 数据结构 --- 数组
- 数据结构--数组
- 数据结构--数组
- 数据结构-数组
- 数据结构-数组
- 数据结构-数组
- 数据结构-数组
- 数据结构-数组
- 数据结构-->数组
- 数据结构-数组
- java I/O 学习笔记
- 顺序表——链接表示
- 坑爹的 GB18030
- 关于iTunes中的链接(https://itunes.apple.com/cn/app/xxx/idxxxxxxxxxx?mt=8)中 mt=8的含义
- 9、Git远程协作的主要命令
- 数据结构 --- 数组
- linux下的中文乱码
- Android 双进程Service常驻后台,无惧“一键清理”
- Android之broadcast
- http1.1和http1.0的区别
- 二分搜索与一般搜索
- 蓝桥杯----剪格子
- JavaScript的bom对象
- C/C++传地址的小陷阱