飞道的博客

剑指offer----C语言版----第一天

319人阅读  评论(0)

目录

1. 数组中重复的数字Ⅰ

 1.1 题目描述

1.2 思路一

1.3 思路二

 1.4 思路三(最优解)


1. 数组中重复的数字Ⅰ

原题:剑指 Offer 03. 数组中重复的数字 - 力扣(LeetCode)https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/

 1.1 题目描述

给你一个数组,大小为N,数组里面的值的介于 [ 0, N-1 ],要求你找出数组中任意一个重复的数字。

1.2 思路一

 我们选用一个时间复杂度为O(N*logN)的排序算法对数组进行排序,例如快速排序,堆排序,归并排序。然后再去遍历排序好的数组,找出重复的数字。解题的时间复杂度:O(N*logN),空间复杂度:O(1)。

我这里自己写了快速排序的代码,如果大家嫌麻烦可以使用C语言的库函数qsort来对数组进行排序,底层逻辑也是快速排序,但是在数据量比较小的时候用的是插入排序,库函数嘛,效率的优化是最高的。

cplusplus.com - The C++ Resources Networkhttps://legacy.cplusplus.com/大家可以使用这个来查看C语言的标准库函数。


  
  1. //数字交换
  2. void Swap(int* pa, int* pb)
  3. {
  4. int temp =*pa;
  5. *pa = *pb;
  6. *pb = temp;
  7. }
  8. //快速排序优化算法:三数取中间
  9. int GetMidIndex(int* arr, int left, int right)
  10. {
  11. int mid = (left + right) >> 1;
  12. if (arr[mid] > arr[left])
  13. {
  14. if (arr[left] > arr[right])
  15. {
  16. return left;
  17. }
  18. else if (arr[mid] > arr[right])
  19. {
  20. return right;
  21. }
  22. else
  23. {
  24. return mid;
  25. }
  26. }
  27. // arr[mid] < arr[left]
  28. else
  29. {
  30. if (arr[right] > arr[left])
  31. {
  32. return left;
  33. }
  34. else if (arr[mid] > arr[right])
  35. {
  36. return mid;
  37. }
  38. else
  39. {
  40. return right;
  41. }
  42. }
  43. }
  44. void QuickSort(int* arr, int left,int right)
  45. {
  46. if (left >= right)
  47. {
  48. return;
  49. }
  50. int index = GetMidIndex(arr, left, right);
  51. int begin = left;
  52. int end = right;
  53. Swap(&arr[index], &arr[begin]);
  54. int pivot = begin;
  55. int key = arr[begin];
  56. while (begin < end)
  57. {
  58. //找小的
  59. while (begin < end && arr[end] >= key)
  60. {
  61. --end;
  62. }
  63. arr[pivot] = arr[end];
  64. pivot = end;
  65. //找大的
  66. while (begin < end && arr[begin] <= key)
  67. {
  68. ++begin;
  69. }
  70. arr[pivot] = arr[begin];
  71. pivot = begin;
  72. }
  73. pivot = begin;
  74. arr[pivot] = key;
  75. QuickSort(arr, left, pivot - 1);
  76. QuickSort(arr, pivot + 1, right);
  77. }
  78. int findRepeatNumber(int* nums, int numsSize){
  79. assert(nums);
  80. int left = 0;
  81. int right = numsSize - 1;
  82. QuickSort(nums, left, right);
  83. int i = 0;
  84. for(i = 0; i < numsSize - 1; i++)
  85. {
  86. if(nums[i] == nums[i + 1])
  87. {
  88. return nums[i];
  89. }
  90. }
  91. return -1;
  92. }

1.3 思路二

我们可以利用一个简单的哈希表:开辟一个同样大小的数组arr,并将数组arr初始化为0,然后遍历原来的数组nums,将nums数组的值对应到arr相应下标的位置,让他的值加一,如果再遍历过程中遇到arr的值大于1,则找到了该重复的数字。该解题方法的时间复杂度:O(N),空间复杂度O(N),以空间换时间。


  
  1. int findRepeatNumber(int* nums, int numsSize){
  2. int* arr = ( int*) calloc(numsSize, sizeof( int));
  3. int i = 0;
  4. for(i = 0; i < numsSize; i++)
  5. {
  6. arr[nums[i]] += 1;
  7. if(arr[nums[i]] > 1)
  8. {
  9. free(arr);
  10. arr = NULL;
  11. return nums[i];
  12. }
  13. }
  14. free(arr);
  15. arr = NULL;
  16. return -1;
  17. }

 1.4 思路三(最优解)

我们分析题目可以得到如下规律:数组大小是N,数值范围是N-1,如果说该数组没有重复的元素,那么数组中每一个下标都可以对应与其下标相同的数字,如果说有重复的数字,那么,会出现一个下标有多个与其下标相等的数字,或者出现没有与其下标相等的数字。

利用这一特性,我们可以遍历该数组,扫描到下标 i 时,首先比较这个数 m 与下标 i 是否相等,如果相等,继续扫描下一个元素。如果不相等,将m与下标为 m 的数进行比较,如果相等则找到一个重复的数字,如果不相等将 m 交换到下标为 m 的位置,对交换过来的数字继续重复上述判断即可,直到找到重复的数字。

解题的时间复杂度:O(N),空间复杂度O(1)。

下面以一组例子来演示方便理解:


  
  1. void Swap(int* pa, int* pb)
  2. {
  3. int temp = *pa;
  4. *pa = *pb;
  5. *pb = temp;
  6. }
  7. int findRepeatNumber(int* nums, int numsSize){
  8. int i= 0;
  9. for(i = 0; i < numsSize; i++)
  10. {
  11. while(nums[i] != i)
  12. {
  13. if(nums[i] == nums[nums[i]])
  14. {
  15. return nums[i];
  16. }
  17. Swap(&nums[i],&nums[nums[i]]);
  18. }
  19. }
  20. return -1;
  21. }

 


转载:https://blog.csdn.net/m0_73096566/article/details/128466408
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场