经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
C基础 旋转数组查找题目
来源:cnblogs  作者:喜欢兰花山丘  时间:2018/11/8 9:51:06  对本文有异议

前言 - 引言

  1. 题目:
  2. 一类有序数组旋转查值问题.
  3. 例如:
      有序数组 [
    1, 2, 3, 5, 5, 7, 7, 8, 9 ] 旋转后为 [ 5, 7, 7, 8, 9, 1, 2, 3, 5 ]
  4. 如何从中找出一个值索引, not found return -1.

(同事面试时手写最简单一题, 回来和我说了一下, 就记下做个终结者系列)

这种旋转数组有个特点. 大家看图

相信大家豁然开朗了.  这里给个网上烂大街答案

  1. //
  2. // [1, 2, 3, 5, 5, 7, 7, 8, 9]
  3. // 升序数组翻转后
  4. // [5, 7, 7, 8, 9, 1, 2, 3, 5]
  5. // 查找 value, return index, not found return -1;
  6. //
  7. int
  8. search(int a[], int len, int v) {
  9. int begin, end, mid;
  10. // 异常判断
  11. if (a == NULL || len <= 0)
  12. return -1;
  13. begin = 0;
  14. end = len;
  15. while (begin < end) {
  16. mid = (begin + end) / 2;
  17. if (a[mid] == v)
  18. return mid;
  19. if (a[begin] < a[mid]) {
  20. // 左边有序 [begin, mid]
  21. if (a[begin] <= v && v < a[mid])
  22. end = mid;
  23. else
  24. begin = mid + 1;
  25. } else if (a[begin] > a[mid]) {
  26. // 右边有序 [mid, end)
  27. if (a[mid] < v && v <= a[end - 1])
  28. begin = mid + 1;
  29. else
  30. end = mid;
  31. } else {
  32. ++begin;
  33. }
  34. }
  35. // 没有找到
  36. return -1;
  37. }

这里使用 [begin, end) 二分法技巧. 代码支持升序旋转重复数组. 最坏情况(全重复)算法复杂度是 O(n).

不过有个问题, 如果不知道是升序 asc 还是降序 desc. 那就需要额外判断了. 

  1. // search_sort_state - 排序状态 -1 faild, 0 desc, 1 asc
  2. static int search_sort_state(int a[], int len) {
  3. int state, i, s[3];
  4. if (a == NULL || len <= 0)
  5. return -1;
  6. // 默认 desc 降序
  7. if (len == 1)
  8. return 0;
  9. // 1, 2 asc 升序, 但必须反转为 2, 1 变成降序. 因而当前降序 desc 原本就是升序 asc
  10. if (len == 2)
  11. return a[0] > a[1];
  12. // 摘取不重复的3个内容
  13. s[0] = a[0];
  14. // 开始找 s[1]
  15. for (i = 1; i < len; ++i) {
  16. if (a[i] == s[0])
  17. continue;
  18. break;
  19. }
  20. // 所有值都一样, 走默认降序
  21. if (i >= len)
  22. return 0;
  23. s[1] = a[i];
  24. // 开始找 s[2]
  25. while (i < len) {
  26. if (a[i] == s[0] || a[i] == s[1]) {
  27. ++i;
  28. continue;
  29. }
  30. break;
  31. }
  32. // 只有两个不一样的值, 走默认降序
  33. if (i >= len)
  34. return s[0] > s[1];
  35. s[2] = a[i];
  36. state = 0;
  37. state += s[0] > s[1] ? 1 : -1;
  38. state += s[1] > s[2] ? 1 : -1;
  39. state += s[2] > s[0] ? 1 : -1;
  40. return state >= 0 ? 0 : 1;
  41. }

最后是自己想得一个排序状态判别的算法(自我感觉巧妙). 试图找出不重复三个数. 例如

  6    7    5  有 

  6 < 7   <

  7 > 5   >

  5 < 5   <

原生组合是 5   6   7

因而 < 居多是升序. > 居多是降序. (核心原因是旋转数组大小关系只改变一次)

 

