当前位置:   article > 正文

分治法经典问题——快速排序、归并排序、全排列、二分查找、求第k大元素_排序算法 分类 分治

排序算法 分类 分治

一、分治法概述

1.设计思想

2.分治法所能解决的问题一般具有以下几个特征

3.求解步骤

二、经典算法

1.快速排序

2.归并排序

3. 全排列

4.二分查找

5.求第k大元素


一、分治法概述


1.设计思想

将规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

2.分治法所能解决的问题一般具有以下几个特征:

  • 该问题的规模缩小到一定的程度就可以容易地解决。
  • 该问题可以分解为若干个规模较小的相同问题。
  • 利用该问题分解出的子问题的解可以合并为该问题的解。
  • 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

3.求解步骤

  • 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题。
  • 求解子问题:若子问题规模较小而容易被解决则直接求解,否则递归地求解各个子问题。
  • 合并:将各个子问题的解合并为原问题的解。
     

二、经典算法

代码注释里有更详细的解释哦

1.快速排序

每次选取第一个数字为pivot,左边的数字均比它大,右边的数字均比它小。

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. /*
  5. 分治法快速排序
  6. */
  7. void quick_sort(int arr[], int left, int right)
  8. {
  9. if (left >= right)
  10. return;
  11. int i = left;
  12. int j = right;
  13. int pivot = arr[left];
  14. while (i < j)
  15. {
  16. //从right一直向mid遍历,如果大于pivot就继续,直到找到第一个小于pivot的元素
  17. while (arr[j] >= pivot && i < j)
  18. j--;
  19. //从left一直向mid遍历,如果小于pivot就继续,直到找到第一个小于pivot的元素
  20. while (arr[i] <= pivot && i < j)
  21. i++;
  22. //交换,把小的换到pivot左边,大的换到pivot右边
  23. if (i < j)
  24. swap(arr[i], arr[j]);
  25. }
  26. /*
  27. pivot回归它正确的位置(左边小,右边大)。
  28. 执行到这句的时侯i和j已经相交了
  29. 此时交换pivot和arr[i](或arr[j] 这俩是一个数)
  30. */
  31. swap(arr[left], arr[i]);
  32. //分治法,左右两边再依次进行上述快排
  33. quick_sort(arr, left, i - 1);
  34. quick_sort(arr, i + 1, right);
  35. }
  36. int main()
  37. {
  38. int a[8] = {2, 0, 2, 1, 1, 1, 7, 2};
  39. quick_sort(a, 0, 7);
  40. for (int i = 0; i < 8; i++)
  41. cout << a[i] << ' ';
  42. return 0;
  43. }

运行结果


2.归并排序

先二分成单个元素,再相邻元素合并

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. //归并函数
  5. void merge_sort_arr(int arr[], int left, int mid, int right)
  6. {
  7. int temp[right - left + 1]; // temp作为临时数组 存放临时的值
  8. int i = left, j = mid + 1, k = 0;
  9. //分成(left,mid)(mid+1,right)两组,从头遍历哪组队头元素更小,选取它进入temp数组
  10. while (i <= mid && j <= right)
  11. {
  12. if (arr[i] < arr[j])
  13. temp[k++] = arr[i++];
  14. else
  15. temp[k++] = arr[j++];
  16. }
  17. //某组为空,则将后续加入temp数组
  18. while (i <= mid)
  19. temp[k++] = arr[i++];
  20. while (j <= right)
  21. temp[k++] = arr[j++];
  22. //修改arr数组
  23. for (int i = 0; i < k; i++)
  24. arr[left + i] = temp[i];
  25. }
  26. //二分函数
  27. void merge_sort(int arr[], int left, int right)
  28. {
  29. if (left == right)
  30. {
  31. return;
  32. }
  33. int mid = (left + right) / 2;
  34. merge_sort(arr, left, mid);
  35. merge_sort(arr, mid + 1, right);
  36. merge_sort_arr(arr, left, mid, right);
  37. for (int i = 0; i < 10; i++)
  38. {
  39. cout << arr[i] << ' ';
  40. }
  41. cout << endl;
  42. }
  43. int main()
  44. {
  45. int arr[10] = {3, 1, 2, 8, 7, 5, 9, 4, 0, 6};
  46. merge_sort(arr, 0, 9);
  47. }

