📑 题目:29. 两数相除

🚀 本题 LeetCode 传送门

题目大意

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。

说明:

  • 被除数和除数均为 32 位有符号整数。
  • 除数不为 0。
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1]。本题中,如果除法结果溢出,则返回 2^31 − 1。

解题思路

  • 给出除数和被除数,要求计算除法运算以后的商。注意值的取值范围在 [−2^31, 2^31 − 1] 之中。超过范围的都按边界计算。

  • 这一题可以用二分搜索来做。要求除法运算之后的商,把商作为要搜索的目标。商的取值范围是 [0, dividend],所以从 0 到被除数之间搜索。利用二分,找到(商 + 1 ) 除数 > 被除数并且 商 除数 ≤ 被除数 或者 (商+1) 除数 ≥ 被除数并且商 除数 < 被除数的时候,就算找到了商,其余情况继续二分即可。最后还要注意符号和题目规定的 Int32 取值范围。

  • 二分的写法常写错的 3 点:

    1. low ≤ high (注意二分循环退出的条件是小于等于)
    2. mid = low + (high-low)»1 (防止溢出)
    3. low = mid + 1 ; high = mid - 1 (注意更新 low 和 high 的值,如果更新不对就会死循环)

代码

  1. package leetcode
  2. import (
  3. ""math""
  4. )
  5. // 解法一 递归版的二分搜索
  6. func divide(dividend int, divisor int) int {
  7. sign, res := -1, 0
  8. // low, high := 0, abs(dividend)
  9. if dividend == 0 {
  10. return 0
  11. }
  12. if divisor == 1 {
  13. return dividend
  14. }
  15. if dividend == math.MinInt32 && divisor == -1 {
  16. return math.MaxInt32
  17. }
  18. if dividend > 0 && divisor > 0 || dividend < 0 && divisor < 0 {
  19. sign = 1
  20. }
  21. if dividend > math.MaxInt32 {
  22. dividend = math.MaxInt32
  23. }
  24. // 如果把递归改成非递归,可以改成下面这段代码
  25. // for low <= high {
  26. // quotient := low + (high-low)>>1
  27. // if ((quotient+1)*abs(divisor) > abs(dividend) && quotient*abs(divisor) <= abs(dividend)) || ((quotient+1)*abs(divisor) >= abs(dividend) && quotient*abs(divisor) < abs(dividend)) {
  28. // if (quotient+1)*abs(divisor) == abs(dividend) {
  29. // res = quotient + 1
  30. // break
  31. // }
  32. // res = quotient
  33. // break
  34. // }
  35. // if (quotient+1)*abs(divisor) > abs(dividend) && quotient*abs(divisor) > abs(dividend) {
  36. // high = quotient - 1
  37. // }
  38. // if (quotient+1)*abs(divisor) < abs(dividend) && quotient*abs(divisor) < abs(dividend) {
  39. // low = quotient + 1
  40. // }
  41. // }
  42. res = binarySearchQuotient(0, abs(dividend), abs(divisor), abs(dividend))
  43. if res > math.MaxInt32 {
  44. return sign * math.MaxInt32
  45. }
  46. if res < math.MinInt32 {
  47. return sign * math.MinInt32
  48. }
  49. return sign * res
  50. }
  51. func binarySearchQuotient(low, high, val, dividend int) int {
  52. quotient := low + (high-low)>>1
  53. if ((quotient+1)*val > dividend && quotient*val <= dividend) || ((quotient+1)*val >= dividend && quotient*val < dividend) {
  54. if (quotient+1)*val == dividend {
  55. return quotient + 1
  56. }
  57. return quotient
  58. }
  59. if (quotient+1)*val > dividend && quotient*val > dividend {
  60. return binarySearchQuotient(low, quotient-1, val, dividend)
  61. }
  62. if (quotient+1)*val < dividend && quotient*val < dividend {
  63. return binarySearchQuotient(quotient+1, high, val, dividend)
  64. }
  65. return 0
  66. }
  67. // 解法二 非递归版的二分搜索
  68. func divide1(divided int, divisor int) int {
  69. if divided == math.MinInt32 && divisor == -1 {
  70. return math.MaxInt32
  71. }
  72. result := 0
  73. sign := -1
  74. if divided > 0 && divisor > 0 || divided < 0 && divisor < 0 {
  75. sign = 1
  76. }
  77. dvd, dvs := abs(divided), abs(divisor)
  78. for dvd >= dvs {
  79. temp := dvs
  80. m := 1
  81. for temp<<1 <= dvd {
  82. temp <<= 1
  83. m <<= 1
  84. }
  85. dvd -= temp
  86. result += m
  87. }
  88. return sign * result
  89. }