正文 - 扩展

  有了上面铺垫那我们开始码一个问题终结者代码. 希望有所感悟

  1. // bsearch_asc - 二分查找升序查找
  2. static int bsearch_asc(int a[], int begin, int end, int v) {
  3. // 简单判断
  4. if (begin >= end || v < a[begin] || v > a[end - 1])
  5. return -1;
  6. // 二分查找
  7. do {
  8. int mid = (begin + end) / 2;
  9. int val = a[mid];
  10. if (val == v)
  11. return mid;
  12. if (val < v)
  13. begin = mid + 1;
  14. else
  15. end = mid;
  16. } while (begin < end);
  17. return -1;
  18. }
  19. static int search_asc(int a[], int len, int v) {
  20. int begin = 0, end = len;
  21. // 异常判断
  22. if (begin >= end)
  23. return -1;
  24. while (begin < end) {
  25. int mid = (begin + end) / 2;
  26. if (a[mid] == v)
  27. return mid;
  28. if (a[begin] < a[mid]) {
  29. // 左边有序 [begin, mid]
  30. if (a[begin] <= v && v < a[mid])
  31. return bsearch_asc(a, begin, mid, v);
  32. // 右边无序, 继续循环
  33. begin = mid + 1;
  34. } else if (a[begin] > a[mid]) {
  35. // 右边有序 [mid, end)
  36. if (a[mid] < v && v <= a[end - 1])
  37. return bsearch_asc(a, mid + 1, end, v);
  38. // 左边无须, 继续循环
  39. end = mid;
  40. } else {
  41. ++begin;
  42. }
  43. }
  44. // 没有找到
  45. return -1;
  46. }
  47. // bsearch_desc - 二分查找降序查找
  48. static int bsearch_desc(int a[], int begin, int end, int v) {
  49. // 简单判断
  50. if (begin >= end || v > a[begin] || v < a[end - 1])
  51. return -1;
  52. // 二分查找
  53. do {
  54. int mid = (begin + end) / 2;
  55. int val = a[mid];
  56. if (val == v)
  57. return mid;
  58. if (val > v)
  59. begin = mid + 1;
  60. else
  61. end = mid;
  62. } while (begin < end);
  63. return -1;
  64. }
  65. static int search_desc(int a[], int len, int v) {
  66. int begin = 0, end = len;
  67. while (begin < end) {
  68. int mid = (begin + end) / 2;
  69. if (a[mid] == v)
  70. return mid;
  71. if (a[begin] > a[mid]) {
  72. // 左边有序 [begin, mid]
  73. if (a[begin] >= v && v > a[mid])
  74. return bsearch_desc(a, begin, mid, v);
  75. // 右边无序, 继续循环
  76. begin = mid + 1;
  77. } else if (a[begin] < a[mid]) {
  78. // 右边有序 [mid, end)
  79. if (a[mid] > v && v >= a[end - 1])
  80. return bsearch_desc(a, mid + 1, end, v);
  81. // 左边无须, 继续循环
  82. end = mid;
  83. } else {
  84. ++begin;
  85. }
  86. }
  87. // 没有找到
  88. return -1;
  89. }
  90. //
  91. // 题目:
  92. // 一类有序数组旋转查值问题.
  93. // 例如:
  94. // 有序数组 [ 1, 2, 3, 5, 5, 7, 7, 8, 9 ] 旋转后为 [ 5, 7, 7, 8, 9, 1, 2, 3, 5 ]
  95. // 如何从中找出一个值索引, not found return -1.
  96. int
  97. search_upgrade(int a[], int len, int v) {
  98. int state, i, s[3];
  99. if (a == NULL || len <= 0)
  100. return -1;
  101. // 默认 desc 降序
  102. if (len == 1) {
  103. if (a[0] == v)
  104. return 0;
  105. return -1;
  106. }
  107. if (len == 2) {
  108. if (a[0] == v)
  109. return 0;
  110. if (a[1] == v)
  111. return 1;
  112. return -1;
  113. }
  114. // 摘取不重复的3个内容
  115. s[0] = a[0];
  116. // 开始找 s[1]
  117. for (i = 1; i < len; ++i) {
  118. if (a[i] == s[0])
  119. continue;
  120. break;
  121. }
  122. // 所有值都一样, 走默认降序
  123. if (i >= len) {
  124. if (s[0] == v)
  125. return 0;
  126. return -1;
  127. }
  128. s[1] = a[state = i];
  129. // 开始找 s[2]
  130. while (i < len) {
  131. if (a[i] == s[0] || a[i] == s[1]) {
  132. ++i;
  133. continue;
  134. }
  135. break;
  136. }
  137. // 只有两个不一样的值, 走默认降序
  138. if (i >= len) {
  139. if (s[0] == v)
  140. return 0;
  141. if (s[1] == v)
  142. return state;
  143. return -1;
  144. }
  145. s[2] = a[i];
  146. state = 0;
  147. state += s[0] > s[1] ? 1 : -1;
  148. state += s[1] > s[2] ? 1 : -1;
  149. state += s[2] > s[0] ? 1 : -1;
  150. // desc 降序, 旋转
  151. if (state >= 0)
  152. return search_desc(a, len, v);
  153. // asc 升序, 旋转
  154. return search_asc(a, len, v);
  155. }