运行结果


3. 全排列

将除了首位的其他元素与首位交换形成暂时首位,固定当前首位,分治剩下位数的全排列,一直分治到数组只剩下一个元素。

{1,2,3},全排列为(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. void perm(int arr[], int j, int k)
  5. {
  6. if (j == k) // 数组只有一个元素
  7. {
  8. for (int i = 0; i <= k; i++)
  9. cout << arr[i] << " ";
  10. cout << endl;
  11. }
  12. else
  13. {
  14. for (int i = j; i <= k; i++)
  15. {
  16. swap(arr[i], arr[j]);
  17. perm(arr, j + 1, k);
  18. /*
  19. 还原状态,否则再次交换时就不是交换的原来的顺序,
  20. 而是交换过一次的顺序,这样就乱套了
  21. */
  22. swap(arr[i], arr[j]);
  23. }
  24. }
  25. }
  26. int main()
  27. {
  28. int arr[] = {1, 2, 3};
  29. perm(arr, 0, 2);
  30. return 0;
  31. }

运行结果


4.二分查找

太经典啦就不过多赘述了

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. //默认已排好序
  5. int binary_search(int arr[],int low,int high,int key)
  6. {
  7. if(low > high)
  8. return -1;
  9. int mid = (low + high) / 2;
  10. if(arr[mid] == key)
  11. return mid;
  12. //mid 比目标数大,在左侧区间找
  13. else if(arr[mid] > key)
  14. binary_search(arr,low,mid - 1,key);
  15. //mid 比目标数小,在右侧区间找
  16. else if(arr[mid] < key)
  17. binary_search(arr,mid + 1,high,key);
  18. }
  19. int main()
  20. {
  21. int key = 99;
  22. int arr[10] = {1,23,96,45,21,36,45,99,12,10};
  23. int ans = binary_search(arr,0,9,key);
  24. if(ans > 0)
  25. cout << key << "在数组的下标是:" << ans << endl;
  26. else
  27. cout << "没有找到~" << endl;
  28. return 0;
  29. }

运行结果


5.求第k大元素

将数组分为两个区域,左边为比基数小的,右边为比基数小的。令 a[i] 等于目标数,第 k 小数在数组中的下标为[ k-1 ],所以:

当 k-1= i 时找到第k小数,返回 a[i]的值;

若 k-1 < i,在左区间递归查找;

若 k-1 > i ,在右区间递归查找。

当区间中只有一个元素时,直接返回 a[k-1] 。

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. int number_k(int arr[], int left, int right, int k)
  5. {
  6. int i = left, j = right;
  7. // 目标数
  8. int target;
  9. // 区间内至少有两个元素
  10. if (left < right)
  11. {
  12. target = arr[left];
  13. while (i != j)
  14. {
  15. // 右边比target小的数字,提前到arr[i]
  16. while (i < j && arr[j] >= target)
  17. j--;
  18. arr[i] = arr[j];
  19. // 左边比target大的数字,置后于arr[j]
  20. while (i < j && arr[i] <= target)
  21. i++;
  22. arr[j] = arr[i];
  23. }
  24. // 第k大的数字下标为k-1
  25. arr[i] = target;
  26. if (k - 1 == i)
  27. return arr[i];
  28. // 比target小,在左区间递归查找
  29. else if (k - 1 < i)
  30. return number_k(arr, left, i - 1, k);
  31. else
  32. return number_k(arr, i + 1, right, k);
  33. }
  34. }
  35. int main()
  36. {
  37. int n = 8;
  38. int a[] = {2, 3, 1, 4, 5, 7, 8, 6};
  39. // int k;
  40. // cout << "input k :" << endl;
  41. // cin >> k;
  42. int ans = number_k(a, 0, n - 1, 5);
  43. // cout << "NO." << k << ":" << ans << endl;
  44. cout << ans;
  45. return 0;
  46. }

运行结果

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/842827
推荐阅读
相关标签
  

闽ICP备14008679号