不同分支不同代码, 针对性强. 代码最坏的情况是 O(n).

 

这里不妨来个测试演示

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #define LEN(a) (sizeof(a)/sizeof(*a))
  5.  
  6. // print - 数据内容打印
  7. #define INT_BR (15)
  8. static void print(int a[], int len) {
  9. int i = 0;
  10. while (i < len) {
  11. printf(" %d", a[i]);
  12. if (!(++i % INT_BR))
  13. putchar('\n');
  14. }
  15. if (i % INT_BR)
  16. putchar('\n');
  17. }
  18. int search_upgrade(int a[], int len, int v);
  19. // sort - 旋转查找
  20. int main(int argc, char * argv[]) {
  21. int i, v;
  22. int a[] = { 5, 7, 7, 8, 9, 1, 2, 3, 5 };
  23. print(a, LEN(a));
  24. // 开始测试
  25. v = 8;
  26. i = search_upgrade(a, LEN(a), v);
  27. printf("%d -> %d\n", v, i);
  28. v = 5;
  29. i = search_upgrade(a, LEN(a), v);
  30. printf("%d -> %d\n", v, i);
  31. v = 6;
  32. i = search_upgrade(a, LEN(a), v);
  33. printf("%d -> %d\n", v, i);
  34. v = 7;
  35. i = search_upgrade(a, LEN(a), v);
  36. printf("%d -> %d\n", v, i);
  37. v = 4;
  38. i = search_upgrade(a, LEN(a), v);
  39. printf("%d -> %d\n", v, i);
  40. int b[] = { 7, 6, 5, 3, 3, 2, 1, 9, 9, 8, 8, 7, 7 };
  41. print(b, LEN(b));
  42. // 开始测试
  43. v = 8;
  44. i = search_upgrade(b, LEN(b), v);
  45. printf("%d -> %d\n", v, i);
  46. v = 5;
  47. i = search_upgrade(b, LEN(b), v);
  48. printf("%d -> %d\n", v, i);
  49. v = 6;
  50. i = search_upgrade(b, LEN(b), v);
  51. printf("%d -> %d\n", v, i);
  52. v = 7;
  53. i = search_upgrade(b, LEN(b), v);
  54. printf("%d -> %d\n", v, i);
  55. v = 4;
  56. i = search_upgrade(b, LEN(b), v);
  57. printf("%d -> %d\n", v, i);
  58. return EXIT_SUCCESS;
  59. }

当前输出结果如下

  1. $ gcc -g -Wall sort.c ; ./a.out
  2. 5 7 7 8 9 1 2 3 5
  3. 8 -> 3
  4. 5 -> 0
  5. 6 -> -1
  6. 7 -> 2
  7. 4 -> -1
  8. 7 6 5 3 3 2 1 9 9 8 8 7 7
  9. 8 -> 10
  10. 5 -> 2
  11. 6 -> 1
  12. 7 -> 0
  13. 4 -> -1

 

后记 - 感谢

        错误是难免的 ~ 欢迎指正 : )

        小桥 - https://music.163.com/#/song?id=493042772

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号